aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2020-05-27 15:12:04 +0200
committerÁlvaro Fernández Rojas <noltari@gmail.com>2020-05-28 10:36:27 +0200
commit584d4bf1d3c2265a810e1494eb5c8ef0a72ee934 (patch)
tree20b3364aee884d3f65f246c16ee7f8bd4a465ea2 /target/linux/bcm27xx/patches-5.4
parent9e467a764b4e30a04dd0431ea277f6acd26babe0 (diff)
downloadupstream-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.tar.gz
upstream-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.tar.bz2
upstream-584d4bf1d3c2265a810e1494eb5c8ef0a72ee934.zip
bcm27xx: update patches from RPi foundation
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0352-clk-bcm2835-Disable-v3d-clock.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0354-arm-dts-Correct-Pi-4B-LED-values.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch647
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0356-arm-dts-2711-Add-pcie0-alias.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0361-overlays-Make-mcp342x-run-time-compatible.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0365-overlays-dht11-Allow-multiple-instantiation.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0389-add-BME680-to-i2c-sensor-overlay.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0397-overlays-Use-preferred-compatible-strings.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0398-tty-amba-pl011-Add-un-throttle-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0399-Fix-i2c-pwm-pca9685a-overlay.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0410-overlays-Correct-the-eth_led-colour-assignments.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0427-ARM-dts-Correct-SoC-name.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0435-of-overlay-Correct-symbol-path-fixups.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0439-of-Factor-out-addr-size-cells-parsing.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0446-resource-Add-a-resource_list_first_type-helper.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-unify-the-dma_capable-definitions.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0456-PCI-brcmstb-Add-MSI-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0492-media-v4l2-common-add-pixel-encoding-support.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0504-media-uapi-hevc-Add-segment-address-field.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch)0
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch42
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch126
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch96
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch87
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch63
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch60
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch56
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch45
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch40
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch51
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch45
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch126
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch86
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch96
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch40
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch80
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch144
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch67
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch173
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch39
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch168
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch85
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch68
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch172
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch43
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch706
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch38
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch54
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch24
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch109
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch497
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch98
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch125
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch75
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch128
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch70
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch56
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch74
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch86
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch26
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch50
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch110
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch80
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch24
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch55
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch459
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch86
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch44
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch49
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch24
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch54
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch152
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch195
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch348
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch682
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch152
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch45
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch151
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch107
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch67
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch141
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch151
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch1310
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch66
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch128
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch104
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch134
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch133
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch46
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch33
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch47
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch165
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch47
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch26
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch163
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch1131
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch174
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch210
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch90
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch241
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch39
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch174
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch26
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch33
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch72
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch27
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch35
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch127
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch46
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch97
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch36
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch26
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch49
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch35
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch39
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch101
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch226
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch26
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch229
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch (renamed from target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch)6
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch323
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch24
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch42
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch44
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch105
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch95
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch49
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch36
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch871
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch33
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch52
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch29
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch41
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch42
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch217
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch206
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch2709
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch85
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch64
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch1084
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch1170
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch308
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch48
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch55
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch319
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch118
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch55
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch58
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch335
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch31
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch53
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch51
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch434
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch425
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch201
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch182
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch117
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch140
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch55
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch49
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch337
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch94
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch169
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch116
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch2255
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch39
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch93
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch181
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch206
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch29
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch131
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch143
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch205
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch58
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch111
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch119
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch27
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch82
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch38
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch333
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch801
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch40
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch62
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch148
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch29
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch23
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch92
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch20
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch21
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch38
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch42
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch43
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch151
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch101
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch22
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch89
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch44
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch55
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch326
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch84
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch130
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch156
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch2266
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch182
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch52
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch35
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch67
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch71
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch90
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch60
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch525
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch394
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch200
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch251
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch453
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch69
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch34
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch25
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch51
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch70
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch58
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch28
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch29
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch32
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch30
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch25
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch38
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch48
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch115
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch54
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch27
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch25
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch154
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch67
409 files changed, 37399 insertions, 673 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch
deleted file mode 100644
index 4529471b6c..0000000000
--- a/target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From a19956ff2941b73204c96127a22edef71b5d0d34 Mon Sep 17 00:00:00 2001
-From: popcornmix <popcornmix@gmail.com>
-Date: Mon, 9 Sep 2019 23:50:44 +0100
-Subject: [PATCH] v3d_drv: Allow clock retrieval by name
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.org>
----
- drivers/gpu/drm/v3d/v3d_drv.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
---- a/drivers/gpu/drm/v3d/v3d_drv.c
-+++ b/drivers/gpu/drm/v3d/v3d_drv.c
-@@ -285,7 +285,9 @@ static int v3d_platform_drm_probe(struct
- }
- }
-
-- v3d->clk = devm_clk_get(dev, NULL);
-+ v3d->clk = devm_clk_get(dev, "v3d");
-+ if (!v3d->clk)
-+ v3d->clk = devm_clk_get(dev, NULL);
- if (IS_ERR_OR_NULL(v3d->clk)) {
- if (PTR_ERR(v3d->clk) != -EPROBE_DEFER)
- dev_err(dev, "Failed to get clock (%ld)\n", PTR_ERR(v3d->clk));
diff --git a/target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch
index 1892a145fa..1892a145fa 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch b/target/linux/bcm27xx/patches-5.4/950-0352-clk-bcm2835-Disable-v3d-clock.patch
index 160b39b9cd..160b39b9cd 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0352-clk-bcm2835-Disable-v3d-clock.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch b/target/linux/bcm27xx/patches-5.4/950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch
index 192b13b69a..192b13b69a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch b/target/linux/bcm27xx/patches-5.4/950-0354-arm-dts-Correct-Pi-4B-LED-values.patch
index a3bae521e7..a3bae521e7 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0354-arm-dts-Correct-Pi-4B-LED-values.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch b/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch
deleted file mode 100644
index b2a9363ad4..0000000000
--- a/target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch
+++ /dev/null
@@ -1,647 +0,0 @@
-From e2262c8ab4755ab574580611d7da22509f07871c Mon Sep 17 00:00:00 2001
-From: popcornmix <popcornmix@gmail.com>
-Date: Wed, 21 Aug 2019 14:55:56 +0100
-Subject: [PATCH] clk-raspberrypi: Also support v3d clock
-
-Signed-off-by: popcornmix <popcornmix@gmail.com>
----
- drivers/clk/bcm/clk-raspberrypi.c | 501 ++++++++++++++++++++++++------
- 1 file changed, 412 insertions(+), 89 deletions(-)
-
---- a/drivers/clk/bcm/clk-raspberrypi.c
-+++ b/drivers/clk/bcm/clk-raspberrypi.c
-@@ -15,33 +15,103 @@
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
--
-+#include <dt-bindings/clock/bcm2835.h>
- #include <soc/bcm2835/raspberrypi-firmware.h>
-
- #define RPI_FIRMWARE_ARM_CLK_ID 0x00000003
-+#define RPI_FIRMWARE_V3D_CLK_ID 0x00000005
-
- #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
- #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1)
-
--/*
-- * Even though the firmware interface alters 'pllb' the frequencies are
-- * provided as per 'pllb_arm'. We need to scale before passing them trough.
-- */
--#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2
--
- #define A2W_PLL_FRAC_BITS 20
-
-+#define SOC_BCM2835 BIT(0)
-+#define SOC_BCM2711 BIT(1)
-+#define SOC_ALL (SOC_BCM2835 | SOC_BCM2711)
-+
- struct raspberrypi_clk {
- struct device *dev;
- struct rpi_firmware *firmware;
- struct platform_device *cpufreq;
-+};
-+
-+typedef int (*raspberrypi_clk_register)(struct raspberrypi_clk *rpi,
-+ const void *data);
-+
-+
-+/* assignment helper macros for different clock types */
-+#define _REGISTER(f, s, ...) { .clk_register = (raspberrypi_clk_register)f, \
-+ .supported = s, \
-+ .data = __VA_ARGS__ }
-+#define REGISTER_PLL(s, ...) _REGISTER(&raspberrypi_register_pll, \
-+ s, \
-+ &(struct raspberrypi_pll_data) \
-+ {__VA_ARGS__})
-+#define REGISTER_PLL_DIV(s, ...) _REGISTER(&raspberrypi_register_pll_divider, \
-+ s, \
-+ &(struct raspberrypi_pll_divider_data) \
-+ {__VA_ARGS__})
-+#define REGISTER_CLK(s, ...) _REGISTER(&raspberrypi_register_clock, \
-+ s, \
-+ &(struct raspberrypi_clock_data) \
-+ {__VA_ARGS__})
-+
-+
-+struct raspberrypi_pll_data {
-+ const char *name;
-+ const char *const *parents;
-+ int num_parents;
-+ u32 clock_id;
-+};
-+
-+struct raspberrypi_clock_data {
-+ const char *name;
-+ const char *const *parents;
-+ int num_parents;
-+ u32 flags;
-+ u32 clock_id;
-+};
-+
-+struct raspberrypi_pll_divider_data {
-+ const char *name;
-+ const char *divider_name;
-+ const char *lookup;
-+ const char *source_pll;
-+
-+ u32 fixed_divider;
-+ u32 flags;
-+ u32 clock_id;
-+};
-
-- unsigned long min_rate;
-- unsigned long max_rate;
-+struct raspberrypi_clk_desc {
-+ raspberrypi_clk_register clk_register;
-+ unsigned int supported;
-+ const void *data;
-+};
-
-- struct clk_hw pllb;
-- struct clk_hw *pllb_arm;
-- struct clk_lookup *pllb_arm_lookup;
-+struct raspberrypi_clock {
-+ struct clk_hw hw;
-+ struct raspberrypi_clk *rpi;
-+ u32 min_rate;
-+ u32 max_rate;
-+ const struct raspberrypi_clock_data *data;
-+};
-+
-+struct raspberrypi_pll {
-+ struct clk_hw hw;
-+ struct raspberrypi_clk *rpi;
-+ u32 min_rate;
-+ u32 max_rate;
-+ const struct raspberrypi_pll_data *data;
-+};
-+
-+struct raspberrypi_pll_divider {
-+ struct clk_divider div;
-+ struct raspberrypi_clk *rpi;
-+ u32 min_rate;
-+ u32 max_rate;
-+ const struct raspberrypi_pll_divider_data *data;
- };
-
- /*
-@@ -83,56 +153,49 @@ static int raspberrypi_clock_property(st
- return 0;
- }
-
--static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
-+static int raspberrypi_fw_is_on(struct raspberrypi_clk *rpi, u32 clock_id, const char *name)
- {
-- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
-- pllb);
- u32 val = 0;
- int ret;
-
- ret = raspberrypi_clock_property(rpi->firmware,
- RPI_FIRMWARE_GET_CLOCK_STATE,
-- RPI_FIRMWARE_ARM_CLK_ID, &val);
-+ clock_id, &val);
- if (ret)
- return 0;
-
- return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
- }
-
--
--static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
-- unsigned long parent_rate)
-+static unsigned long raspberrypi_fw_get_rate(struct raspberrypi_clk *rpi,
-+ u32 clock_id, const char *name, unsigned long parent_rate)
- {
-- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
-- pllb);
- u32 val = 0;
- int ret;
-
- ret = raspberrypi_clock_property(rpi->firmware,
- RPI_FIRMWARE_GET_CLOCK_RATE,
-- RPI_FIRMWARE_ARM_CLK_ID,
-+ clock_id,
- &val);
- if (ret)
-- return ret;
--
-- return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
-+ dev_err_ratelimited(rpi->dev, "Failed to get %s frequency: %d",
-+ name, ret);
-+ return val;
- }
-
--static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
-- unsigned long parent_rate)
-+static int raspberrypi_fw_set_rate(struct raspberrypi_clk *rpi,
-+ u32 clock_id, const char *name, u32 rate,
-+ unsigned long parent_rate)
- {
-- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
-- pllb);
-- u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
- int ret;
-
- ret = raspberrypi_clock_property(rpi->firmware,
- RPI_FIRMWARE_SET_CLOCK_RATE,
-- RPI_FIRMWARE_ARM_CLK_ID,
-- &new_rate);
-+ clock_id,
-+ &rate);
- if (ret)
- dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
-- clk_hw_get_name(hw), ret);
-+ name, ret);
-
- return ret;
- }
-@@ -141,16 +204,18 @@ static int raspberrypi_fw_pll_set_rate(s
- * Sadly there is no firmware rate rounding interface. We borrowed it from
- * clk-bcm2835.
- */
--static int raspberrypi_pll_determine_rate(struct clk_hw *hw,
-+static int raspberrypi_determine_rate(struct raspberrypi_clk *rpi,
-+ u32 clock_id, const char *name, unsigned long min_rate, unsigned long max_rate,
- struct clk_rate_request *req)
- {
-- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
-- pllb);
-+#if 1
-+ req->rate = clamp(req->rate, min_rate, max_rate);
-+#else
- u64 div, final_rate;
- u32 ndiv, fdiv;
-
- /* We can't use req->rate directly as it would overflow */
-- final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate);
-+ final_rate = clamp(req->rate, min_rate, max_rate);
-
- div = (u64)final_rate << A2W_PLL_FRAC_BITS;
- do_div(div, req->best_parent_rate);
-@@ -163,9 +228,129 @@ static int raspberrypi_pll_determine_rat
-
- req->rate = final_rate >> A2W_PLL_FRAC_BITS;
-
-+#endif
- return 0;
- }
-
-+static int raspberrypi_fw_clock_is_on(struct clk_hw *hw)
-+{
-+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_clock_data *data = pll->data;
-+
-+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name);
-+}
-+
-+static unsigned long raspberrypi_fw_clock_get_rate(struct clk_hw *hw,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_clock_data *data = pll->data;
-+
-+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate);
-+}
-+
-+static int raspberrypi_fw_clock_set_rate(struct clk_hw *hw, unsigned long rate,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_clock_data *data = pll->data;
-+
-+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate);
-+}
-+
-+static int raspberrypi_clock_determine_rate(struct clk_hw *hw,
-+ struct clk_rate_request *req)
-+{
-+ struct raspberrypi_clock *pll = container_of(hw, struct raspberrypi_clock, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_clock_data *data = pll->data;
-+
-+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req);
-+}
-+
-+static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
-+{
-+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_data *data = pll->data;
-+
-+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name);
-+}
-+
-+static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_data *data = pll->data;
-+
-+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate);
-+}
-+
-+static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_data *data = pll->data;
-+
-+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate);
-+}
-+
-+static int raspberrypi_pll_determine_rate(struct clk_hw *hw,
-+ struct clk_rate_request *req)
-+{
-+ struct raspberrypi_pll *pll = container_of(hw, struct raspberrypi_pll, hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_data *data = pll->data;
-+
-+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req);
-+}
-+
-+
-+static int raspberrypi_fw_pll_div_is_on(struct clk_hw *hw)
-+{
-+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_divider_data *data = pll->data;
-+
-+ return raspberrypi_fw_is_on(rpi, data->clock_id, data->name);
-+}
-+
-+static unsigned long raspberrypi_fw_pll_div_get_rate(struct clk_hw *hw,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_divider_data *data = pll->data;
-+
-+ return raspberrypi_fw_get_rate(rpi, data->clock_id, data->name, parent_rate);
-+}
-+
-+static int raspberrypi_fw_pll_div_set_rate(struct clk_hw *hw, unsigned long rate,
-+ unsigned long parent_rate)
-+{
-+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_divider_data *data = pll->data;
-+
-+ return raspberrypi_fw_set_rate(rpi, data->clock_id, data->name, rate, parent_rate);
-+}
-+
-+static int raspberrypi_pll_div_determine_rate(struct clk_hw *hw,
-+ struct clk_rate_request *req)
-+{
-+ struct raspberrypi_pll_divider *pll = container_of(hw, struct raspberrypi_pll_divider, div.hw);
-+ struct raspberrypi_clk *rpi = pll->rpi;
-+ const struct raspberrypi_pll_divider_data *data = pll->data;
-+
-+ return raspberrypi_determine_rate(rpi, data->clock_id, data->name, pll->min_rate, pll->max_rate, req);
-+}
-+
-+
- static const struct clk_ops raspberrypi_firmware_pll_clk_ops = {
- .is_prepared = raspberrypi_fw_pll_is_on,
- .recalc_rate = raspberrypi_fw_pll_get_rate,
-@@ -173,87 +358,225 @@ static const struct clk_ops raspberrypi_
- .determine_rate = raspberrypi_pll_determine_rate,
- };
-
--static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
-+static const struct clk_ops raspberrypi_firmware_pll_divider_clk_ops = {
-+ .is_prepared = raspberrypi_fw_pll_div_is_on,
-+ .recalc_rate = raspberrypi_fw_pll_div_get_rate,
-+ .set_rate = raspberrypi_fw_pll_div_set_rate,
-+ .determine_rate = raspberrypi_pll_div_determine_rate,
-+};
-+
-+static const struct clk_ops raspberrypi_firmware_clk_ops = {
-+ .is_prepared = raspberrypi_fw_clock_is_on,
-+ .recalc_rate = raspberrypi_fw_clock_get_rate,
-+ .set_rate = raspberrypi_fw_clock_set_rate,
-+ .determine_rate = raspberrypi_clock_determine_rate,
-+};
-+
-+
-+static int raspberrypi_get_clock_range(struct raspberrypi_clk *rpi, u32 clock_id, u32 *min_rate, u32 *max_rate)
- {
-- u32 min_rate = 0, max_rate = 0;
-+ int ret;
-+
-+ /* Get min & max rates set by the firmware */
-+ ret = raspberrypi_clock_property(rpi->firmware,
-+ RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
-+ clock_id,
-+ min_rate);
-+ if (ret) {
-+ dev_err(rpi->dev, "Failed to get clock %d min freq: %d (%d)\n",
-+ clock_id, *min_rate, ret);
-+ return ret;
-+ }
-+
-+ ret = raspberrypi_clock_property(rpi->firmware,
-+ RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
-+ clock_id,
-+ max_rate);
-+ if (ret) {
-+ dev_err(rpi->dev, "Failed to get clock %d max freq: %d (%d)\n",
-+ clock_id, *max_rate, ret);
-+ return ret;
-+ }
-+ return 0;
-+}
-+
-+
-+static int raspberrypi_register_pll(struct raspberrypi_clk *rpi,
-+ const struct raspberrypi_pll_data *data)
-+{
-+ struct raspberrypi_pll *pll;
- struct clk_init_data init;
- int ret;
-
- memset(&init, 0, sizeof(init));
-
- /* All of the PLLs derive from the external oscillator. */
-- init.parent_names = (const char *[]){ "osc" };
-- init.num_parents = 1;
-- init.name = "pllb";
-+ init.parent_names = data->parents;
-+ init.num_parents = data->num_parents;
-+ init.name = data->name;
- init.ops = &raspberrypi_firmware_pll_clk_ops;
- init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED;
-
-- /* Get min & max rates set by the firmware */
-- ret = raspberrypi_clock_property(rpi->firmware,
-- RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
-- RPI_FIRMWARE_ARM_CLK_ID,
-- &min_rate);
-+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
-+ if (!pll)
-+ return -ENOMEM;
-+ pll->rpi = rpi;
-+ pll->data = data;
-+ pll->hw.init = &init;
-+
-+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, &pll->min_rate, &pll->max_rate);
- if (ret) {
-- dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
-- init.name, ret);
-+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret);
- return ret;
- }
-
-- ret = raspberrypi_clock_property(rpi->firmware,
-- RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
-- RPI_FIRMWARE_ARM_CLK_ID,
-- &max_rate);
-+ ret = devm_clk_hw_register(rpi->dev, &pll->hw);
- if (ret) {
-- dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
-- init.name, ret);
-+ dev_err(rpi->dev, "%s: devm_clk_hw_register(%s) failed: %d\n", __func__, init.name, ret);
- return ret;
- }
-+ return 0;
-+}
-
-- if (!min_rate || !max_rate) {
-- dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n",
-- min_rate, max_rate);
-- return -EINVAL;
-- }
-+static int
-+raspberrypi_register_pll_divider(struct raspberrypi_clk *rpi,
-+ const struct raspberrypi_pll_divider_data *data)
-+{
-+ struct raspberrypi_pll_divider *divider;
-+ struct clk_init_data init;
-+ int ret;
-+
-+ memset(&init, 0, sizeof(init));
-+
-+ init.parent_names = &data->source_pll;
-+ init.num_parents = 1;
-+ init.name = data->name;
-+ init.ops = &raspberrypi_firmware_pll_divider_clk_ops;
-+ init.flags = data->flags | CLK_IGNORE_UNUSED;
-
-- dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n",
-- min_rate, max_rate);
-+ divider = devm_kzalloc(rpi->dev, sizeof(*divider), GFP_KERNEL);
-+ if (!divider)
-+ return -ENOMEM;
-+
-+ divider->div.hw.init = &init;
-+ divider->rpi = rpi;
-+ divider->data = data;
-+
-+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, &divider->min_rate, &divider->max_rate);
-+ if (ret) {
-+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret);
-+ return ret;
-+ }
-
-- rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
-- rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
-+ ret = devm_clk_hw_register(rpi->dev, &divider->div.hw);
-+ if (ret) {
-+ dev_err(rpi->dev, "%s: devm_clk_hw_register(%s) failed: %d\n", __func__, init.name, ret);
-+ return ret;
-+ }
-
-- rpi->pllb.init = &init;
-+ /*
-+ * PLLH's channels have a fixed divide by 10 afterwards, which
-+ * is what our consumers are actually using.
-+ */
-+ if (data->fixed_divider != 0) {
-+ struct clk_lookup *lookup;
-+ struct clk_hw *clk = clk_hw_register_fixed_factor(rpi->dev,
-+ data->divider_name,
-+ data->name,
-+ CLK_SET_RATE_PARENT,
-+ 1,
-+ data->fixed_divider);
-+ if (IS_ERR(clk)) {
-+ dev_err(rpi->dev, "%s: clk_hw_register_fixed_factor(%s) failed: %ld\n", __func__, init.name, PTR_ERR(clk));
-+ return PTR_ERR(clk);
-+ }
-+ if (data->lookup) {
-+ lookup = clkdev_hw_create(clk, NULL, data->lookup);
-+ if (IS_ERR(lookup)) {
-+ dev_err(rpi->dev, "%s: clk_hw_register_fixed_factor(%s) failed: %ld\n", __func__, init.name, PTR_ERR(lookup));
-+ return PTR_ERR(lookup);
-+ }
-+ }
-+ }
-
-- return devm_clk_hw_register(rpi->dev, &rpi->pllb);
-+ return 0;
- }
-
--static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
-+static int raspberrypi_register_clock(struct raspberrypi_clk *rpi,
-+ const struct raspberrypi_clock_data *data)
- {
-- rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev,
-- "pllb_arm", "pllb",
-- CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
-- 1, 2);
-- if (IS_ERR(rpi->pllb_arm)) {
-- dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
-- return PTR_ERR(rpi->pllb_arm);
-- }
-+ struct raspberrypi_clock *clock;
-+ struct clk_init_data init;
-+ struct clk *clk;
-+ int ret;
-+
-+ memset(&init, 0, sizeof(init));
-+ init.parent_names = data->parents;
-+ init.num_parents = data->num_parents;
-+ init.name = data->name;
-+ init.flags = data->flags | CLK_IGNORE_UNUSED;
-
-- rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0");
-- if (!rpi->pllb_arm_lookup) {
-- dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
-- clk_hw_unregister_fixed_factor(rpi->pllb_arm);
-+ init.ops = &raspberrypi_firmware_clk_ops;
-+
-+ clock = devm_kzalloc(rpi->dev, sizeof(*clock), GFP_KERNEL);
-+ if (!clock)
- return -ENOMEM;
-- }
-
-+ clock->rpi = rpi;
-+ clock->data = data;
-+ clock->hw.init = &init;
-+
-+ ret = raspberrypi_get_clock_range(rpi, data->clock_id, &clock->min_rate, &clock->max_rate);
-+ if (ret) {
-+ dev_err(rpi->dev, "%s: raspberrypi_get_clock_range(%s) failed: %d\n", __func__, init.name, ret);
-+ return ret;
-+ }
-+ clk = devm_clk_register(rpi->dev, &clock->hw);
-+ if (IS_ERR(clk)) {
-+ dev_err(rpi->dev, "%s: devm_clk_register(%s) failed: %ld\n", __func__, init.name, PTR_ERR(clk));
-+ return PTR_ERR(clk);
-+ }
-+ ret = clk_register_clkdev(clk, init.name, NULL);
-+ if (ret) {
-+ dev_err(rpi->dev, "%s: clk_register_clkdev(%s) failed: %d\n", __func__, init.name, ret);
-+ return ret;
-+ }
- return 0;
- }
-
-+
-+/*
-+ * the real definition of all the pll, pll_dividers and clocks
-+ * these make use of the above REGISTER_* macros
-+ */
-+static const struct raspberrypi_clk_desc clk_desc_array[] = {
-+ /* the PLL + PLL dividers */
-+ [BCM2835_CLOCK_V3D] = REGISTER_CLK(
-+ SOC_ALL,
-+ .name = "v3d",
-+ .parents = (const char *[]){ "osc" },
-+ .num_parents = 1,
-+ .clock_id = RPI_FIRMWARE_V3D_CLK_ID),
-+ [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV(
-+ SOC_ALL,
-+ .name = "pllb",
-+ .source_pll = "osc",
-+ .divider_name = "pllb_arm",
-+ .lookup = "cpu0",
-+ .fixed_divider = 1,
-+ .clock_id = RPI_FIRMWARE_ARM_CLK_ID,
-+ .flags = CLK_SET_RATE_PARENT),
-+};
-+
- static int raspberrypi_clk_probe(struct platform_device *pdev)
- {
- struct device_node *firmware_node;
- struct device *dev = &pdev->dev;
- struct rpi_firmware *firmware;
- struct raspberrypi_clk *rpi;
-- int ret;
-+ const struct raspberrypi_clk_desc *desc;
-+ const size_t asize = ARRAY_SIZE(clk_desc_array);
-+ int i;
-
- firmware_node = of_find_compatible_node(NULL, NULL,
- "raspberrypi,bcm2835-firmware");
-@@ -275,16 +598,16 @@ static int raspberrypi_clk_probe(struct
- rpi->firmware = firmware;
- platform_set_drvdata(pdev, rpi);
-
-- ret = raspberrypi_register_pllb(rpi);
-- if (ret) {
-- dev_err(dev, "Failed to initialize pllb, %d\n", ret);
-- return ret;
-+ for (i = 0; i < asize; i++) {
-+ desc = &clk_desc_array[i];
-+ if (desc->clk_register && desc->data /*&&
-+ (desc->supported & pdata->soc)*/) {
-+ int ret = desc->clk_register(rpi, desc->data);
-+ if (ret)
-+ return ret;
-+ }
- }
-
-- ret = raspberrypi_register_pllb_arm(rpi);
-- if (ret)
-- return ret;
--
- rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq",
- -1, NULL, 0);
-
diff --git a/target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch b/target/linux/bcm27xx/patches-5.4/950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch
index 67cdd44f29..67cdd44f29 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch b/target/linux/bcm27xx/patches-5.4/950-0356-arm-dts-2711-Add-pcie0-alias.patch
index fa00fb1974..fa00fb1974 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0356-arm-dts-2711-Add-pcie0-alias.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch
index 61f49d5ae4..61f49d5ae4 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch
index 0a6660b893..0a6660b893 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch b/target/linux/bcm27xx/patches-5.4/950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch
index eb35a81023..eb35a81023 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch b/target/linux/bcm27xx/patches-5.4/950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch
index 3bc9abde27..3bc9abde27 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch b/target/linux/bcm27xx/patches-5.4/950-0361-overlays-Make-mcp342x-run-time-compatible.patch
index 1b4809db0d..1b4809db0d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0361-overlays-Make-mcp342x-run-time-compatible.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch b/target/linux/bcm27xx/patches-5.4/950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch
index e1ffd3b0b7..e1ffd3b0b7 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch b/target/linux/bcm27xx/patches-5.4/950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch
index 263f3532f3..263f3532f3 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch b/target/linux/bcm27xx/patches-5.4/950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch
index d0d1e28757..d0d1e28757 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch b/target/linux/bcm27xx/patches-5.4/950-0365-overlays-dht11-Allow-multiple-instantiation.patch
index b75f1d4988..b75f1d4988 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0365-overlays-dht11-Allow-multiple-instantiation.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch b/target/linux/bcm27xx/patches-5.4/950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch
index 93a699a66a..93a699a66a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch b/target/linux/bcm27xx/patches-5.4/950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch
index 31a1a24caf..31a1a24caf 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch b/target/linux/bcm27xx/patches-5.4/950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch
index 3865ae11fb..3865ae11fb 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch b/target/linux/bcm27xx/patches-5.4/950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch
index 0221803993..0221803993 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch
index db90055343..db90055343 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch
index 3ececc8f92..3ececc8f92 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch
index 218a846fa3..218a846fa3 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch b/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch
index 1acf84b7ce..1acf84b7ce 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch b/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch
index ce0b8811d0..ce0b8811d0 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch
index fb3a6a3806..fb3a6a3806 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch b/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch
index ef26293498..ef26293498 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch b/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch
index 0e2a78649f..0e2a78649f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch b/target/linux/bcm27xx/patches-5.4/950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch
index 0e13394c7f..0e13394c7f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch
index ffd7d8e540..ffd7d8e540 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch b/target/linux/bcm27xx/patches-5.4/950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch
index 8d16152154..8d16152154 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch b/target/linux/bcm27xx/patches-5.4/950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch
index 7a47128837..7a47128837 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch b/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch
index 2feda7389e..2feda7389e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch b/target/linux/bcm27xx/patches-5.4/950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch
index 49d885cd73..49d885cd73 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch b/target/linux/bcm27xx/patches-5.4/950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch
index b61e2c5cfe..b61e2c5cfe 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch b/target/linux/bcm27xx/patches-5.4/950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch
index 52aa87ed04..52aa87ed04 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch b/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch
index 26d0c9894b..26d0c9894b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch b/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch
index 86b9400ac6..86b9400ac6 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch b/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch
index 67147fa58d..67147fa58d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0389-add-BME680-to-i2c-sensor-overlay.patch
index 0c2fe6aa8a..0c2fe6aa8a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0389-add-BME680-to-i2c-sensor-overlay.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch b/target/linux/bcm27xx/patches-5.4/950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch
index 4de95a5449..4de95a5449 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch b/target/linux/bcm27xx/patches-5.4/950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
index 0a7356ffa2..0a7356ffa2 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch b/target/linux/bcm27xx/patches-5.4/950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch
index e986f425ff..e986f425ff 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch
index cb8fa91bef..cb8fa91bef 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch b/target/linux/bcm27xx/patches-5.4/950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch
index 6b9a6bd29c..6b9a6bd29c 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch b/target/linux/bcm27xx/patches-5.4/950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch
index 0e0173085d..0e0173085d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch b/target/linux/bcm27xx/patches-5.4/950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch
index 54b366f65a..54b366f65a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch b/target/linux/bcm27xx/patches-5.4/950-0397-overlays-Use-preferred-compatible-strings.patch
index 6a246050cf..6a246050cf 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0397-overlays-Use-preferred-compatible-strings.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch b/target/linux/bcm27xx/patches-5.4/950-0398-tty-amba-pl011-Add-un-throttle-support.patch
index 7777e2df96..7777e2df96 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0398-tty-amba-pl011-Add-un-throttle-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0399-Fix-i2c-pwm-pca9685a-overlay.patch
index 17bdf3984e..17bdf3984e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0399-Fix-i2c-pwm-pca9685a-overlay.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch b/target/linux/bcm27xx/patches-5.4/950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch
index 397a2c3331..397a2c3331 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch b/target/linux/bcm27xx/patches-5.4/950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch
index bd8f405ef8..bd8f405ef8 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch b/target/linux/bcm27xx/patches-5.4/950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch
index a5c3d40512..a5c3d40512 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch b/target/linux/bcm27xx/patches-5.4/950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch
index df6f526e2e..df6f526e2e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch b/target/linux/bcm27xx/patches-5.4/950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch
index 4713dde247..4713dde247 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch b/target/linux/bcm27xx/patches-5.4/950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch
index a69feb50b4..a69feb50b4 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch b/target/linux/bcm27xx/patches-5.4/950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch
index 2f5d87b004..2f5d87b004 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch b/target/linux/bcm27xx/patches-5.4/950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch
index 10c5d23b46..10c5d23b46 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch b/target/linux/bcm27xx/patches-5.4/950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch
index b2b27f258f..b2b27f258f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch b/target/linux/bcm27xx/patches-5.4/950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch
index 06e979a462..06e979a462 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch b/target/linux/bcm27xx/patches-5.4/950-0410-overlays-Correct-the-eth_led-colour-assignments.patch
index f1f3290bad..f1f3290bad 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0410-overlays-Correct-the-eth_led-colour-assignments.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch b/target/linux/bcm27xx/patches-5.4/950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch
index fef644e1c9..fef644e1c9 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch b/target/linux/bcm27xx/patches-5.4/950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch
index 77bf63e2e2..77bf63e2e2 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch b/target/linux/bcm27xx/patches-5.4/950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch
index 23bc39b3d8..23bc39b3d8 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch
index 29ad87227d..29ad87227d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch
index bccf74b410..bccf74b410 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch
index e94f15196b..e94f15196b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch b/target/linux/bcm27xx/patches-5.4/950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch
index 403f534baf..403f534baf 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch b/target/linux/bcm27xx/patches-5.4/950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch
index a66202a2c9..a66202a2c9 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch b/target/linux/bcm27xx/patches-5.4/950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch
index 15e4f53a0a..15e4f53a0a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch b/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch
index 44f60d610f..44f60d610f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch b/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch
index e204859fbd..e204859fbd 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch b/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch
index ed0be9b9a3..ed0be9b9a3 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch b/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch
index 8d230d0edb..8d230d0edb 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch b/target/linux/bcm27xx/patches-5.4/950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch
index 4436c0a39d..4436c0a39d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch
index 5ba80afd98..5ba80afd98 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch
index f4e93308b1..f4e93308b1 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch b/target/linux/bcm27xx/patches-5.4/950-0427-ARM-dts-Correct-SoC-name.patch
index c18eb8af3c..c18eb8af3c 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0427-ARM-dts-Correct-SoC-name.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch b/target/linux/bcm27xx/patches-5.4/950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch
index 2c093459da..2c093459da 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch
index 7fb3443fa9..7fb3443fa9 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch b/target/linux/bcm27xx/patches-5.4/950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch
index 8271b2505a..8271b2505a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch b/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch
index b5e59e6464..b5e59e6464 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch b/target/linux/bcm27xx/patches-5.4/950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch
index ac50884bce..ac50884bce 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch b/target/linux/bcm27xx/patches-5.4/950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
index 4774fd2326..4774fd2326 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch b/target/linux/bcm27xx/patches-5.4/950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
index 213e8b8d9d..213e8b8d9d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch b/target/linux/bcm27xx/patches-5.4/950-0435-of-overlay-Correct-symbol-path-fixups.patch
index 4b005876de..4b005876de 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0435-of-overlay-Correct-symbol-path-fixups.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
index 636ad26a91..636ad26a91 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch
index 1056cfc60d..1056cfc60d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch b/target/linux/bcm27xx/patches-5.4/950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch
index dbfb1025cd..dbfb1025cd 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch b/target/linux/bcm27xx/patches-5.4/950-0439-of-Factor-out-addr-size-cells-parsing.patch
index 045ee9827f..045ee9827f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0439-of-Factor-out-addr-size-cells-parsing.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch b/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
index 28d1987a9c..28d1987a9c 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch
index 1cac2dfcd8..1cac2dfcd8 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch b/target/linux/bcm27xx/patches-5.4/950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
index a90e7f8587..a90e7f8587 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch b/target/linux/bcm27xx/patches-5.4/950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
index 3039bfe822..3039bfe822 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
index 2397c71af7..2397c71af7 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch b/target/linux/bcm27xx/patches-5.4/950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
index 23811e0b6e..23811e0b6e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0446-resource-Add-a-resource_list_first_type-helper.patch
index c2c959a3c1..c2c959a3c1 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0446-resource-Add-a-resource_list_first_type-helper.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
index c3ae61c993..c3ae61c993 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch b/target/linux/bcm27xx/patches-5.4/950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
index 51fd4be35e..51fd4be35e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
index 987351c1fc..987351c1fc 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch b/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-unify-the-dma_capable-definitions.patch
index d115f0eb80..d115f0eb80 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-unify-the-dma_capable-definitions.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch b/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
index a98f1d3852..a98f1d3852 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
index dca3fbfb57..dca3fbfb57 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch b/target/linux/bcm27xx/patches-5.4/950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
index 162a91c530..162a91c530 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch b/target/linux/bcm27xx/patches-5.4/950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch
index 9f114c1633..9f114c1633 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch b/target/linux/bcm27xx/patches-5.4/950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
index ca97a1966e..ca97a1966e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0456-PCI-brcmstb-Add-MSI-support.patch
index a27259bd19..a27259bd19 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0456-PCI-brcmstb-Add-MSI-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch b/target/linux/bcm27xx/patches-5.4/950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
index 6bb45ccb1f..6bb45ccb1f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch b/target/linux/bcm27xx/patches-5.4/950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
index 729d6e68ba..729d6e68ba 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch b/target/linux/bcm27xx/patches-5.4/950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
index 16eaeddb29..16eaeddb29 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
index 4ca345f2ad..4ca345f2ad 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch
index e9eed21451..e9eed21451 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch b/target/linux/bcm27xx/patches-5.4/950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
index 6f71797a8b..6f71797a8b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch b/target/linux/bcm27xx/patches-5.4/950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
index c7e10cbdc1..c7e10cbdc1 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch b/target/linux/bcm27xx/patches-5.4/950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
index 5846e96af8..5846e96af8 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch b/target/linux/bcm27xx/patches-5.4/950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
index 907bab5cfd..907bab5cfd 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch b/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch
index 9e40b5164a..9e40b5164a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch
index 58d23b7bcb..58d23b7bcb 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch b/target/linux/bcm27xx/patches-5.4/950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
index 0b48018c1e..0b48018c1e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch b/target/linux/bcm27xx/patches-5.4/950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
index ac4fe1698b..ac4fe1698b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
index f6264ff04a..f6264ff04a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch b/target/linux/bcm27xx/patches-5.4/950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
index cdd14f7940..cdd14f7940 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch b/target/linux/bcm27xx/patches-5.4/950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
index 8e66bccf6e..8e66bccf6e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch b/target/linux/bcm27xx/patches-5.4/950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
index f549e73fd3..f549e73fd3 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch b/target/linux/bcm27xx/patches-5.4/950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
index 9504bb3982..9504bb3982 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch b/target/linux/bcm27xx/patches-5.4/950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
index 9d8090441a..9d8090441a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch b/target/linux/bcm27xx/patches-5.4/950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
index 9b514c3a25..9b514c3a25 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch b/target/linux/bcm27xx/patches-5.4/950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
index 6abe7beb32..6abe7beb32 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch b/target/linux/bcm27xx/patches-5.4/950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
index 1d356eb6ab..1d356eb6ab 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch b/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
index 8d9a92ec0d..8d9a92ec0d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
index 1716ebd7be..1716ebd7be 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
index 6ed952bd45..6ed952bd45 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
index ac973e1d4e..ac973e1d4e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
index 746f35f83b..746f35f83b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
index cbda9ecc10..cbda9ecc10 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
index fb4a7f1cda..fb4a7f1cda 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
index 372cd0d665..372cd0d665 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch b/target/linux/bcm27xx/patches-5.4/950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch
index 830bc1125e..830bc1125e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch b/target/linux/bcm27xx/patches-5.4/950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch
index 7c50843102..7c50843102 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
index 72941b59bb..72941b59bb 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch b/target/linux/bcm27xx/patches-5.4/950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch
index 265533eb3a..265533eb3a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch b/target/linux/bcm27xx/patches-5.4/950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch
index 0c860c7e4a..0c860c7e4a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l2-common-add-pixel-encoding-support.patch
index aa127ab5e7..aa127ab5e7 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0492-media-v4l2-common-add-pixel-encoding-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch b/target/linux/bcm27xx/patches-5.4/950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch
index 0171cdf821..0171cdf821 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch b/target/linux/bcm27xx/patches-5.4/950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch
index b114aefacc..b114aefacc 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch
index bb66baf07d..bb66baf07d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch b/target/linux/bcm27xx/patches-5.4/950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch
index ef075fdb22..ef075fdb22 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch b/target/linux/bcm27xx/patches-5.4/950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch
index 0b74dbf123..0b74dbf123 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch b/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch
index 3c777922f1..3c777922f1 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch b/target/linux/bcm27xx/patches-5.4/950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch
index 1d478fc68a..1d478fc68a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch b/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch
index 0fe0f8cea4..0fe0f8cea4 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch b/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch
index 18073a8f9d..18073a8f9d 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch b/target/linux/bcm27xx/patches-5.4/950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch
index 7398807782..7398807782 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch b/target/linux/bcm27xx/patches-5.4/950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch
index c2cf27a40e..c2cf27a40e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch b/target/linux/bcm27xx/patches-5.4/950-0504-media-uapi-hevc-Add-segment-address-field.patch
index 91f195b4ae..91f195b4ae 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0504-media-uapi-hevc-Add-segment-address-field.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch b/target/linux/bcm27xx/patches-5.4/950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch
index 1353480476..1353480476 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch b/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch
index 234cb82b2a..234cb82b2a 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch b/target/linux/bcm27xx/patches-5.4/950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch
index 840541cd2e..840541cd2e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch b/target/linux/bcm27xx/patches-5.4/950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch
index a3023cae2b..a3023cae2b 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch b/target/linux/bcm27xx/patches-5.4/950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch
index 203e112466..203e112466 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch b/target/linux/bcm27xx/patches-5.4/950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch
index 134a685f0e..134a685f0e 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch b/target/linux/bcm27xx/patches-5.4/950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch
index ee92ada02f..ee92ada02f 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch
new file mode 100644
index 0000000000..41fa6cb3d2
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch
@@ -0,0 +1,42 @@
+From c99941ee53a8c6fcc466a088f8bd7108f04824e5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 6 Dec 2019 13:05:27 +0100
+Subject: [PATCH] mmc: sdhci: Silence MMC warnings
+
+When the MMC isn't plugged in, the driver will spam the console which is
+pretty annoying when using NFS.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/mmc/host/sdhci.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -39,7 +39,7 @@
+ pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+
+ #define SDHCI_DUMP(f, x...) \
+- pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
++ pr_debug("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+
+ #define MAX_TUNING_LOOP 40
+
+@@ -2754,7 +2754,7 @@ static void sdhci_timeout_timer(struct t
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
+- pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
++ pr_debug("%s: Timeout waiting for hardware cmd interrupt.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+@@ -2776,7 +2776,7 @@ static void sdhci_timeout_data_timer(str
+
+ if (host->data || host->data_cmd ||
+ (host->cmd && sdhci_data_line_cmd(host->cmd))) {
+- pr_err("%s: Timeout waiting for hardware interrupt.\n",
++ pr_debug("%s: Timeout waiting for hardware interrupt.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch b/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch
new file mode 100644
index 0000000000..01bdfee303
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch
@@ -0,0 +1,126 @@
+From 1a2a857af4fe6748fea53799e0007672faa7aa57 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 16:55:01 +0100
+Subject: [PATCH] dt-bindings: i2c: brcmstb: Convert the BRCMSTB
+ binding to a schema
+
+Switch the DT binding to a YAML schema to enable the DT validation.
+
+Cc: Kamal Dasu <kdasu.kdev@gmail.com>
+Cc: Wolfram Sang <wsa@the-dreams.de>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: linux-i2c@vger.kernel.org
+Cc: devicetree@vger.kernel.org
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/i2c/brcm,brcmstb-i2c.yaml | 59 +++++++++++++++++++
+ .../devicetree/bindings/i2c/i2c-brcmstb.txt | 26 --------
+ MAINTAINERS | 2 +-
+ 3 files changed, 60 insertions(+), 27 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
+ delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
+@@ -0,0 +1,59 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/i2c/brcm,brcmstb-i2c.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom STB BSC IIC Master Controller
++
++maintainers:
++ - Kamal Dasu <kdasu.kdev@gmail.com>
++
++allOf:
++ - $ref: /schemas/i2c/i2c-controller.yaml#
++
++properties:
++ compatible:
++ enum:
++ - brcm,brcmstb-i2c
++ - brcm,brcmper-i2c
++
++ reg:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++ interrupt-names:
++ maxItems: 1
++
++ clock-frequency:
++ enum:
++ - 46875
++ - 50000
++ - 93750
++ - 97500
++ - 187500
++ - 200000
++ - 375000
++ - 390000
++
++required:
++ - compatible
++ - reg
++ - clock-frequency
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ bsca: i2c@f0406200 {
++ clock-frequency = <390000>;
++ compatible = "brcm,brcmstb-i2c";
++ interrupt-parent = <&irq0_intc>;
++ reg = <0xf0406200 0x58>;
++ interrupts = <0x18>;
++ interrupt-names = "upg_bsca";
++ };
++
++...
+--- a/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
++++ /dev/null
+@@ -1,26 +0,0 @@
+-Broadcom stb bsc iic master controller
+-
+-Required properties:
+-
+-- compatible: should be "brcm,brcmstb-i2c" or "brcm,brcmper-i2c"
+-- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz
+- valid values are 375000, 390000, 187500, 200000
+- 93750, 97500, 46875 and 50000
+-- reg: specifies the base physical address and size of the registers
+-
+-Optional properties :
+-
+-- interrupts: specifies the interrupt number, the irq line to be used
+-- interrupt-names: Interrupt name string
+-
+-Example:
+-
+-bsca: i2c@f0406200 {
+- clock-frequency = <390000>;
+- compatible = "brcm,brcmstb-i2c";
+- interrupt-parent = <&irq0_intc>;
+- reg = <0xf0406200 0x58>;
+- interrupts = <0x18>;
+- interrupt-names = "upg_bsca";
+-};
+-
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3349,7 +3349,7 @@ L: linux-i2c@vger.kernel.org
+ L: bcm-kernel-feedback-list@broadcom.com
+ S: Supported
+ F: drivers/i2c/busses/i2c-brcmstb.c
+-F: Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
++F: Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
+
+ BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER
+ M: Al Cooper <alcooperx@gmail.com>
diff --git a/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch b/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch
new file mode 100644
index 0000000000..2716a13d25
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch
@@ -0,0 +1,96 @@
+From 16a6810e521eaf24249085b93b467f7bdf8e8a47 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 17 Dec 2019 09:58:34 +0100
+Subject: [PATCH] dt-bindings: i2c: brcmstb: Add BCM2711 BSC/AUTO-I2C
+ binding
+
+The HDMI blocks in the BCM2771 have an i2c controller to retrieve the
+EDID. This block is split into two parts, the BSC and the AUTO_I2C,
+lying in two separate register areas.
+
+The AUTO_I2C block has a mailbox-like interface and will take away the
+BSC control from the CPU if enabled. However, the BSC is the actually
+the same controller than the one supported by the brcmstb driver, and
+the AUTO_I2C doesn't really bring any immediate benefit.
+
+We can model it in the DT as a single device with two register range,
+which will allow us to use or or the other in the driver without
+changing anything in the DT.
+
+Cc: Kamal Dasu <kdasu.kdev@gmail.com>
+Cc: Wolfram Sang <wsa@the-dreams.de>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: linux-i2c@vger.kernel.org
+Cc: devicetree@vger.kernel.org
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/i2c/brcm,brcmstb-i2c.yaml | 40 ++++++++++++++++++-
+ 1 file changed, 39 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
++++ b/Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
+@@ -15,11 +15,21 @@ allOf:
+ properties:
+ compatible:
+ enum:
++ - brcm,bcm2711-hdmi-i2c
+ - brcm,brcmstb-i2c
+ - brcm,brcmper-i2c
+
+ reg:
+- maxItems: 1
++ minItems: 1
++ maxItems: 2
++ items:
++ - description: BSC register range
++ - description: Auto-I2C register range
++
++ reg-names:
++ items:
++ - const: bsc
++ - const: auto-i2c
+
+ interrupts:
+ maxItems: 1
+@@ -45,6 +55,26 @@ required:
+
+ unevaluatedProperties: false
+
++if:
++ properties:
++ compatible:
++ contains:
++ enum:
++ - brcm,bcm2711-hdmi-i2c
++
++then:
++ properties:
++ reg:
++ minItems: 2
++
++ required:
++ - reg-names
++
++else:
++ properties:
++ reg:
++ maxItems: 1
++
+ examples:
+ - |
+ bsca: i2c@f0406200 {
+@@ -56,4 +86,12 @@ examples:
+ interrupt-names = "upg_bsca";
+ };
+
++ - |
++ ddc0: i2c@7ef04500 {
++ compatible = "brcm,bcm2711-hdmi-i2c";
++ reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>;
++ reg-names = "bsc", "auto-i2c";
++ clock-frequency = <390000>;
++ };
++
+ ...
diff --git a/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch b/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch
new file mode 100644
index 0000000000..76ce7403da
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch
@@ -0,0 +1,87 @@
+From 4633a7bc5ffc15fe24c05e52f17a72c346baab6b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 17 Dec 2019 09:58:34 +0100
+Subject: [PATCH] i2c: brcmstb: Support BCM2711 HDMI BSC controllers
+
+The HDMI blocks in the BCM2771 have an i2c controller to retrieve the
+EDID. This block is split into two parts, the BSC and the AUTO_I2C,
+lying in two separate register areas.
+
+The AUTO_I2C block has a mailbox-like interface and will take away the
+BSC control from the CPU if enabled. However, the BSC is the actually
+the same controller than the one supported by the brcmstb driver, and
+the AUTO_I2C doesn't really bring any immediate benefit.
+
+Let's use the BSC then, but let's also tie the AUTO_I2C registers with a
+separate compatible so that we can enable AUTO_I2C if needed in the
+future.
+
+The AUTO_I2C is enabled by default at boot though, so we first need to
+release the BSC from the AUTO_I2C control.
+
+Cc: Kamal Dasu <kdasu.kdev@gmail.com>
+Cc: Wolfram Sang <wsa@the-dreams.de>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: linux-i2c@vger.kernel.org
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/i2c/busses/i2c-brcmstb.c | 33 ++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-brcmstb.c
++++ b/drivers/i2c/busses/i2c-brcmstb.c
+@@ -580,6 +580,31 @@ static void brcmstb_i2c_set_bsc_reg_defa
+ brcmstb_i2c_set_bus_speed(dev);
+ }
+
++#define AUTOI2C_CTRL0 0x26c
++#define AUTOI2C_CTRL0_RELEASE_BSC BIT(1)
++
++static int bcm2711_release_bsc(struct brcmstb_i2c_dev *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev->device);
++ struct resource *iomem;
++ void __iomem *autoi2c;
++
++ /* Map hardware registers */
++ iomem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "auto-i2c");
++ autoi2c = devm_ioremap_resource(&pdev->dev, iomem);
++ if (IS_ERR(autoi2c))
++ return PTR_ERR(autoi2c);
++
++ writel(AUTOI2C_CTRL0_RELEASE_BSC, autoi2c + AUTOI2C_CTRL0);
++ devm_iounmap(&pdev->dev, autoi2c);
++
++ /* We need to reset the controller after the release */
++ dev->bsc_regmap->iic_enable = 0;
++ bsc_writel(dev, dev->bsc_regmap->iic_enable, iic_enable);
++
++ return 0;
++}
++
+ static int brcmstb_i2c_probe(struct platform_device *pdev)
+ {
+ int rc = 0;
+@@ -609,6 +634,13 @@ static int brcmstb_i2c_probe(struct plat
+ goto probe_errorout;
+ }
+
++ if (of_device_is_compatible(dev->device->of_node,
++ "brcm,bcm2711-hdmi-i2c")) {
++ rc = bcm2711_release_bsc(dev);
++ if (rc)
++ goto probe_errorout;
++ }
++
+ rc = of_property_read_string(dev->device->of_node, "interrupt-names",
+ &int_name);
+ if (rc < 0)
+@@ -705,6 +737,7 @@ static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm,
+ static const struct of_device_id brcmstb_i2c_of_match[] = {
+ {.compatible = "brcm,brcmstb-i2c"},
+ {.compatible = "brcm,brcmper-i2c"},
++ {.compatible = "brcm,bcm2711-hdmi-i2c"},
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch b/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch
new file mode 100644
index 0000000000..a21035b69b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch
@@ -0,0 +1,31 @@
+From ec7414dd69a7ea701d0d5676fdb32332cd5f10ec Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 14 Jan 2020 13:36:42 +0100
+Subject: [PATCH] i2c: brcmstb: Allow to compile it on BCM2835
+
+The BCM2711, supported by ARCH_BCM2835, also has a controller by the
+brcmstb driver so let's allow it to be compiled on that platform.
+
+Cc: Kamal Dasu <kdasu.kdev@gmail.com>
+Cc: Wolfram Sang <wsa@the-dreams.de>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: linux-i2c@vger.kernel.org
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/i2c/busses/Kconfig | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -491,8 +491,8 @@ config I2C_BCM_KONA
+
+ config I2C_BRCMSTB
+ tristate "BRCM Settop/DSL I2C controller"
+- depends on ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM_63XX || \
+- COMPILE_TEST
++ depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \
++ ARCH_BCM_63XX || COMPILE_TEST
+ default y
+ help
+ If you say yes to this option, support will be included for the
diff --git a/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch b/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch
new file mode 100644
index 0000000000..f3587060d1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch
@@ -0,0 +1,63 @@
+From 9d4360fc454056fffa9ca487270aca9179906f5d Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 17:51:09 +0100
+Subject: [PATCH] dt-bindings: clock: Add a binding for the RPi
+ Firmware clocks
+
+The firmare running on the RPi VideoCore can be used to discover and
+change the various clocks running in the BCM2711. Since devices will
+need to use them through the DT, let's add a pretty simple binding.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../clock/raspberrypi,firmware-clocks.yaml | 39 +++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.yaml
+@@ -0,0 +1,39 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/clock/raspberrypi,firmware-clocks.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: RaspberryPi Firmware Clocks Device Tree Bindings
++
++maintainers:
++ - Maxime Ripard <mripard@kernel.org>
++
++properties:
++ "#clock-cells":
++ const: 1
++
++ compatible:
++ const: raspberrypi,firmware-clocks
++
++ raspberrypi,firmware:
++ $ref: /schemas/types.yaml#/definitions/phandle
++ description: >
++ Phandle to the mailbox node to communicate with the firmware.
++
++required:
++ - "#clock-cells"
++ - compatible
++ - raspberrypi,firmware
++
++additionalProperties: false
++
++examples:
++ - |
++ firmware_clocks: firmware-clocks {
++ compatible = "raspberrypi,firmware-clocks";
++ raspberrypi,firmware = <&firmware>;
++ #clock-cells = <1>;
++ };
++
++...
diff --git a/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch b/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch
new file mode 100644
index 0000000000..d622d9ded5
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch
@@ -0,0 +1,60 @@
+From faeea753ba262e2a781570f9db23c5773c5d20e7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 23 Dec 2019 19:58:08 +0100
+Subject: [PATCH] clk: bcm: rpi: Allow the driver to be probed by DT
+
+The current firmware clock driver for the RaspberryPi can only be probed by
+manually registering an associated platform_device.
+
+While this works fine for cpufreq where the device gets attached a clkdev
+lookup, it would be tedious to maintain a table of all the devices using
+one of the clocks exposed by the firmware.
+
+Since the DT on the other hand is the perfect place to store those
+associations, make the firmware clocks driver probe-able through the device
+tree so that we can represent it as a node.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -255,15 +255,13 @@ static int raspberrypi_clk_probe(struct
+ struct raspberrypi_clk *rpi;
+ int ret;
+
+- firmware_node = of_find_compatible_node(NULL, NULL,
+- "raspberrypi,bcm2835-firmware");
++ firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
+ if (!firmware_node) {
+ dev_err(dev, "Missing firmware node\n");
+ return -ENOENT;
+ }
+
+ firmware = rpi_firmware_get(firmware_node);
+- of_node_put(firmware_node);
+ if (!firmware)
+ return -EPROBE_DEFER;
+
+@@ -300,9 +298,16 @@ static int raspberrypi_clk_remove(struct
+ return 0;
+ }
+
++static const struct of_device_id raspberrypi_clk_match[] = {
++ { .compatible = "raspberrypi,firmware-clocks" },
++ { },
++};
++MODULE_DEVICE_TABLE(of, raspberrypi_clk_match);
++
+ static struct platform_driver raspberrypi_clk_driver = {
+ .driver = {
+ .name = "raspberrypi-clk",
++ .of_match_table = raspberrypi_clk_match,
+ },
+ .probe = raspberrypi_clk_probe,
+ .remove = raspberrypi_clk_remove,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch b/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch
new file mode 100644
index 0000000000..5c7cb2b437
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch
@@ -0,0 +1,32 @@
+From 7916b5f66f3becb9f223e8a6d8c0a6c0edd8964c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 14:17:54 +0100
+Subject: [PATCH] clk: bcm: rpi: Statically init clk_init_data
+
+Instead of declaring the clk_init_data and then calling memset on it, just
+initialise properly.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -175,11 +175,10 @@ static const struct clk_ops raspberrypi_
+
+ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
+ {
++ struct clk_init_data init = {};
+ u32 min_rate = 0, max_rate = 0;
+- struct clk_init_data init;
+ int ret;
+
+- memset(&init, 0, sizeof(init));
+
+ /* All of the PLLs derive from the external oscillator. */
+ init.parent_names = (const char *[]){ "osc" };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch b/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch
new file mode 100644
index 0000000000..55f86ae340
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch
@@ -0,0 +1,56 @@
+From 3a4163613b7f6e628e7b5a0d3a546523d1d03bb7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 15:40:00 +0100
+Subject: [PATCH] clk: bcm: rpi: Use clk_hw_register for pllb_arm
+
+The pllb_arm clock is defined as a fixed factor clock with the pllb clock
+as a parent. However, all its configuration is entirely static, and thus we
+don't really need to call clk_hw_register_fixed_factor() but can simply call
+clk_hw_register() with a static clk_fixed_factor structure.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 24 ++++++++++++++++++------
+ 1 file changed, 18 insertions(+), 6 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -225,16 +225,28 @@ static int raspberrypi_register_pllb(str
+ return devm_clk_hw_register(rpi->dev, &rpi->pllb);
+ }
+
++static struct clk_fixed_factor raspberrypi_clk_pllb_arm = {
++ .mult = 1,
++ .div = 2,
++ .hw.init = &(struct clk_init_data) {
++ .name = "pllb_arm",
++ .parent_names = (const char *[]){ "pllb" },
++ .num_parents = 1,
++ .ops = &clk_fixed_factor_ops,
++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
++ },
++};
++
+ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
+ {
+- rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev,
+- "pllb_arm", "pllb",
+- CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
+- 1, 2);
+- if (IS_ERR(rpi->pllb_arm)) {
++ int ret;
++
++ ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
++ if (ret) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
+- return PTR_ERR(rpi->pllb_arm);
++ return ret;
+ }
++ rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw;
+
+ rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0");
+ if (!rpi->pllb_arm_lookup) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch
new file mode 100644
index 0000000000..32fc94c675
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch
@@ -0,0 +1,45 @@
+From 5272507555c0ecf02c0dfd78303e86e8eb096191 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 15:41:37 +0100
+Subject: [PATCH] clk: bcm: rpi: Remove global pllb_arm clock pointer
+
+The pllb_arm clk_hw pointer in the raspberry_clk structure isn't used
+anywhere but in the raspberrypi_register_pllb_arm.
+
+Let's remove it, this will make our lives easier in future patches.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -40,7 +40,6 @@ struct raspberrypi_clk {
+ unsigned long max_rate;
+
+ struct clk_hw pllb;
+- struct clk_hw *pllb_arm;
+ struct clk_lookup *pllb_arm_lookup;
+ };
+
+@@ -246,12 +245,12 @@ static int raspberrypi_register_pllb_arm
+ dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
+ return ret;
+ }
+- rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw;
+
+- rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0");
++ rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw,
++ NULL, "cpu0");
+ if (!rpi->pllb_arm_lookup) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
+- clk_hw_unregister_fixed_factor(rpi->pllb_arm);
++ clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw);
+ return -ENOMEM;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch b/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch
new file mode 100644
index 0000000000..425830d5c4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch
@@ -0,0 +1,40 @@
+From aeb75ab90c35c7bd9778a71d606d52ac3e8ff02d Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 15:42:40 +0100
+Subject: [PATCH] clk: bcm: rpi: Make sure pllb_arm is removed
+
+The pllb_arm clock was created at probe time, but was never removed if
+something went wrong later in probe, or if the driver was ever removed from
+the system.
+
+Now that we are using clk_hw_register, we can just use its managed variant
+to take care of that for us.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -240,7 +240,7 @@ static int raspberrypi_register_pllb_arm
+ {
+ int ret;
+
+- ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
++ ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
+ return ret;
+@@ -250,7 +250,6 @@ static int raspberrypi_register_pllb_arm
+ NULL, "cpu0");
+ if (!rpi->pllb_arm_lookup) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
+- clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw);
+ return -ENOMEM;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch
new file mode 100644
index 0000000000..e193b7906a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch
@@ -0,0 +1,51 @@
+From 35be338b9532bb1fda95b9f1123758f57f785f93 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 15:46:24 +0100
+Subject: [PATCH] clk: bcm: rpi: Remove pllb_arm_lookup global pointer
+
+The pllb_arm_lookup pointer in the struct raspberrypi_clk is not used for
+anything but to store the returned pointer to clkdev_hw_create, and is not
+used anywhere else in the driver.
+
+Let's remove that global pointer from the structure.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -40,7 +40,6 @@ struct raspberrypi_clk {
+ unsigned long max_rate;
+
+ struct clk_hw pllb;
+- struct clk_lookup *pllb_arm_lookup;
+ };
+
+ /*
+@@ -238,6 +237,7 @@ static struct clk_fixed_factor raspberry
+
+ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
+ {
++ struct clk_lookup *pllb_arm_lookup;
+ int ret;
+
+ ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
+@@ -246,9 +246,9 @@ static int raspberrypi_register_pllb_arm
+ return ret;
+ }
+
+- rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw,
+- NULL, "cpu0");
+- if (!rpi->pllb_arm_lookup) {
++ pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw,
++ NULL, "cpu0");
++ if (!pllb_arm_lookup) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
+ return -ENOMEM;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch b/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch
new file mode 100644
index 0000000000..e4a129f233
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch
@@ -0,0 +1,45 @@
+From 2d1c30a79cb9d390d2425852ff8c9527c31b3ab8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 12 Feb 2020 14:21:45 +0100
+Subject: [PATCH] clk: bcm: rpi: Switch to clk_hw_register_clkdev
+
+Since we don't care about retrieving the clk_lookup structure pointer
+returned by clkdev_hw_create, we can just use the clk_hw_register_clkdev
+function.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -237,7 +237,6 @@ static struct clk_fixed_factor raspberry
+
+ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
+ {
+- struct clk_lookup *pllb_arm_lookup;
+ int ret;
+
+ ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
+@@ -246,11 +245,11 @@ static int raspberrypi_register_pllb_arm
+ return ret;
+ }
+
+- pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw,
+- NULL, "cpu0");
+- if (!pllb_arm_lookup) {
+- dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
+- return -ENOMEM;
++ ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw,
++ NULL, "cpu0");
++ if (ret) {
++ dev_err(rpi->dev, "Failed to initialize clkdev\n");
++ return ret;
+ }
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch b/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch
new file mode 100644
index 0000000000..7c887795ee
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch
@@ -0,0 +1,34 @@
+From 31fe609e3f5f9d4e52f0f88f8ebfd20fb606c672 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 15:47:13 +0100
+Subject: [PATCH] clk: bcm: rpi: Make sure the clkdev lookup is removed
+
+The clkdev lookup created for the cpufreq device is never removed if
+there's an issue later in probe or at module removal time.
+
+Let's convert to the managed variant of the clk_hw_register_clkdev function
+to make sure it happens.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -245,8 +245,9 @@ static int raspberrypi_register_pllb_arm
+ return ret;
+ }
+
+- ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw,
+- NULL, "cpu0");
++ ret = devm_clk_hw_register_clkdev(rpi->dev,
++ &raspberrypi_clk_pllb_arm.hw,
++ NULL, "cpu0");
+ if (ret) {
+ dev_err(rpi->dev, "Failed to initialize clkdev\n");
+ return ret;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch
new file mode 100644
index 0000000000..85b6f4a6e2
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch
@@ -0,0 +1,126 @@
+From 8af8b61bf6b5689af9f29f0e04e57c832dad0406 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 16:01:33 +0100
+Subject: [PATCH] clk: bcm: rpi: Create a data structure for the clocks
+
+So far the driver has really only been providing a single clock, and stored
+both the data associated to that clock in particular with the data
+associated to the "controller".
+
+Since we will change that in the future, let's decouple the clock data from
+the provider data.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 40 ++++++++++++++++++++-----------
+ 1 file changed, 26 insertions(+), 14 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -35,11 +35,15 @@ struct raspberrypi_clk {
+ struct device *dev;
+ struct rpi_firmware *firmware;
+ struct platform_device *cpufreq;
++};
++
++struct raspberrypi_clk_data {
++ struct clk_hw hw;
+
+ unsigned long min_rate;
+ unsigned long max_rate;
+
+- struct clk_hw pllb;
++ struct raspberrypi_clk *rpi;
+ };
+
+ /*
+@@ -83,8 +87,9 @@ static int raspberrypi_clock_property(st
+
+ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
+ {
+- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
+- pllb);
++ struct raspberrypi_clk_data *data =
++ container_of(hw, struct raspberrypi_clk_data, hw);
++ struct raspberrypi_clk *rpi = data->rpi;
+ u32 val = 0;
+ int ret;
+
+@@ -101,8 +106,9 @@ static int raspberrypi_fw_pll_is_on(stru
+ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+ {
+- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
+- pllb);
++ struct raspberrypi_clk_data *data =
++ container_of(hw, struct raspberrypi_clk_data, hw);
++ struct raspberrypi_clk *rpi = data->rpi;
+ u32 val = 0;
+ int ret;
+
+@@ -119,8 +125,9 @@ static unsigned long raspberrypi_fw_pll_
+ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+ {
+- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
+- pllb);
++ struct raspberrypi_clk_data *data =
++ container_of(hw, struct raspberrypi_clk_data, hw);
++ struct raspberrypi_clk *rpi = data->rpi;
+ u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
+ int ret;
+
+@@ -142,13 +149,13 @@ static int raspberrypi_fw_pll_set_rate(s
+ static int raspberrypi_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+ {
+- struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
+- pllb);
++ struct raspberrypi_clk_data *data =
++ container_of(hw, struct raspberrypi_clk_data, hw);
+ u64 div, final_rate;
+ u32 ndiv, fdiv;
+
+ /* We can't use req->rate directly as it would overflow */
+- final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate);
++ final_rate = clamp(req->rate, data->min_rate, data->max_rate);
+
+ div = (u64)final_rate << A2W_PLL_FRAC_BITS;
+ do_div(div, req->best_parent_rate);
+@@ -173,10 +180,15 @@ static const struct clk_ops raspberrypi_
+
+ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
+ {
++ struct raspberrypi_clk_data *data;
+ struct clk_init_data init = {};
+ u32 min_rate = 0, max_rate = 0;
+ int ret;
+
++ data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++ data->rpi = rpi;
+
+ /* All of the PLLs derive from the external oscillator. */
+ init.parent_names = (const char *[]){ "osc" };
+@@ -215,12 +227,12 @@ static int raspberrypi_register_pllb(str
+ dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n",
+ min_rate, max_rate);
+
+- rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
+- rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++ data->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++ data->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
+
+- rpi->pllb.init = &init;
++ data->hw.init = &init;
+
+- return devm_clk_hw_register(rpi->dev, &rpi->pllb);
++ return devm_clk_hw_register(rpi->dev, &data->hw);
+ }
+
+ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch b/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch
new file mode 100644
index 0000000000..09f023e09c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch
@@ -0,0 +1,86 @@
+From 98d529ffea66937e8a9ba8b69172bb9c599cfa39 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 16:04:16 +0100
+Subject: [PATCH] clk: bcm: rpi: Add clock id to data
+
+The driver has really only supported one clock so far and has hardcoded the
+ID used in communications with the firmware in all the functions
+implementing the clock framework hooks. Let's store that in the clock data
+structure so that we can support more clocks later on.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -39,6 +39,7 @@ struct raspberrypi_clk {
+
+ struct raspberrypi_clk_data {
+ struct clk_hw hw;
++ unsigned id;
+
+ unsigned long min_rate;
+ unsigned long max_rate;
+@@ -95,7 +96,7 @@ static int raspberrypi_fw_pll_is_on(stru
+
+ ret = raspberrypi_clock_property(rpi->firmware,
+ RPI_FIRMWARE_GET_CLOCK_STATE,
+- RPI_FIRMWARE_ARM_CLK_ID, &val);
++ data->id, &val);
+ if (ret)
+ return 0;
+
+@@ -114,8 +115,7 @@ static unsigned long raspberrypi_fw_pll_
+
+ ret = raspberrypi_clock_property(rpi->firmware,
+ RPI_FIRMWARE_GET_CLOCK_RATE,
+- RPI_FIRMWARE_ARM_CLK_ID,
+- &val);
++ data->id, &val);
+ if (ret)
+ return ret;
+
+@@ -133,8 +133,7 @@ static int raspberrypi_fw_pll_set_rate(s
+
+ ret = raspberrypi_clock_property(rpi->firmware,
+ RPI_FIRMWARE_SET_CLOCK_RATE,
+- RPI_FIRMWARE_ARM_CLK_ID,
+- &new_rate);
++ data->id, &new_rate);
+ if (ret)
+ dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
+ clk_hw_get_name(hw), ret);
+@@ -189,6 +188,7 @@ static int raspberrypi_register_pllb(str
+ if (!data)
+ return -ENOMEM;
+ data->rpi = rpi;
++ data->id = RPI_FIRMWARE_ARM_CLK_ID;
+
+ /* All of the PLLs derive from the external oscillator. */
+ init.parent_names = (const char *[]){ "osc" };
+@@ -200,8 +200,7 @@ static int raspberrypi_register_pllb(str
+ /* Get min & max rates set by the firmware */
+ ret = raspberrypi_clock_property(rpi->firmware,
+ RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
+- RPI_FIRMWARE_ARM_CLK_ID,
+- &min_rate);
++ data->id, &min_rate);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
+ init.name, ret);
+@@ -210,8 +209,7 @@ static int raspberrypi_register_pllb(str
+
+ ret = raspberrypi_clock_property(rpi->firmware,
+ RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+- RPI_FIRMWARE_ARM_CLK_ID,
+- &max_rate);
++ data->id, &max_rate);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
+ init.name, ret);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch b/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch
new file mode 100644
index 0000000000..7822ff0d11
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch
@@ -0,0 +1,96 @@
+From 1231dbeb8bfeda68c53854cc68016acd74665079 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 16:08:17 +0100
+Subject: [PATCH] clk: bcm: rpi: Pass the clocks data to the firmware
+ function
+
+The raspberry_clock_property only takes the clock ID as an argument, but
+now that we have a clock data structure it makes more sense to just pass
+that structure instead.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 29 ++++++++++++++---------------
+ 1 file changed, 14 insertions(+), 15 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -67,11 +67,12 @@ struct raspberrypi_firmware_prop {
+ __le32 disable_turbo;
+ } __packed;
+
+-static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag,
+- u32 clk, u32 *val)
++static int raspberrypi_clock_property(struct rpi_firmware *firmware,
++ const struct raspberrypi_clk_data *data,
++ u32 tag, u32 *val)
+ {
+ struct raspberrypi_firmware_prop msg = {
+- .id = cpu_to_le32(clk),
++ .id = cpu_to_le32(data->id),
+ .val = cpu_to_le32(*val),
+ .disable_turbo = cpu_to_le32(1),
+ };
+@@ -94,9 +95,8 @@ static int raspberrypi_fw_pll_is_on(stru
+ u32 val = 0;
+ int ret;
+
+- ret = raspberrypi_clock_property(rpi->firmware,
+- RPI_FIRMWARE_GET_CLOCK_STATE,
+- data->id, &val);
++ ret = raspberrypi_clock_property(rpi->firmware, data,
++ RPI_FIRMWARE_GET_CLOCK_STATE, &val);
+ if (ret)
+ return 0;
+
+@@ -113,9 +113,8 @@ static unsigned long raspberrypi_fw_pll_
+ u32 val = 0;
+ int ret;
+
+- ret = raspberrypi_clock_property(rpi->firmware,
+- RPI_FIRMWARE_GET_CLOCK_RATE,
+- data->id, &val);
++ ret = raspberrypi_clock_property(rpi->firmware, data,
++ RPI_FIRMWARE_GET_CLOCK_RATE, &val);
+ if (ret)
+ return ret;
+
+@@ -131,9 +130,9 @@ static int raspberrypi_fw_pll_set_rate(s
+ u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
+ int ret;
+
+- ret = raspberrypi_clock_property(rpi->firmware,
++ ret = raspberrypi_clock_property(rpi->firmware, data,
+ RPI_FIRMWARE_SET_CLOCK_RATE,
+- data->id, &new_rate);
++ &new_rate);
+ if (ret)
+ dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
+ clk_hw_get_name(hw), ret);
+@@ -198,18 +197,18 @@ static int raspberrypi_register_pllb(str
+ init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED;
+
+ /* Get min & max rates set by the firmware */
+- ret = raspberrypi_clock_property(rpi->firmware,
++ ret = raspberrypi_clock_property(rpi->firmware, data,
+ RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
+- data->id, &min_rate);
++ &min_rate);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
+ init.name, ret);
+ return ret;
+ }
+
+- ret = raspberrypi_clock_property(rpi->firmware,
++ ret = raspberrypi_clock_property(rpi->firmware, data,
+ RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+- data->id, &max_rate);
++ &max_rate);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
+ init.name, ret);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch b/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch
new file mode 100644
index 0000000000..1a6c9813de
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch
@@ -0,0 +1,40 @@
+From c37a4cc3fc34b6c53c331d0d079df322082ff183 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 20 Feb 2020 12:45:47 +0100
+Subject: [PATCH] clk: bcm: rpi: Rename is_prepared function
+
+The raspberrypi_fw_pll_is_on function doesn't only apply to PLL
+registered in the driver, but any clock exposed by the firmware.
+
+Since we also implement the is_prepared hook, make the function
+consistent with the other function names.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -87,7 +87,7 @@ static int raspberrypi_clock_property(st
+ return 0;
+ }
+
+-static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
++static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
+ {
+ struct raspberrypi_clk_data *data =
+ container_of(hw, struct raspberrypi_clk_data, hw);
+@@ -170,7 +170,7 @@ static int raspberrypi_pll_determine_rat
+ }
+
+ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = {
+- .is_prepared = raspberrypi_fw_pll_is_on,
++ .is_prepared = raspberrypi_fw_is_prepared,
+ .recalc_rate = raspberrypi_fw_pll_get_rate,
+ .set_rate = raspberrypi_fw_pll_set_rate,
+ .determine_rate = raspberrypi_pll_determine_rate,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch b/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch
new file mode 100644
index 0000000000..38720b89bd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch
@@ -0,0 +1,80 @@
+From e2537b383e247198347e7124876b9ead531dbeef Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 16:14:18 +0100
+Subject: [PATCH] clk: bcm: rpi: Split pllb clock hooks
+
+The driver only supports the pllb for now and all the clock framework hooks
+are a mix of the generic firmware interface and the specifics of the pllb.
+Since we will support more clocks in the future let's split the generic and
+specific hooks
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 30 ++++++++++++++++++++++--------
+ 1 file changed, 22 insertions(+), 8 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -104,8 +104,8 @@ static int raspberrypi_fw_is_prepared(st
+ }
+
+
+-static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
+- unsigned long parent_rate)
++static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
+ {
+ struct raspberrypi_clk_data *data =
+ container_of(hw, struct raspberrypi_clk_data, hw);
+@@ -118,21 +118,27 @@ static unsigned long raspberrypi_fw_pll_
+ if (ret)
+ return ret;
+
+- return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++ return val;
+ }
+
+-static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+- unsigned long parent_rate)
++static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ return raspberrypi_fw_get_rate(hw, parent_rate) *
++ RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++}
++
++static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
+ {
+ struct raspberrypi_clk_data *data =
+ container_of(hw, struct raspberrypi_clk_data, hw);
+ struct raspberrypi_clk *rpi = data->rpi;
+- u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++ u32 _rate = rate;
+ int ret;
+
+ ret = raspberrypi_clock_property(rpi->firmware, data,
+- RPI_FIRMWARE_SET_CLOCK_RATE,
+- &new_rate);
++ RPI_FIRMWARE_SET_CLOCK_RATE, &_rate);
+ if (ret)
+ dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
+ clk_hw_get_name(hw), ret);
+@@ -140,6 +146,14 @@ static int raspberrypi_fw_pll_set_rate(s
+ return ret;
+ }
+
++static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
++
++ return raspberrypi_fw_set_rate(hw, new_rate, parent_rate);
++}
++
+ /*
+ * Sadly there is no firmware rate rounding interface. We borrowed it from
+ * clk-bcm2835.
diff --git a/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch
new file mode 100644
index 0000000000..633cf18782
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch
@@ -0,0 +1,144 @@
+From 5272bad5ff927362e5d12da82eb819a8d1444da6 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 16:30:01 +0100
+Subject: [PATCH] clk: bcm: rpi: Make the PLLB registration function
+ return a clk_hw
+
+The raspberrypi_register_pllb has been returning an integer so far to
+notify whether the functions has exited successfully or not.
+
+However, the OF provider functions in the clock framework require access to
+the clk_hw structure so that we can expose those clocks to device tree
+consumers.
+
+Since we'll want that for the future clocks, let's return a clk_hw pointer
+instead of the return code.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 40 +++++++++++++++++--------------
+ 1 file changed, 22 insertions(+), 18 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -190,7 +190,7 @@ static const struct clk_ops raspberrypi_
+ .determine_rate = raspberrypi_pll_determine_rate,
+ };
+
+-static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
++static struct clk_hw *raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
+ {
+ struct raspberrypi_clk_data *data;
+ struct clk_init_data init = {};
+@@ -199,7 +199,7 @@ static int raspberrypi_register_pllb(str
+
+ data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+ data->rpi = rpi;
+ data->id = RPI_FIRMWARE_ARM_CLK_ID;
+
+@@ -217,7 +217,7 @@ static int raspberrypi_register_pllb(str
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
+ init.name, ret);
+- return ret;
++ return ERR_PTR(ret);
+ }
+
+ ret = raspberrypi_clock_property(rpi->firmware, data,
+@@ -226,13 +226,13 @@ static int raspberrypi_register_pllb(str
+ if (ret) {
+ dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
+ init.name, ret);
+- return ret;
++ return ERR_PTR(ret);
+ }
+
+ if (!min_rate || !max_rate) {
+ dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n",
+ min_rate, max_rate);
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ }
+
+ dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n",
+@@ -243,7 +243,11 @@ static int raspberrypi_register_pllb(str
+
+ data->hw.init = &init;
+
+- return devm_clk_hw_register(rpi->dev, &data->hw);
++ ret = devm_clk_hw_register(rpi->dev, &data->hw);
++ if (ret)
++ return ERR_PTR(ret);
++
++ return &data->hw;
+ }
+
+ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = {
+@@ -258,14 +262,14 @@ static struct clk_fixed_factor raspberry
+ },
+ };
+
+-static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
++static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
+ {
+ int ret;
+
+ ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw);
+ if (ret) {
+ dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
+- return ret;
++ return ERR_PTR(ret);
+ }
+
+ ret = devm_clk_hw_register_clkdev(rpi->dev,
+@@ -273,10 +277,10 @@ static int raspberrypi_register_pllb_arm
+ NULL, "cpu0");
+ if (ret) {
+ dev_err(rpi->dev, "Failed to initialize clkdev\n");
+- return ret;
++ return ERR_PTR(ret);
+ }
+
+- return 0;
++ return &raspberrypi_clk_pllb_arm.hw;
+ }
+
+ static int raspberrypi_clk_probe(struct platform_device *pdev)
+@@ -285,7 +289,7 @@ static int raspberrypi_clk_probe(struct
+ struct device *dev = &pdev->dev;
+ struct rpi_firmware *firmware;
+ struct raspberrypi_clk *rpi;
+- int ret;
++ struct clk_hw *hw;
+
+ firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
+ if (!firmware_node) {
+@@ -305,15 +309,15 @@ static int raspberrypi_clk_probe(struct
+ rpi->firmware = firmware;
+ platform_set_drvdata(pdev, rpi);
+
+- ret = raspberrypi_register_pllb(rpi);
+- if (ret) {
+- dev_err(dev, "Failed to initialize pllb, %d\n", ret);
+- return ret;
++ hw = raspberrypi_register_pllb(rpi);
++ if (IS_ERR(hw)) {
++ dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw));
++ return PTR_ERR(hw);
+ }
+
+- ret = raspberrypi_register_pllb_arm(rpi);
+- if (ret)
+- return ret;
++ hw = raspberrypi_register_pllb_arm(rpi);
++ if (IS_ERR(hw))
++ return PTR_ERR(hw);
+
+ rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq",
+ -1, NULL, 0);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch
new file mode 100644
index 0000000000..99fdf8fbd6
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch
@@ -0,0 +1,67 @@
+From 19f7515528fbd1dc0d45e4b5ce6531c1406fc8d8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 7 Feb 2020 17:03:46 +0100
+Subject: [PATCH] clk: bcm: rpi: Add DT provider for the clocks
+
+For the upcoming registration of the clocks provided by the firmware, make
+sure it's exposed to the device tree providers.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: linux-clk@vger.kernel.org
+Reviewed-by: Stephen Boyd <sboyd@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -31,6 +31,8 @@
+
+ #define A2W_PLL_FRAC_BITS 20
+
++#define NUM_FW_CLKS 16
++
+ struct raspberrypi_clk {
+ struct device *dev;
+ struct rpi_firmware *firmware;
+@@ -285,11 +287,13 @@ static struct clk_hw *raspberrypi_regist
+
+ static int raspberrypi_clk_probe(struct platform_device *pdev)
+ {
++ struct clk_hw_onecell_data *clk_data;
+ struct device_node *firmware_node;
+ struct device *dev = &pdev->dev;
+ struct rpi_firmware *firmware;
+ struct raspberrypi_clk *rpi;
+ struct clk_hw *hw;
++ int ret;
+
+ firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
+ if (!firmware_node) {
+@@ -309,6 +313,11 @@ static int raspberrypi_clk_probe(struct
+ rpi->firmware = firmware;
+ platform_set_drvdata(pdev, rpi);
+
++ clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, NUM_FW_CLKS),
++ GFP_KERNEL);
++ if (!clk_data)
++ return -ENOMEM;
++
+ hw = raspberrypi_register_pllb(rpi);
+ if (IS_ERR(hw)) {
+ dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw));
+@@ -318,6 +327,13 @@ static int raspberrypi_clk_probe(struct
+ hw = raspberrypi_register_pllb_arm(rpi);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
++ clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw;
++ clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1;
++
++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
++ clk_data);
++ if (ret)
++ return ret;
+
+ rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq",
+ -1, NULL, 0);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch
new file mode 100644
index 0000000000..eaa4a81141
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch
@@ -0,0 +1,173 @@
+From 54276fe20c0735dd18d298891b71b664ea54962d Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 10 Feb 2020 14:06:09 +0100
+Subject: [PATCH] clk: bcm: rpi: Discover the firmware clocks
+
+The RaspberryPi4 firmware actually exposes more clocks than are currently
+handled by the driver and we will need to change some of them directly
+based on the pixel rate for the display related clocks, or the load for the
+GPU.
+
+This rate change can have a number of side-effects, including adjusting the
+various PLL voltages or the PLL parents. The firmware will also update
+those clocks by itself for example if the SoC runs too hot.
+
+In order to make Linux play as nice as possible with those constraints, it
+makes sense to rely on the firmware clocks as much as possible.
+
+Fortunately,t he firmware has an interface to discover the clocks it
+exposes.
+
+Let's use it to discover, register the clocks in the clocks framework and
+then expose them through the device tree for consumers to use them.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 104 ++++++++++++++++++---
+ include/soc/bcm2835/raspberrypi-firmware.h | 5 +
+ 2 files changed, 97 insertions(+), 12 deletions(-)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -285,6 +285,95 @@ static struct clk_hw *raspberrypi_regist
+ return &raspberrypi_clk_pllb_arm.hw;
+ }
+
++static long raspberrypi_fw_dumb_round_rate(struct clk_hw *hw,
++ unsigned long rate,
++ unsigned long *parent_rate)
++{
++ /*
++ * The firmware will do the rounding but that isn't part of
++ * the interface with the firmware, so we just do our best
++ * here.
++ */
++ return rate;
++}
++
++static const struct clk_ops raspberrypi_firmware_clk_ops = {
++ .is_prepared = raspberrypi_fw_is_prepared,
++ .recalc_rate = raspberrypi_fw_get_rate,
++ .round_rate = raspberrypi_fw_dumb_round_rate,
++ .set_rate = raspberrypi_fw_set_rate,
++};
++
++static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
++ unsigned int parent,
++ unsigned int id)
++{
++ struct raspberrypi_clk_data *data;
++ struct clk_init_data init = {};
++ int ret;
++
++ if (id == RPI_FIRMWARE_ARM_CLK_ID) {
++ struct clk_hw *hw;
++
++ hw = raspberrypi_register_pllb(rpi);
++ if (IS_ERR(hw)) {
++ dev_err(rpi->dev, "Failed to initialize pllb, %ld\n",
++ PTR_ERR(hw));
++ return hw;
++ }
++
++ return raspberrypi_register_pllb_arm(rpi);
++ }
++
++ data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return ERR_PTR(-ENOMEM);
++ data->rpi = rpi;
++ data->id = id;
++
++ init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id);
++ init.ops = &raspberrypi_firmware_clk_ops;
++ init.flags = CLK_GET_RATE_NOCACHE;
++
++ data->hw.init = &init;
++
++ ret = devm_clk_hw_register(rpi->dev, &data->hw);
++ if (ret)
++ return ERR_PTR(ret);
++
++ return &data->hw;
++}
++
++static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
++ struct clk_hw_onecell_data *data)
++{
++ struct rpi_firmware_get_clocks_response *clks;
++ int ret;
++
++ clks = devm_kcalloc(rpi->dev, sizeof(*clks), NUM_FW_CLKS, GFP_KERNEL);
++ if (!clks)
++ return -ENOMEM;
++
++ ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS,
++ clks, sizeof(*clks) * NUM_FW_CLKS);
++ if (ret)
++ return ret;
++
++ while (clks->id) {
++ struct clk_hw *hw;
++
++ hw = raspberrypi_clk_register(rpi, clks->parent, clks->id);
++ if (IS_ERR(hw))
++ return PTR_ERR(hw);
++
++ data->hws[clks->id] = hw;
++ data->num = clks->id + 1;
++ clks++;
++ }
++
++ return 0;
++}
++
+ static int raspberrypi_clk_probe(struct platform_device *pdev)
+ {
+ struct clk_hw_onecell_data *clk_data;
+@@ -292,7 +381,6 @@ static int raspberrypi_clk_probe(struct
+ struct device *dev = &pdev->dev;
+ struct rpi_firmware *firmware;
+ struct raspberrypi_clk *rpi;
+- struct clk_hw *hw;
+ int ret;
+
+ firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
+@@ -318,17 +406,9 @@ static int raspberrypi_clk_probe(struct
+ if (!clk_data)
+ return -ENOMEM;
+
+- hw = raspberrypi_register_pllb(rpi);
+- if (IS_ERR(hw)) {
+- dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw));
+- return PTR_ERR(hw);
+- }
+-
+- hw = raspberrypi_register_pllb_arm(rpi);
+- if (IS_ERR(hw))
+- return PTR_ERR(hw);
+- clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw;
+- clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1;
++ ret = raspberrypi_discover_clocks(rpi, clk_data);
++ if (ret)
++ return ret;
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ clk_data);
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -160,6 +160,11 @@ enum rpi_firmware_property_tag {
+
+ #define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64
+
++struct rpi_firmware_get_clocks_response {
++ __le32 parent;
++ __le32 id;
++};
++
+ #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
+ int rpi_firmware_property(struct rpi_firmware *fw,
+ u32 tag, void *data, size_t len);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch b/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch
new file mode 100644
index 0000000000..098903d2e1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch
@@ -0,0 +1,39 @@
+From a0ebfa1829b5d3a1f698426c29f99078640b498f Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 23 Dec 2019 19:58:30 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Add firmware clocks node
+
+Now that we have a clock driver for the clocks exposed by the firmware,
+let's add the device tree nodes for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 2 +-
+ arch/arm/boot/dts/bcm2711.dtsi | 5 +++++
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -33,7 +33,7 @@
+
+ power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
+ resets = <&pm BCM2835_RESET_V3D>;
+- clocks = <&clocks BCM2835_CLOCK_V3D>;
++ clocks = <&firmware_clocks 5>;
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -31,6 +31,11 @@
+ };
+ };
+
++ firmware_clocks: firmware-clocks {
++ compatible = "raspberrypi,firmware-clocks";
++ raspberrypi,firmware = <&firmware>;
++ #clock-cells = <1>;
++ };
+
+ soc {
+ /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch b/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch
new file mode 100644
index 0000000000..3df4fde439
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch
@@ -0,0 +1,168 @@
+From e108e2c34b3acc70ec55b7d0772abb79c96319b2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 28 Jan 2020 09:33:52 +0100
+Subject: [PATCH] reset: Move reset-simple header out of drivers/reset
+
+The reset-simple code can be useful for drivers outside of drivers/reset
+that have a few reset controls as part of their features. Let's move it to
+include/linux/reset.
+
+Cc: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/reset/reset-simple.c | 3 +--
+ drivers/reset/reset-socfpga.c | 3 +--
+ drivers/reset/reset-sunxi.c | 3 +--
+ drivers/reset/reset-uniphier-glue.c | 3 +--
+ {drivers => include/linux}/reset/reset-simple.h | 0
+ 5 files changed, 4 insertions(+), 8 deletions(-)
+ rename {drivers => include/linux}/reset/reset-simple.h (100%)
+
+--- a/drivers/reset/reset-simple.c
++++ b/drivers/reset/reset-simple.c
+@@ -18,10 +18,9 @@
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset-controller.h>
++#include <linux/reset/reset-simple.h>
+ #include <linux/spinlock.h>
+
+-#include "reset-simple.h"
+-
+ static inline struct reset_simple_data *
+ to_reset_simple_data(struct reset_controller_dev *rcdev)
+ {
+--- a/drivers/reset/reset-socfpga.c
++++ b/drivers/reset/reset-socfpga.c
+@@ -11,13 +11,12 @@
+ #include <linux/of_address.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset-controller.h>
++#include <linux/reset/reset-simple.h>
+ #include <linux/reset/socfpga.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/types.h>
+
+-#include "reset-simple.h"
+-
+ #define SOCFPGA_NR_BANKS 8
+
+ static int a10_reset_init(struct device_node *np)
+--- a/drivers/reset/reset-sunxi.c
++++ b/drivers/reset/reset-sunxi.c
+@@ -14,13 +14,12 @@
+ #include <linux/of_address.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset-controller.h>
++#include <linux/reset/reset-simple.h>
+ #include <linux/reset/sunxi.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/types.h>
+
+-#include "reset-simple.h"
+-
+ static int sunxi_reset_init(struct device_node *np)
+ {
+ struct reset_simple_data *data;
+--- a/drivers/reset/reset-uniphier-glue.c
++++ b/drivers/reset/reset-uniphier-glue.c
+@@ -9,8 +9,7 @@
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset.h>
+-
+-#include "reset-simple.h"
++#include <linux/reset/reset-simple.h>
+
+ #define MAX_CLKS 2
+ #define MAX_RSTS 2
+--- a/drivers/reset/reset-simple.h
++++ /dev/null
+@@ -1,41 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- * Simple Reset Controller ops
+- *
+- * Based on Allwinner SoCs Reset Controller driver
+- *
+- * Copyright 2013 Maxime Ripard
+- *
+- * Maxime Ripard <maxime.ripard@free-electrons.com>
+- */
+-
+-#ifndef __RESET_SIMPLE_H__
+-#define __RESET_SIMPLE_H__
+-
+-#include <linux/io.h>
+-#include <linux/reset-controller.h>
+-#include <linux/spinlock.h>
+-
+-/**
+- * struct reset_simple_data - driver data for simple reset controllers
+- * @lock: spinlock to protect registers during read-modify-write cycles
+- * @membase: memory mapped I/O register range
+- * @rcdev: reset controller device base structure
+- * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits
+- * are set to assert the reset. Note that this says nothing about
+- * the voltage level of the actual reset line.
+- * @status_active_low: if true, bits read back as cleared while the reset is
+- * asserted. Otherwise, bits read back as set while the
+- * reset is asserted.
+- */
+-struct reset_simple_data {
+- spinlock_t lock;
+- void __iomem *membase;
+- struct reset_controller_dev rcdev;
+- bool active_low;
+- bool status_active_low;
+-};
+-
+-extern const struct reset_control_ops reset_simple_ops;
+-
+-#endif /* __RESET_SIMPLE_H__ */
+--- /dev/null
++++ b/include/linux/reset/reset-simple.h
+@@ -0,0 +1,41 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Simple Reset Controller ops
++ *
++ * Based on Allwinner SoCs Reset Controller driver
++ *
++ * Copyright 2013 Maxime Ripard
++ *
++ * Maxime Ripard <maxime.ripard@free-electrons.com>
++ */
++
++#ifndef __RESET_SIMPLE_H__
++#define __RESET_SIMPLE_H__
++
++#include <linux/io.h>
++#include <linux/reset-controller.h>
++#include <linux/spinlock.h>
++
++/**
++ * struct reset_simple_data - driver data for simple reset controllers
++ * @lock: spinlock to protect registers during read-modify-write cycles
++ * @membase: memory mapped I/O register range
++ * @rcdev: reset controller device base structure
++ * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits
++ * are set to assert the reset. Note that this says nothing about
++ * the voltage level of the actual reset line.
++ * @status_active_low: if true, bits read back as cleared while the reset is
++ * asserted. Otherwise, bits read back as set while the
++ * reset is asserted.
++ */
++struct reset_simple_data {
++ spinlock_t lock;
++ void __iomem *membase;
++ struct reset_controller_dev rcdev;
++ bool active_low;
++ bool status_active_low;
++};
++
++extern const struct reset_control_ops reset_simple_ops;
++
++#endif /* __RESET_SIMPLE_H__ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch
new file mode 100644
index 0000000000..035c9d6aa0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch
@@ -0,0 +1,85 @@
+From 66deff85fee24ecd7b0ffa2901711aa8f026fcfa Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 28 Jan 2020 16:22:20 +0100
+Subject: [PATCH] reset: simple: Add reset callback
+
+The reset-simple code lacks a reset callback that is still pretty easy to
+implement. The only real thing to consider is the delay needed for a device
+to be reset, so let's expose that as part of the reset-simple driver data.
+
+Cc: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/reset/reset-simple.c | 24 ++++++++++++++++++++++++
+ include/linux/reset/reset-simple.h | 6 ++++++
+ 2 files changed, 30 insertions(+)
+
+--- a/drivers/reset/reset-simple.c
++++ b/drivers/reset/reset-simple.c
+@@ -11,6 +11,7 @@
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
++#include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/err.h>
+ #include <linux/io.h>
+@@ -63,6 +64,28 @@ static int reset_simple_deassert(struct
+ return reset_simple_update(rcdev, id, false);
+ }
+
++static int reset_simple_reset(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ struct reset_simple_data *data = to_reset_simple_data(rcdev);
++ int ret;
++
++ if (!data->reset_us)
++ return -ENOTSUPP;
++
++ ret = reset_simple_assert(rcdev, id);
++ if (ret)
++ return ret;
++
++ usleep_range(data->reset_us, data->reset_us * 2);
++
++ ret = reset_simple_deassert(rcdev, id);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
+ static int reset_simple_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+ {
+@@ -80,6 +103,7 @@ static int reset_simple_status(struct re
+ const struct reset_control_ops reset_simple_ops = {
+ .assert = reset_simple_assert,
+ .deassert = reset_simple_deassert,
++ .reset = reset_simple_reset,
+ .status = reset_simple_status,
+ };
+ EXPORT_SYMBOL_GPL(reset_simple_ops);
+--- a/include/linux/reset/reset-simple.h
++++ b/include/linux/reset/reset-simple.h
+@@ -27,6 +27,11 @@
+ * @status_active_low: if true, bits read back as cleared while the reset is
+ * asserted. Otherwise, bits read back as set while the
+ * reset is asserted.
++ * @reset_us: Minimum delay in microseconds needed that needs to be
++ * waited for between an assert and a deassert to reset the
++ * device. If multiple consumers with different delay
++ * requirements are connected to this controller, it must
++ * be the largest minimum delay.
+ */
+ struct reset_simple_data {
+ spinlock_t lock;
+@@ -34,6 +39,7 @@ struct reset_simple_data {
+ struct reset_controller_dev rcdev;
+ bool active_low;
+ bool status_active_low;
++ unsigned int reset_us;
+ };
+
+ extern const struct reset_control_ops reset_simple_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch b/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch
new file mode 100644
index 0000000000..ca87cbac83
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch
@@ -0,0 +1,68 @@
+From 67405a5468f8972f3a3db44292aff8fc05188db9 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 17:50:31 +0100
+Subject: [PATCH] dt-bindings: clock: Add BCM2711 DVP binding
+
+The BCM2711 has a unit controlling the HDMI0 and HDMI1 clock and reset
+signals. Let's add a binding for it.
+
+Cc: Philipp Zabel <p.zabel@pengutronix.de>
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/clock/brcm,bcm2711-dvp.yaml | 47 +++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/brcm,bcm2711-dvp.yaml
+@@ -0,0 +1,47 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/clock/brcm,bcm2711-dvp.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom BCM2711 HDMI DVP Device Tree Bindings
++
++maintainers:
++ - Maxime Ripard <mripard@kernel.org>
++
++properties:
++ "#clock-cells":
++ const: 1
++
++ "#reset-cells":
++ const: 1
++
++ compatible:
++ const: brcm,brcm2711-dvp
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++required:
++ - "#clock-cells"
++ - "#reset-cells"
++ - compatible
++ - reg
++ - clocks
++
++additionalProperties: false
++
++examples:
++ - |
++ dvp: clock@7ef00000 {
++ compatible = "brcm,brcm2711-dvp";
++ reg = <0x7ef00000 0x10>;
++ clocks = <&clk_108MHz>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++...
diff --git a/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch b/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch
new file mode 100644
index 0000000000..23105057a9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch
@@ -0,0 +1,172 @@
+From 0a2b9668e391b5fef4c54f992d7f8f99e5f50ef3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 28 Jan 2020 09:36:27 +0100
+Subject: [PATCH] clk: bcm: Add BCM2711 DVP driver
+
+The HDMI block has a block that controls clocks and reset signals to the
+HDMI0 and HDMI1 controllers.
+
+Let's expose that through a clock driver implementing a clock and reset
+provider.
+
+Cc: Michael Turquette <mturquette@baylibre.com>
+Cc: Stephen Boyd <sboyd@kernel.org>
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: linux-clk@vger.kernel.org
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/Kconfig | 1 +
+ drivers/clk/bcm/Makefile | 1 +
+ drivers/clk/bcm/clk-bcm2711-dvp.c | 125 ++++++++++++++++++++++++++++++
+ 3 files changed, 127 insertions(+)
+ create mode 100644 drivers/clk/bcm/clk-bcm2711-dvp.c
+
+--- a/drivers/clk/bcm/Kconfig
++++ b/drivers/clk/bcm/Kconfig
+@@ -4,6 +4,7 @@ config CLK_BCM2835
+ depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
+ depends on COMMON_CLK
+ default ARCH_BCM2835 || ARCH_BRCMSTB
++ select RESET_SIMPLE
+ help
+ Enable common clock framework support for Broadcom BCM2835
+ SoCs.
+--- a/drivers/clk/bcm/Makefile
++++ b/drivers/clk/bcm/Makefile
+@@ -6,6 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-s
+ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
+ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
+ obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
++obj-$(CONFIG_CLK_BCM2835) += clk-bcm2711-dvp.o
+ obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o
+ obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o
+ obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o
+--- /dev/null
++++ b/drivers/clk/bcm/clk-bcm2711-dvp.c
+@@ -0,0 +1,125 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++// Copyright 2020 Cerno
++
++#include <linux/clk-provider.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/reset-controller.h>
++#include <linux/reset/reset-simple.h>
++
++#define DVP_HT_RPI_SW_INIT 0x04
++#define DVP_HT_RPI_MISC_CONFIG 0x08
++
++#define NR_CLOCKS 2
++#define NR_RESETS 6
++
++struct clk_dvp {
++ struct clk_hw_onecell_data *data;
++ struct reset_simple_data reset;
++};
++
++static int clk_dvp_probe(struct platform_device *pdev)
++{
++ struct clk_hw_onecell_data *data;
++ struct resource *res;
++ struct clk_dvp *dvp;
++ void __iomem *base;
++ const char *parent;
++ int ret;
++
++ dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL);
++ if (!dvp)
++ return -ENOMEM;
++ platform_set_drvdata(pdev, dvp);
++
++ dvp->data = devm_kzalloc(&pdev->dev,
++ struct_size(dvp->data, hws, NR_CLOCKS),
++ GFP_KERNEL);
++ if (!dvp->data)
++ return -ENOMEM;
++ data = dvp->data;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(base))
++ return PTR_ERR(base);
++
++ dvp->reset.rcdev.owner = THIS_MODULE;
++ dvp->reset.rcdev.nr_resets = NR_RESETS;
++ dvp->reset.rcdev.ops = &reset_simple_ops;
++ dvp->reset.rcdev.of_node = pdev->dev.of_node;
++ dvp->reset.membase = base + DVP_HT_RPI_SW_INIT;
++ spin_lock_init(&dvp->reset.lock);
++
++ ret = reset_controller_register(&dvp->reset.rcdev);
++ if (ret)
++ return ret;
++
++ parent = of_clk_get_parent_name(pdev->dev.of_node, 0);
++ if (!parent)
++ goto unregister_reset;
++
++ data->hws[0] = clk_hw_register_gate(&pdev->dev, "hdmi0-108MHz",
++ parent, 0,
++ base + DVP_HT_RPI_MISC_CONFIG, 3,
++ CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock);
++ if (IS_ERR(data->hws[0])) {
++ ret = PTR_ERR(data->hws[0]);
++ goto unregister_reset;
++ }
++
++ data->hws[1] = clk_hw_register_gate(&pdev->dev, "hdmi1-108MHz",
++ parent, 0,
++ base + DVP_HT_RPI_MISC_CONFIG, 4,
++ CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock);
++ if (IS_ERR(data->hws[1])) {
++ ret = PTR_ERR(data->hws[1]);
++ goto unregister_clk0;
++ }
++
++ data->num = NR_CLOCKS;
++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
++ data);
++ if (ret)
++ goto unregister_clk1;
++
++ return 0;
++
++
++unregister_clk1:
++ clk_hw_unregister_gate(data->hws[1]);
++
++unregister_clk0:
++ clk_hw_unregister_gate(data->hws[0]);
++
++unregister_reset:
++ reset_controller_unregister(&dvp->reset.rcdev);
++ return ret;
++};
++
++static int clk_dvp_remove(struct platform_device *pdev)
++{
++ struct clk_dvp *dvp = platform_get_drvdata(pdev);
++ struct clk_hw_onecell_data *data = dvp->data;
++
++ clk_hw_unregister_gate(data->hws[1]);
++ clk_hw_unregister_gate(data->hws[0]);
++ reset_controller_unregister(&dvp->reset.rcdev);
++
++ return 0;
++}
++
++static const struct of_device_id clk_dvp_dt_ids[] = {
++ { .compatible = "brcm,brcm2711-dvp", },
++ { /* sentinel */ }
++};
++
++static struct platform_driver clk_dvp_driver = {
++ .probe = clk_dvp_probe,
++ .remove = clk_dvp_remove,
++ .driver = {
++ .name = "brcm2711-dvp",
++ .of_match_table = clk_dvp_dt_ids,
++ },
++};
++module_platform_driver(clk_dvp_driver);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch b/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch
new file mode 100644
index 0000000000..c4d230e730
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch
@@ -0,0 +1,43 @@
+From 119c9cdf9beab785d10ebf8a804ce20b3b0fd779 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 28 Jan 2020 09:37:06 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Add HDMI DVP
+
+Now that we have a driver for the DVP, let's add its DT node.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -31,6 +31,13 @@
+ };
+ };
+
++ clk_108MHz: clk-108M {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <108000000>;
++ clock-output-names = "108MHz-clock";
++ };
++
+ firmware_clocks: firmware-clocks {
+ compatible = "raspberrypi,firmware-clocks";
+ raspberrypi,firmware = <&firmware>;
+@@ -268,6 +275,14 @@
+ hvs@7e400000 {
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ };
++
++ dvp: clock@7ef00000 {
++ compatible = "brcm,brcm2711-dvp";
++ reg = <0x7ef00000 0x10>;
++ clocks = <&clk_108MHz>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
+ };
+
+ arm-pmu {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch b/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch
new file mode 100644
index 0000000000..3e47c475ff
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch
@@ -0,0 +1,706 @@
+From 193065956ba3e285df2c67f7c3bdeb3bdaae6ee9 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 15:42:05 +0100
+Subject: [PATCH] dt-bindings: display: Convert VC4 bindings to schemas
+
+The BCM283x SoCs have a display pipeline composed of several controllers
+with device tree bindings that are supported by Linux.
+
+Now that we have the DT validation in place, let's split into separate
+files and convert the device tree bindings for those controllers to
+schemas.
+
+This is just a 1:1 conversion though, and some bindings were incomplete so
+it results in example validation warnings that are going to be addressed in
+the following patches.
+
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/display/brcm,bcm-vc4.txt | 174 ------------------
+ .../bindings/display/brcm,bcm2835-dpi.yaml | 66 +++++++
+ .../bindings/display/brcm,bcm2835-dsi0.yaml | 73 ++++++++
+ .../bindings/display/brcm,bcm2835-hdmi.yaml | 75 ++++++++
+ .../bindings/display/brcm,bcm2835-hvs.yaml | 37 ++++
+ .../display/brcm,bcm2835-pixelvalve0.yaml | 40 ++++
+ .../bindings/display/brcm,bcm2835-txp.yaml | 37 ++++
+ .../bindings/display/brcm,bcm2835-v3d.yaml | 42 +++++
+ .../bindings/display/brcm,bcm2835-vc4.yaml | 34 ++++
+ .../bindings/display/brcm,bcm2835-vec.yaml | 44 +++++
+ MAINTAINERS | 2 +-
+ 11 files changed, 449 insertions(+), 175 deletions(-)
+ delete mode 100644 Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
+ create mode 100644 Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
++++ /dev/null
+@@ -1,174 +0,0 @@
+-Broadcom VC4 (VideoCore4) GPU
+-
+-The VC4 device present on the Raspberry Pi includes a display system
+-with HDMI output and the HVS (Hardware Video Scaler) for compositing
+-display planes.
+-
+-Required properties for VC4:
+-- compatible: Should be "brcm,bcm2835-vc4" or "brcm,cygnus-vc4"
+-
+-Required properties for Pixel Valve:
+-- compatible: Should be one of "brcm,bcm2835-pixelvalve0",
+- "brcm,bcm2835-pixelvalve1", or "brcm,bcm2835-pixelvalve2"
+-- reg: Physical base address and length of the PV's registers
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-
+-Required properties for HVS:
+-- compatible: Should be "brcm,bcm2835-hvs"
+-- reg: Physical base address and length of the HVS's registers
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-
+-Required properties for HDMI
+-- compatible: Should be "brcm,bcm2835-hdmi"
+-- reg: Physical base address and length of the two register ranges
+- ("HDMI" and "HD", in that order)
+-- interrupts: The interrupt numbers
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-- ddc: phandle of the I2C controller used for DDC EDID probing
+-- clocks: a) hdmi: The HDMI state machine clock
+- b) pixel: The pixel clock.
+-
+-Optional properties for HDMI:
+-- hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear
+- as an interrupt/status bit in the HDMI controller
+- itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt
+-- dmas: Should contain one entry pointing to the DMA channel used to
+- transfer audio data
+-- dma-names: Should contain "audio-rx"
+-
+-Required properties for DPI:
+-- compatible: Should be "brcm,bcm2835-dpi"
+-- reg: Physical base address and length of the registers
+-- clocks: a) core: The core clock the unit runs on
+- b) pixel: The pixel clock that feeds the pixelvalve
+-- port: Port node with a single endpoint connecting to the panel
+- device, as defined in [1]
+-
+-Required properties for VEC:
+-- compatible: Should be "brcm,bcm2835-vec"
+-- reg: Physical base address and length of the registers
+-- clocks: The core clock the unit runs on
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-
+-Required properties for V3D:
+-- compatible: Should be "brcm,bcm2835-v3d" or "brcm,cygnus-v3d"
+-- reg: Physical base address and length of the V3D's registers
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-
+-Optional properties for V3D:
+-- clocks: The clock the unit runs on
+-
+-Required properties for DSI:
+-- compatible: Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1"
+-- reg: Physical base address and length of the DSI block's registers
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-- clocks: a) phy: The DSI PLL clock feeding the DSI analog PHY
+- b) escape: The DSI ESC clock from CPRMAN
+- c) pixel: The DSI pixel clock from CPRMAN
+-- clock-output-names:
+- The 3 clocks output from the DSI analog PHY: dsi[01]_byte,
+- dsi[01]_ddr2, and dsi[01]_ddr
+-
+-Required properties for the TXP (writeback) block:
+-- compatible: Should be "brcm,bcm2835-txp"
+-- reg: Physical base address and length of the TXP block's registers
+-- interrupts: The interrupt number
+- See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+-
+-[1] Documentation/devicetree/bindings/media/video-interfaces.txt
+-
+-Example:
+-pixelvalve@7e807000 {
+- compatible = "brcm,bcm2835-pixelvalve2";
+- reg = <0x7e807000 0x100>;
+- interrupts = <2 10>; /* pixelvalve */
+-};
+-
+-hvs@7e400000 {
+- compatible = "brcm,bcm2835-hvs";
+- reg = <0x7e400000 0x6000>;
+- interrupts = <2 1>;
+-};
+-
+-hdmi: hdmi@7e902000 {
+- compatible = "brcm,bcm2835-hdmi";
+- reg = <0x7e902000 0x600>,
+- <0x7e808000 0x100>;
+- interrupts = <2 8>, <2 9>;
+- ddc = <&i2c2>;
+- hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
+- clocks = <&clocks BCM2835_PLLH_PIX>,
+- <&clocks BCM2835_CLOCK_HSM>;
+- clock-names = "pixel", "hdmi";
+-};
+-
+-dpi: dpi@7e208000 {
+- compatible = "brcm,bcm2835-dpi";
+- reg = <0x7e208000 0x8c>;
+- clocks = <&clocks BCM2835_CLOCK_VPU>,
+- <&clocks BCM2835_CLOCK_DPI>;
+- clock-names = "core", "pixel";
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- port {
+- dpi_out: endpoint@0 {
+- remote-endpoint = <&panel_in>;
+- };
+- };
+-};
+-
+-dsi1: dsi@7e700000 {
+- compatible = "brcm,bcm2835-dsi1";
+- reg = <0x7e700000 0x8c>;
+- interrupts = <2 12>;
+- #address-cells = <1>;
+- #size-cells = <0>;
+- #clock-cells = <1>;
+-
+- clocks = <&clocks BCM2835_PLLD_DSI1>,
+- <&clocks BCM2835_CLOCK_DSI1E>,
+- <&clocks BCM2835_CLOCK_DSI1P>;
+- clock-names = "phy", "escape", "pixel";
+-
+- clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr";
+-
+- pitouchscreen: panel@0 {
+- compatible = "raspberrypi,touchscreen";
+- reg = <0>;
+-
+- <...>
+- };
+-};
+-
+-vec: vec@7e806000 {
+- compatible = "brcm,bcm2835-vec";
+- reg = <0x7e806000 0x1000>;
+- clocks = <&clocks BCM2835_CLOCK_VEC>;
+- interrupts = <2 27>;
+-};
+-
+-v3d: v3d@7ec00000 {
+- compatible = "brcm,bcm2835-v3d";
+- reg = <0x7ec00000 0x1000>;
+- interrupts = <1 10>;
+-};
+-
+-vc4: gpu {
+- compatible = "brcm,bcm2835-vc4";
+-};
+-
+-panel: panel {
+- compatible = "ontat,yx700wv03", "simple-panel";
+-
+- port {
+- panel_in: endpoint {
+- remote-endpoint = <&dpi_out>;
+- };
+- };
+-};
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml
+@@ -0,0 +1,66 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-dpi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) DPI Controller
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ const: brcm,bcm2835-dpi
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ items:
++ - description: The core clock the unit runs on
++ - description: The pixel clock that feeds the pixelvalve
++
++ port:
++ type: object
++ description: >
++ Port node with a single endpoint connecting to the panel, as
++ defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/bcm2835.h>
++
++ panel: panel {
++ compatible = "ontat,yx700wv03", "simple-panel";
++
++ port {
++ panel_in: endpoint {
++ remote-endpoint = <&dpi_out>;
++ };
++ };
++ };
++
++ dpi: dpi@7e208000 {
++ compatible = "brcm,bcm2835-dpi";
++ reg = <0x7e208000 0x8c>;
++ clocks = <&clocks BCM2835_CLOCK_VPU>,
++ <&clocks BCM2835_CLOCK_DPI>;
++ clock-names = "core", "pixel";
++
++ port {
++ dpi_out: endpoint {
++ remote-endpoint = <&panel_in>;
++ };
++ };
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml
+@@ -0,0 +1,73 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-dsi0.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) DSI Controller
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ enum:
++ - brcm,bcm2835-dsi0
++ - brcm,bcm2835-dsi1
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ items:
++ - description: The DSI PLL clock feeding the DSI analog PHY
++ - description: The DSI ESC clock
++ - description: The DSI pixel clock
++
++ clock-output-names: true
++ # FIXME: The meta-schemas don't seem to allow it for now
++ # items:
++ # - description: The DSI byte clock for the PHY
++ # - description: The DSI DDR2 clock
++ # - description: The DSI DDR clock
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - clock-output-names
++ - interrupts
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/bcm2835.h>
++
++ dsi1: dsi@7e700000 {
++ compatible = "brcm,bcm2835-dsi1";
++ reg = <0x7e700000 0x8c>;
++ interrupts = <2 12>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ #clock-cells = <1>;
++
++ clocks = <&clocks BCM2835_PLLD_DSI1>,
++ <&clocks BCM2835_CLOCK_DSI1E>,
++ <&clocks BCM2835_CLOCK_DSI1P>;
++ clock-names = "phy", "escape", "pixel";
++
++ clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr";
++
++ pitouchscreen: panel@0 {
++ compatible = "raspberrypi,touchscreen";
++ reg = <0>;
++
++ /* ... */
++ };
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
+@@ -0,0 +1,75 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-hdmi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) HDMI Controller
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ const: brcm,bcm2835-hdmi
++
++ reg:
++ items:
++ - description: HDMI register range
++ - description: HD register range
++
++ interrupts:
++ minItems: 2
++
++ clocks:
++ items:
++ - description: The HDMI state machine clock
++ - description: The pixel clock
++
++ ddc:
++ allOf:
++ - $ref: /schemas/types.yaml#/definitions/phandle
++ description: >
++ Phandle of the I2C controller used for DDC EDID probing
++
++ hpd-gpios:
++ description: >
++ The GPIO pin for the HDMI hotplug detect (if it doesn't appear
++ as an interrupt/status bit in the HDMI controller itself)
++
++ dmas:
++ maxItems: 1
++ description: >
++ Should contain one entry pointing to the DMA channel used to
++ transfer audio data.
++
++ dma-names:
++ const: audio-rx
++
++required:
++ - compatible
++ - reg
++ - interrupts
++ - clocks
++ - ddc
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/bcm2835.h>
++ #include <dt-bindings/gpio/gpio.h>
++
++ hdmi: hdmi@7e902000 {
++ compatible = "brcm,bcm2835-hdmi";
++ reg = <0x7e902000 0x600>,
++ <0x7e808000 0x100>;
++ interrupts = <2 8>, <2 9>;
++ ddc = <&i2c2>;
++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
++ clocks = <&clocks BCM2835_PLLH_PIX>,
++ <&clocks BCM2835_CLOCK_HSM>;
++ clock-names = "pixel", "hdmi";
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
+@@ -0,0 +1,37 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-hvs.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) Hardware Video Scaler
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ const: brcm,bcm2835-hvs
++
++ reg:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - interrupts
++
++additionalProperties: false
++
++examples:
++ - |
++ hvs@7e400000 {
++ compatible = "brcm,bcm2835-hvs";
++ reg = <0x7e400000 0x6000>;
++ interrupts = <2 1>;
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
+@@ -0,0 +1,40 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-pixelvalve0.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) PixelValve
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ enum:
++ - brcm,bcm2835-pixelvalve0
++ - brcm,bcm2835-pixelvalve1
++ - brcm,bcm2835-pixelvalve2
++
++ reg:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - interrupts
++
++additionalProperties: false
++
++examples:
++ - |
++ pixelvalve@7e807000 {
++ compatible = "brcm,bcm2835-pixelvalve2";
++ reg = <0x7e807000 0x100>;
++ interrupts = <2 10>; /* pixelvalve */
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+@@ -0,0 +1,37 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-txp.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) TXP (writeback) Controller
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ const: brcm,bcm2835-txp
++
++ reg:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - interrupts
++
++additionalProperties: false
++
++examples:
++ - |
++ txp: txp@7e004000 {
++ compatible = "brcm,bcm2835-txp";
++ reg = <0x7e004000 0x20>;
++ interrupts = <1 11>;
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml
+@@ -0,0 +1,42 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-v3d.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) V3D GPU
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ enum:
++ - brcm,bcm2835-v3d
++ - brcm,cygnus-v3d
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - interrupts
++
++additionalProperties: false
++
++examples:
++ - |
++ v3d: v3d@7ec00000 {
++ compatible = "brcm,bcm2835-v3d";
++ reg = <0x7ec00000 0x1000>;
++ interrupts = <1 10>;
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
+@@ -0,0 +1,34 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-vc4.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) GPU
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++description: >
++ The VC4 device present on the Raspberry Pi includes a display system
++ with HDMI output and the HVS (Hardware Video Scaler) for compositing
++ display planes.
++
++properties:
++ compatible:
++ enum:
++ - brcm,bcm2835-vc4
++ - brcm,cygnus-vc4
++
++required:
++ - compatible
++
++additionalProperties: false
++
++examples:
++ - |
++ vc4: gpu {
++ compatible = "brcm,bcm2835-vc4";
++ };
++
++...
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vec.yaml
+@@ -0,0 +1,44 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/brcm,bcm2835-vec.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Broadcom VC4 (VideoCore4) VEC
++
++maintainers:
++ - Eric Anholt <eric@anholt.net>
++
++properties:
++ compatible:
++ const: brcm,bcm2835-vec
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ interrupts:
++ maxItems: 1
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - interrupts
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/bcm2835.h>
++
++ vec: vec@7e806000 {
++ compatible = "brcm,bcm2835-vec";
++ reg = <0x7e806000 0x1000>;
++ clocks = <&clocks BCM2835_CLOCK_VEC>;
++ interrupts = <2 27>;
++ };
++
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -5573,7 +5573,7 @@ T: git git://github.com/anholt/linux
+ S: Supported
+ F: drivers/gpu/drm/vc4/
+ F: include/uapi/drm/vc4_drm.h
+-F: Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
++F: Documentation/devicetree/bindings/display/brcm,bcm2835-*.yaml
+ T: git git://anongit.freedesktop.org/drm/drm-misc
+
+ DRM DRIVERS FOR VIVANTE GPU IP
diff --git a/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch b/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch
new file mode 100644
index 0000000000..2150f0abec
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch
@@ -0,0 +1,38 @@
+From 9a624b11291306b4b4c49c70bc75ef7d72d81405 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 15:47:18 +0100
+Subject: [PATCH] dt-bindings: display: vc4: dpi: Add missing
+ clock-names property
+
+While the device tree and the driver expected a clock-names property, it
+wasn't explicitly documented in the previous binding. Make sure it is now.
+
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-dpi.yaml | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dpi.yaml
+@@ -21,6 +21,11 @@ properties:
+ - description: The core clock the unit runs on
+ - description: The pixel clock that feeds the pixelvalve
+
++ clock-names:
++ items:
++ - const: core
++ - const: pixel
++
+ port:
+ type: object
+ description: >
+@@ -31,6 +36,7 @@ required:
+ - compatible
+ - reg
+ - clocks
++ - clock-names
+ - port
+
+ additionalProperties: false
diff --git a/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch b/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch
new file mode 100644
index 0000000000..4cb313f152
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch
@@ -0,0 +1,54 @@
+From 12abb6775e99482ff9fc71e16292ccf4dfeacee5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 15:47:18 +0100
+Subject: [PATCH] dt-bindings: display: vc4: dsi: Add missing clock
+ properties
+
+While the device tree and the driver expected a clock-names and a
+clock-cells properties, it wasn't explicitly documented in the previous
+binding. Make sure it is now.
+
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/display/brcm,bcm2835-dsi0.yaml | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-dsi0.yaml
+@@ -10,6 +10,9 @@ maintainers:
+ - Eric Anholt <eric@anholt.net>
+
+ properties:
++ "#clock-cells":
++ const: 1
++
+ compatible:
+ enum:
+ - brcm,bcm2835-dsi0
+@@ -24,6 +27,12 @@ properties:
+ - description: The DSI ESC clock
+ - description: The DSI pixel clock
+
++ clock-names:
++ items:
++ - const: phy
++ - const: escape
++ - const: pixel
++
+ clock-output-names: true
+ # FIXME: The meta-schemas don't seem to allow it for now
+ # items:
+@@ -35,9 +44,11 @@ properties:
+ maxItems: 1
+
+ required:
++ - "#clock-cells"
+ - compatible
+ - reg
+ - clocks
++ - clock-names
+ - clock-output-names
+ - interrupts
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch b/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch
new file mode 100644
index 0000000000..5ef4eaeab9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch
@@ -0,0 +1,34 @@
+From 293db446089f00599f0a22b933a6a5a13ccfc5e2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 15:47:18 +0100
+Subject: [PATCH] dt-bindings: display: vc4: hdmi: Add missing
+ clock-names property
+
+While the device tree and the driver expected a clock-names property, it
+wasn't explicitly documented in the previous binding. The documented order
+was wrong too, so make sure clock-names is there and in the proper order.
+
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-hdmi.yaml | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
+@@ -23,8 +23,13 @@ properties:
+
+ clocks:
+ items:
+- - description: The HDMI state machine clock
+ - description: The pixel clock
++ - description: The HDMI state machine clock
++
++ clock-names:
++ items:
++ - const: pixel
++ - const: hdmi
+
+ ddc:
+ allOf:
diff --git a/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch b/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch
new file mode 100644
index 0000000000..2499dee0c9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch
@@ -0,0 +1,24 @@
+From a12c5df87364ef6965a750345b1e28a5aba5cb14 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 17:40:56 +0100
+Subject: [PATCH] dt-bindings: display: vc4: Document BCM2711 VC5
+
+The BCM2711 comes with a new VideoCore. Add a compatible for it.
+
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
+@@ -17,6 +17,7 @@ description: >
+ properties:
+ compatible:
+ enum:
++ - brcm,bcm2711-vc5
+ - brcm,bcm2835-vc4
+ - brcm,cygnus-vc4
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch b/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch
new file mode 100644
index 0000000000..16f8b0ad82
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch
@@ -0,0 +1,30 @@
+From 42566c11972c9edace45d5a787e276588214cb79 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 19 Dec 2019 18:08:48 +0100
+Subject: [PATCH] drm/vc4: drv: Add include guards
+
+vc4_drv.h doesn't have any include guards which prevents it from being
+included twice. Let's add them.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -2,6 +2,8 @@
+ /*
+ * Copyright (C) 2015 Broadcom
+ */
++#ifndef _VC4_DRV_H_
++#define _VC4_DRV_H_
+
+ #include <linux/delay.h>
+ #include <linux/refcount.h>
+@@ -899,3 +901,5 @@ int vc4_perfmon_destroy_ioctl(struct drm
+ struct drm_file *file_priv);
+ int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
++
++#endif /* _VC4_DRV_H_ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch b/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch
new file mode 100644
index 0000000000..00bac3a2b9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch
@@ -0,0 +1,109 @@
+From d52f29a5e0ee9882f6f734c057224686b9820152 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 15:40:34 +0100
+Subject: [PATCH] drm/vc4: drv: Support BCM2711
+
+The BCM2711 has a reworked display pipeline, and the load tracker needs
+some adjustement to operate properly. Let's add a compatible for BCM2711
+and disable the load tracker until properly supported.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 1 +
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 +++
+ drivers/gpu/drm/vc4/vc4_kms.c | 32 +++++++++++++++++++++-----------
+ drivers/gpu/drm/vc4/vc4_plane.c | 5 +++++
+ 4 files changed, 30 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -368,6 +368,7 @@ static int vc4_platform_drm_remove(struc
+ }
+
+ static const struct of_device_id vc4_of_match[] = {
++ { .compatible = "brcm,bcm2711-vc5", },
+ { .compatible = "brcm,bcm2835-vc4", },
+ { .compatible = "brcm,cygnus-vc4", },
+ {},
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -205,6 +205,9 @@ struct vc4_dev {
+
+ int power_refcount;
+
++ /* Set to true when the load tracker is supported. */
++ bool load_tracker_available;
++
+ /* Set to true when the load tracker is active. */
+ bool load_tracker_enabled;
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -421,6 +421,9 @@ static int vc4_load_tracker_atomic_check
+ struct drm_plane *plane;
+ int i;
+
++ if (!vc4->load_tracker_available)
++ return 0;
++
+ priv_state = drm_atomic_get_private_obj_state(state,
+ &vc4->load_tracker);
+ if (IS_ERR(priv_state))
+@@ -520,10 +523,14 @@ int vc4_kms_load(struct drm_device *dev)
+ struct vc4_load_tracker_state *load_state;
+ int ret;
+
+- /* Start with the load tracker enabled. Can be disabled through the
+- * debugfs load_tracker file.
+- */
+- vc4->load_tracker_enabled = true;
++ if (!of_device_is_compatible(dev->dev->of_node, "brcm,bcm2711-vc5")) {
++ vc4->load_tracker_available = true;
++
++ /* Start with the load tracker enabled. Can be
++ * disabled through the debugfs load_tracker file.
++ */
++ vc4->load_tracker_enabled = true;
++ }
+
+ sema_init(&vc4->async_modeset, 1);
+
+@@ -560,14 +567,17 @@ int vc4_kms_load(struct drm_device *dev)
+ drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base,
+ &vc4_ctm_state_funcs);
+
+- load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
+- if (!load_state) {
+- drm_atomic_private_obj_fini(&vc4->ctm_manager);
+- return -ENOMEM;
+- }
++ if (vc4->load_tracker_available) {
++ load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
++ if (!load_state) {
++ drm_atomic_private_obj_fini(&vc4->ctm_manager);
++ return -ENOMEM;
++ }
+
+- drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base,
+- &vc4_load_tracker_state_funcs);
++ drm_atomic_private_obj_init(dev, &vc4->load_tracker,
++ &load_state->base,
++ &vc4_load_tracker_state_funcs);
++ }
+
+ drm_mode_config_reset(dev);
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -492,6 +492,11 @@ static void vc4_plane_calc_load(struct d
+ struct vc4_plane_state *vc4_state;
+ struct drm_crtc_state *crtc_state;
+ unsigned int vscale_factor;
++ struct vc4_dev *vc4;
++
++ vc4 = to_vc4_dev(state->plane->dev);
++ if (!vc4->load_tracker_available)
++ return;
+
+ vc4_state = to_vc4_plane_state(state);
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch b/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch
new file mode 100644
index 0000000000..0f6259e347
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch
@@ -0,0 +1,497 @@
+From 354d70a82947041b3d7b87f69641a6741febfc95 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 8 Aug 2019 17:51:07 +0100
+Subject: [PATCH] drm/vc4: drv: Add support for the BCM2711 HVS5
+
+The HVS found in the BCM2711 is slightly different from the previous
+generations.
+
+Most notably, the display list layout changes a bit, the LBM doesn't have
+the same size and the formats ordering for some formats is swapped.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 24 +++-
+ drivers/gpu/drm/vc4/vc4_drv.h | 4 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 17 ++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 194 +++++++++++++++++++++++---------
+ drivers/gpu/drm/vc4/vc4_regs.h | 67 +++++++++++
+ 5 files changed, 247 insertions(+), 59 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -550,6 +550,7 @@ static void vc4_crtc_atomic_enable(struc
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
++ u32 dispctrl;
+
+ require_hvs_enabled(dev);
+
+@@ -564,11 +565,24 @@ static void vc4_crtc_atomic_enable(struc
+ * When feeding the transposer, we should operate in oneshot
+ * mode.
+ */
+- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
+- VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
+- VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
+- SCALER_DISPCTRLX_ENABLE |
+- (vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0));
++ dispctrl = SCALER_DISPCTRLX_ENABLE;
++
++ if (!vc4->hvs->hvs5)
++ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
++ SCALER_DISPCTRLX_WIDTH) |
++ VC4_SET_FIELD(mode->vdisplay,
++ SCALER_DISPCTRLX_HEIGHT) |
++ (vc4_state->feed_txp ?
++ SCALER_DISPCTRLX_ONESHOT : 0);
++ else
++ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
++ SCALER5_DISPCTRLX_WIDTH) |
++ VC4_SET_FIELD(mode->vdisplay,
++ SCALER5_DISPCTRLX_HEIGHT) |
++ (vc4_state->feed_txp ?
++ SCALER5_DISPCTRLX_ONESHOT : 0);
++
++ HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl);
+
+ /* When feeding the transposer block the pixelvalve is unneeded and
+ * should not be enabled.
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -336,7 +336,11 @@ struct vc4_hvs {
+ spinlock_t mm_lock;
+
+ struct drm_mm_node mitchell_netravali_filter;
++
+ struct debugfs_regset32 regset;
++
++ /* HVS version 5 flag, therefore requires updated dlist structures */
++ bool hvs5;
+ };
+
+ struct vc4_plane {
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -223,6 +223,7 @@ static int vc4_hvs_bind(struct device *d
+ struct vc4_hvs *hvs = NULL;
+ int ret;
+ u32 dispctrl;
++ unsigned int hvs_version;
+
+ hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
+ if (!hvs)
+@@ -238,7 +239,14 @@ static int vc4_hvs_bind(struct device *d
+ hvs->regset.regs = hvs_regs;
+ hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+
+- hvs->dlist = hvs->regs + SCALER_DLIST_START;
++ hvs_version = readl(hvs->regs + SCALER_DISPLSTAT) >> 24;
++ if (hvs_version >= 0x40)
++ hvs->hvs5 = true;
++
++ if (!hvs->hvs5)
++ hvs->dlist = hvs->regs + SCALER_DLIST_START;
++ else
++ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+
+ spin_lock_init(&hvs->mm_lock);
+
+@@ -256,7 +264,12 @@ static int vc4_hvs_bind(struct device *d
+ * between planes when they don't overlap on the screen, but
+ * for now we just allocate globally.
+ */
+- drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024);
++ if (!hvs->hvs5)
++ /* 96kB */
++ drm_mm_init(&hvs->lbm_mm, 0, 96 * 1024);
++ else
++ /* 70k words */
++ drm_mm_init(&hvs->lbm_mm, 0, 70 * 2 * 1024);
+
+ /* Upload filter kernels. We only have the one for now, so we
+ * keep it around for the lifetime of the driver.
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -32,45 +32,60 @@ static const struct hvs_format {
+ u32 drm; /* DRM_FORMAT_* */
+ u32 hvs; /* HVS_FORMAT_* */
+ u32 pixel_order;
++ u32 pixel_order_hvs5;
+ } hvs_formats[] = {
+ {
+- .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
++ .drm = DRM_FORMAT_XRGB8888,
++ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ },
+ {
+- .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
++ .drm = DRM_FORMAT_ARGB8888,
++ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ },
+ {
+- .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
++ .drm = DRM_FORMAT_ABGR8888,
++ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .pixel_order = HVS_PIXEL_ORDER_ARGB,
++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+- .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
++ .drm = DRM_FORMAT_XBGR8888,
++ .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+ .pixel_order = HVS_PIXEL_ORDER_ARGB,
++ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+- .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
++ .drm = DRM_FORMAT_RGB565,
++ .hvs = HVS_PIXEL_FORMAT_RGB565,
+ .pixel_order = HVS_PIXEL_ORDER_XRGB,
+ },
+ {
+- .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
++ .drm = DRM_FORMAT_BGR565,
++ .hvs = HVS_PIXEL_FORMAT_RGB565,
+ .pixel_order = HVS_PIXEL_ORDER_XBGR,
+ },
+ {
+- .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
++ .drm = DRM_FORMAT_ARGB1555,
++ .hvs = HVS_PIXEL_FORMAT_RGBA5551,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+- .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
++ .drm = DRM_FORMAT_XRGB1555,
++ .hvs = HVS_PIXEL_FORMAT_RGBA5551,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+- .drm = DRM_FORMAT_RGB888, .hvs = HVS_PIXEL_FORMAT_RGB888,
++ .drm = DRM_FORMAT_RGB888,
++ .hvs = HVS_PIXEL_FORMAT_RGB888,
+ .pixel_order = HVS_PIXEL_ORDER_XRGB,
+ },
+ {
+- .drm = DRM_FORMAT_BGR888, .hvs = HVS_PIXEL_FORMAT_RGB888,
++ .drm = DRM_FORMAT_BGR888,
++ .hvs = HVS_PIXEL_FORMAT_RGB888,
+ .pixel_order = HVS_PIXEL_ORDER_XBGR,
+ },
+ {
+@@ -828,35 +843,6 @@ static int vc4_plane_mode_set(struct drm
+ return -EINVAL;
+ }
+
+- /* Control word */
+- vc4_dlist_write(vc4_state,
+- SCALER_CTL0_VALID |
+- (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
+- (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
+- VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
+- (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+- (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
+- VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
+- (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
+- VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
+- VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
+-
+- /* Position Word 0: Image Positions and Alpha Value */
+- vc4_state->pos0_offset = vc4_state->dlist_count;
+- vc4_dlist_write(vc4_state,
+- VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) |
+- VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
+- VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
+-
+- /* Position Word 1: Scaled Image Dimensions. */
+- if (!vc4_state->is_unity) {
+- vc4_dlist_write(vc4_state,
+- VC4_SET_FIELD(vc4_state->crtc_w,
+- SCALER_POS1_SCL_WIDTH) |
+- VC4_SET_FIELD(vc4_state->crtc_h,
+- SCALER_POS1_SCL_HEIGHT));
+- }
+-
+ /* 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.
+@@ -864,20 +850,120 @@ static int vc4_plane_mode_set(struct drm
+ mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
+ fb->format->has_alpha;
+
+- /* Position Word 2: Source Image Size, Alpha */
+- vc4_state->pos2_offset = vc4_state->dlist_count;
+- vc4_dlist_write(vc4_state,
+- VC4_SET_FIELD(fb->format->has_alpha ?
+- SCALER_POS2_ALPHA_MODE_PIPELINE :
+- SCALER_POS2_ALPHA_MODE_FIXED,
+- SCALER_POS2_ALPHA_MODE) |
+- (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
+- (fb->format->has_alpha ? SCALER_POS2_ALPHA_PREMULT : 0) |
+- VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) |
+- VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT));
++ if (!vc4->hvs->hvs5) {
++ /* Control word */
++ vc4_dlist_write(vc4_state,
++ SCALER_CTL0_VALID |
++ (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
++ (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
++ VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
++ (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
++ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
++ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
++ (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
++ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
++ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
++
++ /* Position Word 0: Image Positions and Alpha Value */
++ vc4_state->pos0_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) |
++ VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
++ VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
++
++ /* Position Word 1: Scaled Image Dimensions. */
++ if (!vc4_state->is_unity) {
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(vc4_state->crtc_w,
++ SCALER_POS1_SCL_WIDTH) |
++ VC4_SET_FIELD(vc4_state->crtc_h,
++ SCALER_POS1_SCL_HEIGHT));
++ }
++
++ /* Position Word 2: Source Image Size, Alpha */
++ vc4_state->pos2_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(fb->format->has_alpha ?
++ SCALER_POS2_ALPHA_MODE_PIPELINE :
++ SCALER_POS2_ALPHA_MODE_FIXED,
++ SCALER_POS2_ALPHA_MODE) |
++ (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
++ (fb->format->has_alpha ?
++ SCALER_POS2_ALPHA_PREMULT : 0) |
++ VC4_SET_FIELD(vc4_state->src_w[0],
++ SCALER_POS2_WIDTH) |
++ VC4_SET_FIELD(vc4_state->src_h[0],
++ SCALER_POS2_HEIGHT));
+
+- /* Position Word 3: Context. Written by the HVS. */
+- vc4_dlist_write(vc4_state, 0xc0c0c0c0);
++ /* Position Word 3: Context. Written by the HVS. */
++ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
++
++ } else {
++ u32 hvs_pixel_order = format->pixel_order;
++
++ if (format->pixel_order_hvs5)
++ hvs_pixel_order = format->pixel_order_hvs5;
++
++ /* Control word */
++ vc4_dlist_write(vc4_state,
++ SCALER_CTL0_VALID |
++ (hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) |
++ (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
++ VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
++ (vc4_state->is_unity ?
++ SCALER5_CTL0_UNITY : 0) |
++ VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
++ VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1) |
++ SCALER5_CTL0_ALPHA_EXPAND |
++ SCALER5_CTL0_RGB_EXPAND);
++
++ /* Position Word 0: Image Positions and Alpha Value */
++ vc4_state->pos0_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ (rotation & DRM_MODE_REFLECT_Y ?
++ SCALER5_POS0_VFLIP : 0) |
++ VC4_SET_FIELD(vc4_state->crtc_x,
++ SCALER_POS0_START_X) |
++ (rotation & DRM_MODE_REFLECT_X ?
++ SCALER5_POS0_HFLIP : 0) |
++ VC4_SET_FIELD(vc4_state->crtc_y,
++ SCALER5_POS0_START_Y)
++ );
++
++ /* Control Word 2 */
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(state->alpha >> 4,
++ SCALER5_CTL2_ALPHA) |
++ fb->format->has_alpha ?
++ SCALER5_CTL2_ALPHA_PREMULT : 0 |
++ (mix_plane_alpha ?
++ SCALER5_CTL2_ALPHA_MIX : 0) |
++ VC4_SET_FIELD(fb->format->has_alpha ?
++ SCALER5_CTL2_ALPHA_MODE_PIPELINE :
++ SCALER5_CTL2_ALPHA_MODE_FIXED,
++ SCALER5_CTL2_ALPHA_MODE)
++ );
++
++ /* Position Word 1: Scaled Image Dimensions. */
++ if (!vc4_state->is_unity) {
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(vc4_state->crtc_w,
++ SCALER_POS1_SCL_WIDTH) |
++ VC4_SET_FIELD(vc4_state->crtc_h,
++ SCALER_POS1_SCL_HEIGHT));
++ }
++
++ /* Position Word 2: Source Image Size */
++ vc4_state->pos2_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(vc4_state->src_w[0],
++ SCALER5_POS2_WIDTH) |
++ VC4_SET_FIELD(vc4_state->src_h[0],
++ SCALER5_POS2_HEIGHT));
++
++ /* Position Word 3: Context. Written by the HVS. */
++ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
++ }
+
+
+ /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
+@@ -1266,6 +1352,10 @@ static bool vc4_format_mod_supported(str
+ default:
+ return false;
+ }
++ case DRM_FORMAT_RGBX1010102:
++ case DRM_FORMAT_BGRX1010102:
++ case DRM_FORMAT_RGBA1010102:
++ case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YUV420:
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -328,6 +328,20 @@
+ # define SCALER_DISPCTRLX_HEIGHT_MASK VC4_MASK(11, 0)
+ # define SCALER_DISPCTRLX_HEIGHT_SHIFT 0
+
++# define SCALER5_DISPCTRLX_WIDTH_MASK VC4_MASK(28, 16)
++# define SCALER5_DISPCTRLX_WIDTH_SHIFT 16
++/* Generates a single frame when VSTART is seen and stops at the last
++ * pixel read from the FIFO.
++ */
++# define SCALER5_DISPCTRLX_ONESHOT BIT(15)
++/* Processes a single context in the dlist and then task switch,
++ * instead of an entire line.
++ */
++# define SCALER5_DISPCTRLX_ONECTX_MASK VC4_MASK(14, 13)
++# define SCALER5_DISPCTRLX_ONECTX_SHIFT 13
++# define SCALER5_DISPCTRLX_HEIGHT_MASK VC4_MASK(12, 0)
++# define SCALER5_DISPCTRLX_HEIGHT_SHIFT 0
++
+ #define SCALER_DISPBKGND0 0x00000044
+ # define SCALER_DISPBKGND_AUTOHS BIT(31)
+ # define SCALER_DISPBKGND_INTERLACE BIT(30)
+@@ -461,6 +475,8 @@
+ #define SCALER_DLIST_START 0x00002000
+ #define SCALER_DLIST_SIZE 0x00004000
+
++#define SCALER5_DLIST_START 0x00004000
++
+ #define VC4_HDMI_CORE_REV 0x000
+
+ #define VC4_HDMI_SW_RESET_CONTROL 0x004
+@@ -826,6 +842,8 @@ enum hvs_pixel_format {
+ HVS_PIXEL_FORMAT_PALETTE = 13,
+ HVS_PIXEL_FORMAT_YUV444_RGB = 14,
+ HVS_PIXEL_FORMAT_AYUV444_RGB = 15,
++ HVS_PIXEL_FORMAT_RGBA1010102 = 16,
++ HVS_PIXEL_FORMAT_YCBCR_10BIT = 17,
+ };
+
+ /* Note: the LSB is the rightmost character shown. Only valid for
+@@ -880,6 +898,10 @@ enum hvs_pixel_format {
+ #define SCALER_CTL0_RGBA_EXPAND_MSB 2
+ #define SCALER_CTL0_RGBA_EXPAND_ROUND 3
+
++#define SCALER5_CTL0_ALPHA_EXPAND BIT(12)
++
++#define SCALER5_CTL0_RGB_EXPAND BIT(11)
++
+ #define SCALER_CTL0_SCL1_MASK VC4_MASK(10, 8)
+ #define SCALER_CTL0_SCL1_SHIFT 8
+
+@@ -897,10 +919,13 @@ enum hvs_pixel_format {
+
+ /* Set to indicate no scaling. */
+ #define SCALER_CTL0_UNITY BIT(4)
++#define SCALER5_CTL0_UNITY BIT(15)
+
+ #define SCALER_CTL0_PIXEL_FORMAT_MASK VC4_MASK(3, 0)
+ #define SCALER_CTL0_PIXEL_FORMAT_SHIFT 0
+
++#define SCALER5_CTL0_PIXEL_FORMAT_MASK VC4_MASK(4, 0)
++
+ #define SCALER_POS0_FIXED_ALPHA_MASK VC4_MASK(31, 24)
+ #define SCALER_POS0_FIXED_ALPHA_SHIFT 24
+
+@@ -910,12 +935,48 @@ enum hvs_pixel_format {
+ #define SCALER_POS0_START_X_MASK VC4_MASK(11, 0)
+ #define SCALER_POS0_START_X_SHIFT 0
+
++#define SCALER5_POS0_START_Y_MASK VC4_MASK(27, 16)
++#define SCALER5_POS0_START_Y_SHIFT 16
++
++#define SCALER5_POS0_START_X_MASK VC4_MASK(13, 0)
++#define SCALER5_POS0_START_X_SHIFT 0
++
++#define SCALER5_POS0_VFLIP BIT(31)
++#define SCALER5_POS0_HFLIP BIT(15)
++
++#define SCALER5_CTL2_ALPHA_MODE_MASK VC4_MASK(31, 30)
++#define SCALER5_CTL2_ALPHA_MODE_SHIFT 30
++#define SCALER5_CTL2_ALPHA_MODE_PIPELINE 0
++#define SCALER5_CTL2_ALPHA_MODE_FIXED 1
++#define SCALER5_CTL2_ALPHA_MODE_FIXED_NONZERO 2
++#define SCALER5_CTL2_ALPHA_MODE_FIXED_OVER_0x07 3
++
++#define SCALER5_CTL2_ALPHA_PREMULT BIT(29)
++
++#define SCALER5_CTL2_ALPHA_MIX BIT(28)
++
++#define SCALER5_CTL2_ALPHA_LOC BIT(25)
++
++#define SCALER5_CTL2_MAP_SEL_MASK VC4_MASK(18, 17)
++#define SCALER5_CTL2_MAP_SEL_SHIFT 17
++
++#define SCALER5_CTL2_GAMMA BIT(16)
++
++#define SCALER5_CTL2_ALPHA_MASK VC4_MASK(15, 4)
++#define SCALER5_CTL2_ALPHA_SHIFT 4
++
+ #define SCALER_POS1_SCL_HEIGHT_MASK VC4_MASK(27, 16)
+ #define SCALER_POS1_SCL_HEIGHT_SHIFT 16
+
+ #define SCALER_POS1_SCL_WIDTH_MASK VC4_MASK(11, 0)
+ #define SCALER_POS1_SCL_WIDTH_SHIFT 0
+
++#define SCALER5_POS1_SCL_HEIGHT_MASK VC4_MASK(28, 16)
++#define SCALER5_POS1_SCL_HEIGHT_SHIFT 16
++
++#define SCALER5_POS1_SCL_WIDTH_MASK VC4_MASK(12, 0)
++#define SCALER5_POS1_SCL_WIDTH_SHIFT 0
++
+ #define SCALER_POS2_ALPHA_MODE_MASK VC4_MASK(31, 30)
+ #define SCALER_POS2_ALPHA_MODE_SHIFT 30
+ #define SCALER_POS2_ALPHA_MODE_PIPELINE 0
+@@ -931,6 +992,12 @@ enum hvs_pixel_format {
+ #define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0)
+ #define SCALER_POS2_WIDTH_SHIFT 0
+
++#define SCALER5_POS2_HEIGHT_MASK VC4_MASK(28, 16)
++#define SCALER5_POS2_HEIGHT_SHIFT 16
++
++#define SCALER5_POS2_WIDTH_MASK VC4_MASK(12, 0)
++#define SCALER5_POS2_WIDTH_SHIFT 0
++
+ /* Color Space Conversion words. Some values are S2.8 signed
+ * integers, except that the 2 integer bits map as {0x0: 0, 0x1: 1,
+ * 0x2: 2, 0x3: -1}
diff --git a/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch b/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch
new file mode 100644
index 0000000000..df7a98fc54
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch
@@ -0,0 +1,98 @@
+From 81072e19a85bfa3f80c23acdff6156522d945efa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 11 Feb 2020 16:55:02 +0000
+Subject: [PATCH] drm/vc4: plane: Improve LBM usage
+
+LBM allocations were always taking the worst case sizing of
+max(src_width, dst_width) * 16. This is significantly over
+the required sizing, and stops us rendering multiple 4k images
+to the screen.
+
+Add some of the additional constraints to more accurately
+describe the LBM requirements.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 31 ++++++++++++++++++++-----------
+ 1 file changed, 20 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -142,9 +142,10 @@ static const struct hvs_format *vc4_get_
+ return NULL;
+ }
+
+-static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst)
++static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst,
++ bool chroma_vrep)
+ {
+- if (dst == src)
++ if (dst == src && !chroma_vrep)
+ return VC4_SCALING_NONE;
+ if (3 * dst >= 2 * src)
+ return VC4_SCALING_PPF;
+@@ -369,9 +370,11 @@ static int vc4_plane_setup_clipping_and_
+ return ret;
+
+ vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
+- vc4_state->crtc_w);
++ vc4_state->crtc_w,
++ false);
+ vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
+- vc4_state->crtc_h);
++ vc4_state->crtc_h,
++ false);
+
+ vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE &&
+ vc4_state->y_scaling[0] == VC4_SCALING_NONE);
+@@ -384,10 +387,12 @@ static int vc4_plane_setup_clipping_and_
+
+ vc4_state->x_scaling[1] =
+ vc4_get_scaling_mode(vc4_state->src_w[1],
+- vc4_state->crtc_w);
++ vc4_state->crtc_w,
++ v_subsample == 2);
+ vc4_state->y_scaling[1] =
+ vc4_get_scaling_mode(vc4_state->src_h[1],
+- vc4_state->crtc_h);
++ vc4_state->crtc_h,
++ v_subsample == 2);
+
+ /* YUV conversion requires that horizontal scaling be enabled
+ * on the UV plane even if vc4_get_scaling_mode() returned
+@@ -437,10 +442,7 @@ static void vc4_write_ppf(struct vc4_pla
+ static u32 vc4_lbm_size(struct drm_plane_state *state)
+ {
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+- /* This is the worst case number. One of the two sizes will
+- * be used depending on the scaling configuration.
+- */
+- u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w);
++ u32 pix_per_line;
+ u32 lbm;
+
+ /* LBM is not needed when there's no vertical scaling. */
+@@ -448,6 +450,11 @@ static u32 vc4_lbm_size(struct drm_plane
+ vc4_state->y_scaling[1] == VC4_SCALING_NONE)
+ return 0;
+
++ if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ)
++ pix_per_line = vc4_state->crtc_w;
++ else
++ pix_per_line = vc4_state->src_w[0];
++
+ if (!vc4_state->is_yuv) {
+ if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
+ lbm = pix_per_line * 8;
+@@ -583,7 +590,9 @@ static int vc4_plane_allocate_lbm(struct
+ spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
+ ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+ &vc4_state->lbm,
+- lbm_size, 32, 0, 0);
++ lbm_size,
++ vc4->hvs->hvs5 ? 64 : 32,
++ 0, 0);
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+
+ if (ret)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch b/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch
new file mode 100644
index 0000000000..7c3c470849
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch
@@ -0,0 +1,125 @@
+From ac2c812856c3a496354b9f19d0a43458e108844d Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 14:32:57 +0100
+Subject: [PATCH] drm/vc4: plane: Move planes creation to its own
+ function
+
+The planes so far were created as part of the CRTC binding code with
+each planes created associated only to one CRTC. However, the hardware
+in the vc4 doesn't really have such constraint and can be used with any
+CRTC.
+
+In order to rework this, let's first move the overlay and cursor planes
+creation to a function of its own.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 33 ++++------------------------
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
+ drivers/gpu/drm/vc4/vc4_plane.c | 38 +++++++++++++++++++++++++++++++++
+ 3 files changed, 44 insertions(+), 29 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1142,7 +1142,7 @@ static int vc4_crtc_bind(struct device *
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_crtc *vc4_crtc;
+ struct drm_crtc *crtc;
+- struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp;
++ struct drm_plane *primary_plane, *destroy_plane, *temp;
+ const struct of_device_id *match;
+ int ret, i;
+
+@@ -1190,34 +1190,9 @@ static int vc4_crtc_bind(struct device *
+ */
+ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
+
+- /* Set up some arbitrary number of planes. We're not limited
+- * by a set number of physical registers, just the space in
+- * the HVS (16k) and how small an plane can be (28 bytes).
+- * However, each plane we set up takes up some memory, and
+- * increases the cost of looping over planes, which atomic
+- * modesetting does quite a bit. As a result, we pick a
+- * modest number of planes to expose, that should hopefully
+- * still cover any sane usecase.
+- */
+- for (i = 0; i < 8; i++) {
+- struct drm_plane *plane =
+- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
+-
+- if (IS_ERR(plane))
+- continue;
+-
+- plane->possible_crtcs = drm_crtc_mask(crtc);
+- }
+-
+- /* Set up the legacy cursor after overlay initialization,
+- * since we overlay planes on the CRTC in the order they were
+- * initialized.
+- */
+- cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
+- if (!IS_ERR(cursor_plane)) {
+- cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
+- crtc->cursor = cursor_plane;
+- }
++ ret = vc4_plane_create_additional_planes(drm, crtc);
++ if (ret)
++ goto err_destroy_planes;
+
+ vc4_crtc_get_cob_allocation(vc4_crtc);
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -855,6 +855,8 @@ int vc4_kms_load(struct drm_device *dev)
+ /* vc4_plane.c */
+ struct drm_plane *vc4_plane_init(struct drm_device *dev,
+ enum drm_plane_type type);
++int vc4_plane_create_additional_planes(struct drm_device *dev,
++ struct drm_crtc *crtc);
+ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+ u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
+ void vc4_plane_async_set_fb(struct drm_plane *plane,
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1437,3 +1437,41 @@ struct drm_plane *vc4_plane_init(struct
+
+ return plane;
+ }
++
++int vc4_plane_create_additional_planes(struct drm_device *drm,
++ struct drm_crtc *crtc)
++{
++ struct drm_plane *cursor_plane;
++ unsigned int i;
++
++ /* Set up some arbitrary number of planes. We're not limited
++ * by a set number of physical registers, just the space in
++ * the HVS (16k) and how small an plane can be (28 bytes).
++ * However, each plane we set up takes up some memory, and
++ * increases the cost of looping over planes, which atomic
++ * modesetting does quite a bit. As a result, we pick a
++ * modest number of planes to expose, that should hopefully
++ * still cover any sane usecase.
++ */
++ for (i = 0; i < 8; i++) {
++ struct drm_plane *plane =
++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
++
++ if (IS_ERR(plane))
++ continue;
++
++ plane->possible_crtcs = drm_crtc_mask(crtc);
++ }
++
++ /* Set up the legacy cursor after overlay initialization,
++ * since we overlay planes on the CRTC in the order they were
++ * initialized.
++ */
++ cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
++ if (!IS_ERR(cursor_plane)) {
++ cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
++ crtc->cursor = cursor_plane;
++ }
++
++ return 0;
++}
diff --git a/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch b/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch
new file mode 100644
index 0000000000..b2e9f15647
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch
@@ -0,0 +1,75 @@
+From 5331cbb3d9cfb172ed134f08a35740e0a52d1107 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 14:41:41 +0100
+Subject: [PATCH] drm/vc4: plane: Move additional planes creation to
+ driver
+
+So far the plane creation was done when each CRTC was bound, and those
+planes were only tied to the CRTC that was registering them.
+
+This causes two main issues:
+ - The planes in the vc4 hardware are actually not tied to any CRTC, but
+ can be used with every combination
+
+ - More importantly, so far, we allocate 10 planes per CRTC, with 3 CRTCs.
+ However, the next generation of hardware will have 5 CRTCs, putting us
+ well above the maximum of 32 planes currently allowed by DRM.
+
+This patch is the first one in a series of patches that will take down both
+of these issues so that we can support the next generation of hardware
+while keeping a good amount of planes.
+
+We start by changing the way the planes are registered to first registering
+the primary planes for each CRTC in the CRTC bind function as we used to,
+but moving the overlay and cursor creation to the main driver bind
+function, after all the CRTCs have been bound.
+
+This will slightly change the ID order of the planes, since the primary
+planes of all CRTCs will be first, and then a pattern of 8 overlays, 1
+cursor plane for each CRTC.
+
+This shouldn't cause any trouble since the ordering between the planes is
+preserved though.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 4 ----
+ drivers/gpu/drm/vc4/vc4_drv.c | 7 +++++++
+ 2 files changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1190,10 +1190,6 @@ static int vc4_crtc_bind(struct device *
+ */
+ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
+
+- ret = vc4_plane_create_additional_planes(drm, crtc);
+- if (ret)
+- goto err_destroy_planes;
+-
+ vc4_crtc_get_cob_allocation(vc4_crtc);
+
+ CRTC_WRITE(PV_INTEN, 0);
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -253,6 +253,7 @@ static int vc4_drm_bind(struct device *d
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm;
++ struct drm_crtc *crtc;
+ struct vc4_dev *vc4;
+ struct device_node *node;
+ int ret = 0;
+@@ -291,6 +292,12 @@ static int vc4_drm_bind(struct device *d
+ if (ret)
+ goto gem_destroy;
+
++ drm_for_each_crtc(crtc, drm) {
++ ret = vc4_plane_create_additional_planes(drm, crtc);
++ if (ret)
++ continue;
++ }
++
+ drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false);
+
+ ret = vc4_kms_load(drm);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch b/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch
new file mode 100644
index 0000000000..e917410b86
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch
@@ -0,0 +1,128 @@
+From bb2b068209d73b320cac7222a3b8ecef9b0dcc9a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 14:46:14 +0100
+Subject: [PATCH] drm/vc4: plane: Register all the planes at once
+
+Instead of creating planes for each CRTC, we eventually want to create all
+the planes for each CRTCs.
+
+In order to make that more convenient, let's iterate on the CRTCs in the
+plane creation function instead of its caller.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 9 ++----
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 +-
+ drivers/gpu/drm/vc4/vc4_plane.c | 54 +++++++++++++++++----------------
+ 3 files changed, 32 insertions(+), 34 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -253,7 +253,6 @@ static int vc4_drm_bind(struct device *d
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm;
+- struct drm_crtc *crtc;
+ struct vc4_dev *vc4;
+ struct device_node *node;
+ int ret = 0;
+@@ -292,11 +291,9 @@ static int vc4_drm_bind(struct device *d
+ if (ret)
+ goto gem_destroy;
+
+- drm_for_each_crtc(crtc, drm) {
+- ret = vc4_plane_create_additional_planes(drm, crtc);
+- if (ret)
+- continue;
+- }
++ ret = vc4_plane_create_additional_planes(drm);
++ if (ret)
++ goto unbind_all;
+
+ drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false);
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -855,8 +855,7 @@ int vc4_kms_load(struct drm_device *dev)
+ /* vc4_plane.c */
+ struct drm_plane *vc4_plane_init(struct drm_device *dev,
+ enum drm_plane_type type);
+-int vc4_plane_create_additional_planes(struct drm_device *dev,
+- struct drm_crtc *crtc);
++int vc4_plane_create_additional_planes(struct drm_device *dev);
+ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+ u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
+ void vc4_plane_async_set_fb(struct drm_plane *plane,
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1438,39 +1438,41 @@ struct drm_plane *vc4_plane_init(struct
+ return plane;
+ }
+
+-int vc4_plane_create_additional_planes(struct drm_device *drm,
+- struct drm_crtc *crtc)
++int vc4_plane_create_additional_planes(struct drm_device *drm)
+ {
+ struct drm_plane *cursor_plane;
++ struct drm_crtc *crtc;
+ unsigned int i;
+
+- /* Set up some arbitrary number of planes. We're not limited
+- * by a set number of physical registers, just the space in
+- * the HVS (16k) and how small an plane can be (28 bytes).
+- * However, each plane we set up takes up some memory, and
+- * increases the cost of looping over planes, which atomic
+- * modesetting does quite a bit. As a result, we pick a
+- * modest number of planes to expose, that should hopefully
+- * still cover any sane usecase.
+- */
+- for (i = 0; i < 8; i++) {
+- struct drm_plane *plane =
+- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
+-
+- if (IS_ERR(plane))
+- continue;
+-
+- plane->possible_crtcs = drm_crtc_mask(crtc);
+- }
+-
+- /* Set up the legacy cursor after overlay initialization,
+- * since we overlay planes on the CRTC in the order they were
+- * initialized.
+- */
+- cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
+- if (!IS_ERR(cursor_plane)) {
+- cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
+- crtc->cursor = cursor_plane;
++ drm_for_each_crtc(crtc, drm) {
++ /* Set up some arbitrary number of planes. We're not limited
++ * by a set number of physical registers, just the space in
++ * the HVS (16k) and how small an plane can be (28 bytes).
++ * However, each plane we set up takes up some memory, and
++ * increases the cost of looping over planes, which atomic
++ * modesetting does quite a bit. As a result, we pick a
++ * modest number of planes to expose, that should hopefully
++ * still cover any sane usecase.
++ */
++ for (i = 0; i < 8; i++) {
++ struct drm_plane *plane =
++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
++
++ if (IS_ERR(plane))
++ continue;
++
++ plane->possible_crtcs = drm_crtc_mask(crtc);
++ }
++
++ /* Set up the legacy cursor after overlay initialization,
++ * since we overlay planes on the CRTC in the order they were
++ * initialized.
++ */
++ cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
++ if (!IS_ERR(cursor_plane)) {
++ cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
++ crtc->cursor = cursor_plane;
++ }
+ }
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch b/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch
new file mode 100644
index 0000000000..54d04e59e1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch
@@ -0,0 +1,70 @@
+From b65167e0bcce67f2e7b7e813dba536f1cca3ef9f Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 14:50:06 +0100
+Subject: [PATCH] drm/vc4: plane: Create overlays for any CRTC
+
+Now that we have everything in place, we can now register all the overlay
+planes that can be assigned to all the CRTCs.
+
+This has two side effects:
+
+ - The number of overlay planes is reduced from 24 to 8. This is temporary
+ and will be increased again in the next patch.
+
+ - The ID of the various planes is changed again, and we will now have all
+ the primary planes, then all the overlay planes and finally the cursor
+ planes. This shouldn't cause any issue since the ordering between
+ primary, overlay and cursor planes is preserved.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 35 +++++++++++++++++----------------
+ 1 file changed, 18 insertions(+), 17 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1444,26 +1444,27 @@ int vc4_plane_create_additional_planes(s
+ struct drm_crtc *crtc;
+ unsigned int i;
+
+- drm_for_each_crtc(crtc, drm) {
+- /* Set up some arbitrary number of planes. We're not limited
+- * by a set number of physical registers, just the space in
+- * the HVS (16k) and how small an plane can be (28 bytes).
+- * However, each plane we set up takes up some memory, and
+- * increases the cost of looping over planes, which atomic
+- * modesetting does quite a bit. As a result, we pick a
+- * modest number of planes to expose, that should hopefully
+- * still cover any sane usecase.
+- */
+- for (i = 0; i < 8; i++) {
+- struct drm_plane *plane =
+- vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
++ /* Set up some arbitrary number of planes. We're not limited
++ * by a set number of physical registers, just the space in
++ * the HVS (16k) and how small an plane can be (28 bytes).
++ * However, each plane we set up takes up some memory, and
++ * increases the cost of looping over planes, which atomic
++ * modesetting does quite a bit. As a result, we pick a
++ * modest number of planes to expose, that should hopefully
++ * still cover any sane usecase.
++ */
++ for (i = 0; i < 8; i++) {
++ struct drm_plane *plane =
++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
+
+- if (IS_ERR(plane))
+- continue;
++ if (IS_ERR(plane))
++ continue;
+
+- plane->possible_crtcs = drm_crtc_mask(crtc);
+- }
++ plane->possible_crtcs =
++ GENMASK(drm->mode_config.num_crtc - 1, 0);
++ }
+
++ drm_for_each_crtc(crtc, drm) {
+ /* Set up the legacy cursor after overlay initialization,
+ * since we overlay planes on the CRTC in the order they were
+ * initialized.
diff --git a/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch b/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch
new file mode 100644
index 0000000000..08383e3708
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch
@@ -0,0 +1,32 @@
+From b79a33509a3aa863cdf54d24e1a4a0cc2c6fe84c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 14:52:42 +0100
+Subject: [PATCH] drm/vc4: plane: Create more planes
+
+Let's now create more planes that can be affected to all the CRTCs.
+
+vc4 has 3 CRTCs, 1 primary and 1 cursor each, and was having 24 (8
+planes per CRTC) overlays.
+
+However, vc5 has 5 CRTCs, so keeping the same logic would put us at 50
+planes which is well above the 32 planes limit imposed by DRM.
+
+Using 16 seems like a good tradeoff between staying under 32 and yet
+providing enough planes.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1453,7 +1453,7 @@ int vc4_plane_create_additional_planes(s
+ * modest number of planes to expose, that should hopefully
+ * still cover any sane usecase.
+ */
+- for (i = 0; i < 8; i++) {
++ for (i = 0; i < 16; i++) {
+ struct drm_plane *plane =
+ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch b/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch
new file mode 100644
index 0000000000..8b8eeea737
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch
@@ -0,0 +1,56 @@
+From f071c70678b875d2e5411ead123015381647e9f9 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 11:45:04 +0100
+Subject: [PATCH] drm/vc4: crtc: Rename SoC data structures
+
+Since we're going to introduce pixelvalve data structures for other SoCs
+than the BCM2835, let's rename the structures defined in the code to
+make it obvious which SoC we're targetting.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1056,7 +1056,7 @@ static const struct drm_crtc_helper_func
+ .atomic_disable = vc4_crtc_atomic_disable,
+ };
+
+-static const struct vc4_crtc_data pv0_data = {
++static const struct vc4_crtc_data bcm2835_pv0_data = {
+ .hvs_channel = 0,
+ .debugfs_name = "crtc0_regs",
+ .encoder_types = {
+@@ -1065,7 +1065,7 @@ static const struct vc4_crtc_data pv0_da
+ },
+ };
+
+-static const struct vc4_crtc_data pv1_data = {
++static const struct vc4_crtc_data bcm2835_pv1_data = {
+ .hvs_channel = 2,
+ .debugfs_name = "crtc1_regs",
+ .encoder_types = {
+@@ -1074,7 +1074,7 @@ static const struct vc4_crtc_data pv1_da
+ },
+ };
+
+-static const struct vc4_crtc_data pv2_data = {
++static const struct vc4_crtc_data bcm2835_pv2_data = {
+ .hvs_channel = 1,
+ .debugfs_name = "crtc2_regs",
+ .encoder_types = {
+@@ -1084,9 +1084,9 @@ static const struct vc4_crtc_data pv2_da
+ };
+
+ static const struct of_device_id vc4_crtc_dt_match[] = {
+- { .compatible = "brcm,bcm2835-pixelvalve0", .data = &pv0_data },
+- { .compatible = "brcm,bcm2835-pixelvalve1", .data = &pv1_data },
+- { .compatible = "brcm,bcm2835-pixelvalve2", .data = &pv2_data },
++ { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data },
++ { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data },
++ { .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data },
+ {}
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch b/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch
new file mode 100644
index 0000000000..6108f86787
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch
@@ -0,0 +1,74 @@
+From 05293c3b61cdeb0004722cc86e03123183557de1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 15:45:04 +0100
+Subject: [PATCH] drm/vc4: crtc: Move crtc state to common header
+
+We'll need to access the crtc_state from outside of vc4_crtc.c, so let's
+move it to vc4_drv.h
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 21 ---------------------
+ drivers/gpu/drm/vc4/vc4_drv.h | 21 +++++++++++++++++++++
+ 2 files changed, 21 insertions(+), 21 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -44,27 +44,6 @@
+ #include "vc4_drv.h"
+ #include "vc4_regs.h"
+
+-struct vc4_crtc_state {
+- struct drm_crtc_state base;
+- /* Dlist area for this CRTC configuration. */
+- struct drm_mm_node mm;
+- bool feed_txp;
+- bool txp_armed;
+-
+- struct {
+- unsigned int left;
+- unsigned int right;
+- unsigned int top;
+- unsigned int bottom;
+- } margins;
+-};
+-
+-static inline struct vc4_crtc_state *
+-to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
+-{
+- return (struct vc4_crtc_state *)crtc_state;
+-}
+-
+ #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
+ #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -488,6 +488,27 @@ to_vc4_crtc(struct drm_crtc *crtc)
+ return (struct vc4_crtc *)crtc;
+ }
+
++struct vc4_crtc_state {
++ struct drm_crtc_state base;
++ /* Dlist area for this CRTC configuration. */
++ struct drm_mm_node mm;
++ bool feed_txp;
++ bool txp_armed;
++
++ struct {
++ unsigned int left;
++ unsigned int right;
++ unsigned int top;
++ unsigned int bottom;
++ } margins;
++};
++
++static inline struct vc4_crtc_state *
++to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
++{
++ return (struct vc4_crtc_state *)crtc_state;
++}
++
+ #define V3D_READ(offset) readl(vc4->v3d->regs + offset)
+ #define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset)
+ #define HVS_READ(offset) readl(vc4->hvs->regs + offset)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch b/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch
new file mode 100644
index 0000000000..5949e616f9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch
@@ -0,0 +1,86 @@
+From b8714036be64c86a274ea49ba0066af0a81c6b98 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 11:36:50 +0100
+Subject: [PATCH] drm/vc4: crtc: Deal with different number of pixel
+ per clock
+
+Some of the HDMI pixelvalves in vc5 output two pixels per clock cycle.
+Let's put the number of pixel output per clock cycle in the CRTC data and
+update the various calculations to reflect that.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 17 ++++++++++-------
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 +++
+ 2 files changed, 13 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -281,6 +281,7 @@ static void vc4_crtc_config_pv(struct dr
+ bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
+ vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
+ u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
++ u8 ppc = vc4_crtc->data->pixels_per_clock;
+
+ /* Reset the PV fifo. */
+ CRTC_WRITE(PV_CONTROL, 0);
+@@ -288,17 +289,16 @@ static void vc4_crtc_config_pv(struct dr
+ CRTC_WRITE(PV_CONTROL, 0);
+
+ CRTC_WRITE(PV_HORZA,
+- VC4_SET_FIELD((mode->htotal -
+- mode->hsync_end) * pixel_rep,
++ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+ PV_HORZA_HBP) |
+- VC4_SET_FIELD((mode->hsync_end -
+- mode->hsync_start) * pixel_rep,
++ VC4_SET_FIELD((mode->hsync_end - mode->hsync_start) * pixel_rep / ppc,
+ PV_HORZA_HSYNC));
++
+ CRTC_WRITE(PV_HORZB,
+- VC4_SET_FIELD((mode->hsync_start -
+- mode->hdisplay) * pixel_rep,
++ VC4_SET_FIELD((mode->hsync_start - mode->hdisplay) * pixel_rep / ppc,
+ PV_HORZB_HFP) |
+- VC4_SET_FIELD(mode->hdisplay * pixel_rep, PV_HORZB_HACTIVE));
++ VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc,
++ PV_HORZB_HACTIVE));
+
+ CRTC_WRITE(PV_VERTA,
+ VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+@@ -1038,6 +1038,7 @@ static const struct drm_crtc_helper_func
+ static const struct vc4_crtc_data bcm2835_pv0_data = {
+ .hvs_channel = 0,
+ .debugfs_name = "crtc0_regs",
++ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0,
+ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI,
+@@ -1047,6 +1048,7 @@ static const struct vc4_crtc_data bcm283
+ static const struct vc4_crtc_data bcm2835_pv1_data = {
+ .hvs_channel = 2,
+ .debugfs_name = "crtc1_regs",
++ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1,
+ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI,
+@@ -1056,6 +1058,7 @@ static const struct vc4_crtc_data bcm283
+ static const struct vc4_crtc_data bcm2835_pv2_data = {
+ .hvs_channel = 1,
+ .debugfs_name = "crtc2_regs",
++ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI,
+ [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -455,6 +455,9 @@ struct vc4_crtc_data {
+ /* Which channel of the HVS this pixelvalve sources from. */
+ int hvs_channel;
+
++ /* Number of pixels output per clock period */
++ u8 pixels_per_clock;
++
+ enum vc4_encoder_type encoder_types[4];
+ const char *debugfs_name;
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch b/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch
new file mode 100644
index 0000000000..b517b5414b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch
@@ -0,0 +1,26 @@
+From 5451dc04ff87dcf514c422f180ea5e23b7b60151 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 9 Jan 2020 18:40:49 +0100
+Subject: [PATCH] drm/vc4: crtc: Use a shared interrupt
+
+Some pixelvalves in vc5 use the same interrupt line so let's register our
+interrupt handler as a shared one.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1177,7 +1177,9 @@ static int vc4_crtc_bind(struct device *
+ CRTC_WRITE(PV_INTEN, 0);
+ CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+- vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
++ vc4_crtc_irq_handler,
++ IRQF_SHARED,
++ "vc4 crtc", vc4_crtc);
+ if (ret)
+ goto err_destroy_planes;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch b/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch
new file mode 100644
index 0000000000..3deefc361f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch
@@ -0,0 +1,50 @@
+From c017882242d671cf81256301a3e9a6fc9eefdc13 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 13 Jan 2020 13:39:32 +0100
+Subject: [PATCH] drm/vc4: crtc: Turn static const variable into a
+ define
+
+The hvs_latency_pix variable doesn't need to be a variable and can just be
+defined.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -44,6 +44,8 @@
+ #include "vc4_drv.h"
+ #include "vc4_regs.h"
+
++#define HVS_FIFO_LATENCY_PIX 6
++
+ #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
+ #define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
+
+@@ -227,21 +229,21 @@ vc4_crtc_update_gamma_lut(struct drm_crt
+ vc4_crtc_lut_load(crtc);
+ }
+
++
+ static u32 vc4_get_fifo_full_level(u32 format)
+ {
+ static const u32 fifo_len_bytes = 64;
+- static const u32 hvs_latency_pix = 6;
+
+ switch (format) {
+ case PV_CONTROL_FORMAT_DSIV_16:
+ case PV_CONTROL_FORMAT_DSIC_16:
+- return fifo_len_bytes - 2 * hvs_latency_pix;
++ return fifo_len_bytes - 2 * HVS_FIFO_LATENCY_PIX;
+ case PV_CONTROL_FORMAT_DSIV_18:
+ return fifo_len_bytes - 14;
+ case PV_CONTROL_FORMAT_24:
+ case PV_CONTROL_FORMAT_DSIV_24:
+ default:
+- return fifo_len_bytes - 3 * hvs_latency_pix;
++ return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX;
+ }
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch b/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch
new file mode 100644
index 0000000000..83c49132a3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch
@@ -0,0 +1,110 @@
+From e93fc4ed811c7dcc6b0c93716f760431fc645ba2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 15:48:09 +0100
+Subject: [PATCH] drm/vc4: crtc: Move the cob allocation outside of
+ bind
+
+The COB allocation depends on the HVS channel used for a given
+pixelvalve.
+
+While the channel allocation was entirely static in vc4, vc5 changes
+that and at bind time, a pixelvalve can be assigned to multiple
+HVS channels.
+
+Let's prepare that rework by allocating the COB when it's actually
+needed.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 39 +++++++++++++++++-----------------
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 --
+ 2 files changed, 20 insertions(+), 21 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -65,6 +65,23 @@ static const struct debugfs_reg32 crtc_r
+ VC4_REG32(PV_HACT_ACT),
+ };
+
++static unsigned int
++vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc, unsigned int channel)
++{
++ struct drm_device *drm = vc4_crtc->base.dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
++
++ u32 dispbase = HVS_READ(SCALER_DISPBASEX(channel));
++ /* Top/base are supposed to be 4-pixel aligned, but the
++ * Raspberry Pi firmware fills the low bits (which are
++ * presumably ignored).
++ */
++ u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
++ u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
++
++ return top - base + 4;
++}
++
+ bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+@@ -73,6 +90,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++ unsigned int cob_size;
+ u32 val;
+ int fifo_lines;
+ int vblank_lines;
+@@ -108,8 +126,9 @@ bool vc4_crtc_get_scanoutpos(struct drm_
+ *hpos += mode->crtc_htotal / 2;
+ }
+
++ cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc->channel);
+ /* This is the offset we need for translating hvs -> pv scanout pos. */
+- fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
++ fifo_lines = cob_size / mode->crtc_hdisplay;
+
+ if (fifo_lines > 0)
+ ret = true;
+@@ -1104,22 +1123,6 @@ static void vc4_set_crtc_possible_masks(
+ }
+ }
+
+-static void
+-vc4_crtc_get_cob_allocation(struct vc4_crtc *vc4_crtc)
+-{
+- struct drm_device *drm = vc4_crtc->base.dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+- u32 dispbase = HVS_READ(SCALER_DISPBASEX(vc4_crtc->channel));
+- /* Top/base are supposed to be 4-pixel aligned, but the
+- * Raspberry Pi firmware fills the low bits (which are
+- * presumably ignored).
+- */
+- u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
+- u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
+-
+- vc4_crtc->cob_size = top - base + 4;
+-}
+-
+ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+@@ -1174,8 +1177,6 @@ static int vc4_crtc_bind(struct device *
+ */
+ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
+
+- vc4_crtc_get_cob_allocation(vc4_crtc);
+-
+ CRTC_WRITE(PV_INTEN, 0);
+ CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -477,8 +477,6 @@ struct vc4_crtc {
+ u8 lut_r[256];
+ u8 lut_g[256];
+ u8 lut_b[256];
+- /* Size in pixels of the COB memory allocated to this CRTC. */
+- u32 cob_size;
+
+ struct drm_pending_vblank_event *event;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch b/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch
new file mode 100644
index 0000000000..cf854fceb8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch
@@ -0,0 +1,80 @@
+From a106e57a643c957af9a71eb2ec3a62df69a1f371 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 13:49:17 +0100
+Subject: [PATCH] drm/vc4: crtc: Rename HVS channel to output
+
+In vc5, the HVS has 6 outputs and 3 FIFOs (or channels), with
+pixelvalves each being assigned to a given output, but each output can
+then be muxed to feed from multiple FIFOs.
+
+Since vc4 had that entirely static, both were probably equivalent, but
+since that changes, let's rename hvs_channel to hvs_output in the
+vc4_crtc_data, since a pixelvalve is really connected to an output, and
+not to a FIFO.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++-----
+ drivers/gpu/drm/vc4/vc4_drv.h | 4 ++--
+ 2 files changed, 7 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1057,7 +1057,7 @@ static const struct drm_crtc_helper_func
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv0_data = {
+- .hvs_channel = 0,
++ .hvs_output = 0,
+ .debugfs_name = "crtc0_regs",
+ .pixels_per_clock = 1,
+ .encoder_types = {
+@@ -1067,7 +1067,7 @@ static const struct vc4_crtc_data bcm283
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv1_data = {
+- .hvs_channel = 2,
++ .hvs_output = 2,
+ .debugfs_name = "crtc1_regs",
+ .pixels_per_clock = 1,
+ .encoder_types = {
+@@ -1077,7 +1077,7 @@ static const struct vc4_crtc_data bcm283
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv2_data = {
+- .hvs_channel = 1,
++ .hvs_output = 1,
+ .debugfs_name = "crtc2_regs",
+ .pixels_per_clock = 1,
+ .encoder_types = {
+@@ -1106,7 +1106,7 @@ static void vc4_set_crtc_possible_masks(
+ int i;
+
+ /* HVS FIFO2 can feed the TXP IP. */
+- if (crtc_data->hvs_channel == 2 &&
++ if (crtc_data->hvs_output == 2 &&
+ encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) {
+ encoder->possible_crtcs |= drm_crtc_mask(crtc);
+ continue;
+@@ -1168,7 +1168,7 @@ static int vc4_crtc_bind(struct device *
+ drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+ &vc4_crtc_funcs, NULL);
+ drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+- vc4_crtc->channel = vc4_crtc->data->hvs_channel;
++ vc4_crtc->channel = vc4_crtc->data->hvs_output;
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+ drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -452,8 +452,8 @@ to_vc4_encoder(struct drm_encoder *encod
+ }
+
+ struct vc4_crtc_data {
+- /* Which channel of the HVS this pixelvalve sources from. */
+- int hvs_channel;
++ /* Which output of the HVS this pixelvalve sources from. */
++ int hvs_output;
+
+ /* Number of pixels output per clock period */
+ u8 pixels_per_clock;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch
new file mode 100644
index 0000000000..6f77bace01
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch
@@ -0,0 +1,24 @@
+From 888e5149bdb810e67996828bb26955a57a482d4c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 14 Jan 2020 13:37:27 +0100
+Subject: [PATCH] drm/vc4: crtc: Use local chan variable
+
+The vc4_crtc_handle_page_flip already has a local variable holding the
+value of vc4_crtc->channel, so let's use it instead.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -816,7 +816,7 @@ static void vc4_crtc_handle_page_flip(st
+ * underruns. This can be seen when reconfiguring the CRTC.
+ */
+ if (vc4->hvs)
+- vc4_hvs_unmask_underrun(dev, vc4_crtc->channel);
++ vc4_hvs_unmask_underrun(dev, chan);
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch b/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch
new file mode 100644
index 0000000000..c5f06f20ea
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch
@@ -0,0 +1,55 @@
+From 7bbbfef1c98e832cbd55e66ac2d7f13ec0a2b11e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 21 Feb 2020 14:34:31 +0100
+Subject: [PATCH] drm/vc4: crtc: Enable and disable the PV in
+ atomic_enable / disable
+
+The VIDEN bit in the pixelvalve currently being used to enable or disable
+the pixelvalve seems to not be enough in some situations, which whill end
+up with the pixelvalve stalling.
+
+In such a case, even re-enabling VIDEN doesn't bring it back and we need to
+clear the FIFO. This can only be done if the pixelvalve is disabled though.
+
+In order to overcome this, we can configure the pixelvalve during
+mode_set_no_fb, but only enable it in atomic_enable and flush the FIFO
+there, and in atomic_disable disable the pixelvalve again.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -374,9 +374,7 @@ static void vc4_crtc_config_pv(struct dr
+ PV_CONTROL_TRIGGER_UNDERFLOW |
+ PV_CONTROL_WAIT_HSTART |
+ VC4_SET_FIELD(vc4_encoder->clock_select,
+- PV_CONTROL_CLK_SELECT) |
+- PV_CONTROL_FIFO_CLR |
+- PV_CONTROL_EN);
++ PV_CONTROL_CLK_SELECT));
+ }
+
+ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
+@@ -467,6 +465,8 @@ static void vc4_crtc_atomic_disable(stru
+ ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
+ WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n");
+
++ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN);
++
+ if (HVS_READ(SCALER_DISPCTRLX(chan)) &
+ SCALER_DISPCTRLX_ENABLE) {
+ HVS_WRITE(SCALER_DISPCTRLX(chan),
+@@ -554,6 +554,10 @@ static void vc4_crtc_atomic_enable(struc
+
+ require_hvs_enabled(dev);
+
++ /* Reset the PV fifo. */
++ CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) |
++ PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
++
+ /* Enable vblank irq handling before crtc is started otherwise
+ * drm_crtc_get_vblank() fails in vc4_crtc_update_dlist().
+ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch b/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch
new file mode 100644
index 0000000000..d470f3b7f0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch
@@ -0,0 +1,459 @@
+From 9efecb2ccd14a6d226ba2afa04f6e70b96026b3e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 17:53:18 +0100
+Subject: [PATCH] drm/vc4: crtc: Assign output to channel automatically
+
+The HVS found in the BCM2711 has 6 outputs and 3 FIFOs, with each output
+being connected to a pixelvalve, and some muxing between the FIFOs and
+outputs.
+
+Any output cannot feed from any FIFO though, and they all have a bunch of
+constraints.
+
+In order to support this, let's store the possible FIFOs each output can be
+assigned to in the vc4_crtc_data, and use that information at atomic_check
+time to iterate over all the CRTCs enabled and assign them FIFOs.
+
+The channel assigned is then set in the vc4_crtc_state so that the rest of
+the driver can use it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 37 +++++----
+ drivers/gpu/drm/vc4/vc4_drv.h | 7 +-
+ drivers/gpu/drm/vc4/vc4_kms.c | 146 +++++++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_regs.h | 10 +++
+ 4 files changed, 175 insertions(+), 25 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -90,6 +90,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
+ unsigned int cob_size;
+ u32 val;
+ int fifo_lines;
+@@ -106,7 +107,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_
+ * Read vertical scanline which is currently composed for our
+ * pixelvalve by the HVS, and also the scaler status.
+ */
+- val = HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel));
++ val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel));
+
+ /* Get optional system timestamp after query. */
+ if (etime)
+@@ -126,7 +127,7 @@ bool vc4_crtc_get_scanoutpos(struct drm_
+ *hpos += mode->crtc_htotal / 2;
+ }
+
+- cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc->channel);
++ cob_size = vc4_crtc_get_cob_allocation(vc4_crtc, vc4_crtc_state->assigned_channel);
+ /* This is the offset we need for translating hvs -> pv scanout pos. */
+ fifo_lines = cob_size / mode->crtc_hdisplay;
+
+@@ -213,6 +214,7 @@ vc4_crtc_lut_load(struct drm_crtc *crtc)
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
+ u32 i;
+
+ /* The LUT memory is laid out with each HVS channel in order,
+@@ -221,7 +223,7 @@ vc4_crtc_lut_load(struct drm_crtc *crtc)
+ */
+ HVS_WRITE(SCALER_GAMADDR,
+ SCALER_GAMADDR_AUTOINC |
+- (vc4_crtc->channel * 3 * crtc->gamma_size));
++ (vc4_crtc_state->assigned_channel * 3 * crtc->gamma_size));
+
+ for (i = 0; i < crtc->gamma_size; i++)
+ HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]);
+@@ -394,7 +396,7 @@ static void vc4_crtc_mode_set_nofb(struc
+ drm_print_regset32(&p, &vc4_crtc->regset);
+ }
+
+- if (vc4_crtc->channel == 2) {
++ if (vc4_crtc->data->hvs_output == 2) {
+ u32 dispctrl;
+ u32 dsp3_mux;
+
+@@ -421,7 +423,7 @@ static void vc4_crtc_mode_set_nofb(struc
+ if (!vc4_state->feed_txp)
+ vc4_crtc_config_pv(crtc);
+
+- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
+ SCALER_DISPBKGND_AUTOHS |
+ SCALER_DISPBKGND_GAMMA |
+ (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
+@@ -453,7 +455,8 @@ static void vc4_crtc_atomic_disable(stru
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+- u32 chan = vc4_crtc->channel;
++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(old_state);
++ u32 chan = vc4_crtc_state->assigned_channel;
+ int ret;
+ require_hvs_enabled(dev);
+
+@@ -532,12 +535,12 @@ static void vc4_crtc_update_dlist(struct
+ crtc->state->event = NULL;
+ }
+
+- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+ vc4_state->mm.start);
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ } else {
+- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+ vc4_state->mm.start);
+ }
+ }
+@@ -586,7 +589,7 @@ static void vc4_crtc_atomic_enable(struc
+ (vc4_state->feed_txp ?
+ SCALER5_DISPCTRLX_ONESHOT : 0);
+
+- HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), dispctrl);
++ HVS_WRITE(SCALER_DISPCTRLX(vc4_state->assigned_channel), dispctrl);
+
+ /* When feeding the transposer block the pixelvalve is unneeded and
+ * should not be enabled.
+@@ -702,7 +705,6 @@ static void vc4_crtc_atomic_flush(struct
+ {
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ struct drm_plane *plane;
+ struct vc4_plane_state *vc4_plane_state;
+@@ -744,8 +746,8 @@ static void vc4_crtc_atomic_flush(struct
+ /* This sets a black background color fill, as is the case
+ * with other DRM drivers.
+ */
+- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel),
+- HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel)) |
++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
++ HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) |
+ SCALER_DISPBKGND_FILL);
+
+ /* Only update DISPLIST if the CRTC was already running and is not
+@@ -759,7 +761,7 @@ static void vc4_crtc_atomic_flush(struct
+ vc4_crtc_update_dlist(crtc);
+
+ if (crtc->state->color_mgmt_changed) {
+- u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_crtc->channel));
++ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel));
+
+ if (crtc->state->gamma_lut) {
+ vc4_crtc_update_gamma_lut(crtc);
+@@ -771,7 +773,7 @@ static void vc4_crtc_atomic_flush(struct
+ */
+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+ }
+- HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), dispbkgndx);
++ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx);
+ }
+
+ if (debug_dump_regs) {
+@@ -802,7 +804,7 @@ static void vc4_crtc_handle_page_flip(st
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+- u32 chan = vc4_crtc->channel;
++ u32 chan = vc4_state->assigned_channel;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+@@ -1002,6 +1004,7 @@ static struct drm_crtc_state *vc4_crtc_d
+ old_vc4_state = to_vc4_crtc_state(crtc->state);
+ vc4_state->feed_txp = old_vc4_state->feed_txp;
+ vc4_state->margins = old_vc4_state->margins;
++ vc4_state->assigned_channel = old_vc4_state->assigned_channel;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
+ return &vc4_state->base;
+@@ -1061,6 +1064,7 @@ static const struct drm_crtc_helper_func
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv0_data = {
++ .hvs_available_channels = BIT(0),
+ .hvs_output = 0,
+ .debugfs_name = "crtc0_regs",
+ .pixels_per_clock = 1,
+@@ -1071,6 +1075,7 @@ static const struct vc4_crtc_data bcm283
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv1_data = {
++ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
+ .debugfs_name = "crtc1_regs",
+ .pixels_per_clock = 1,
+@@ -1081,6 +1086,7 @@ static const struct vc4_crtc_data bcm283
+ };
+
+ static const struct vc4_crtc_data bcm2835_pv2_data = {
++ .hvs_available_channels = BIT(1),
+ .hvs_output = 1,
+ .debugfs_name = "crtc2_regs",
+ .pixels_per_clock = 1,
+@@ -1172,7 +1178,6 @@ static int vc4_crtc_bind(struct device *
+ drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+ &vc4_crtc_funcs, NULL);
+ drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+- vc4_crtc->channel = vc4_crtc->data->hvs_output;
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+ drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -452,6 +452,9 @@ to_vc4_encoder(struct drm_encoder *encod
+ }
+
+ struct vc4_crtc_data {
++ /* Which channels of the HVS can the output source from */
++ unsigned int hvs_available_channels;
++
+ /* Which output of the HVS this pixelvalve sources from. */
+ int hvs_output;
+
+@@ -471,9 +474,6 @@ struct vc4_crtc {
+ /* Timestamp at start of vblank irq - unaffected by lock delays. */
+ ktime_t t_vblank;
+
+- /* Which HVS channel we're using for our CRTC. */
+- int channel;
+-
+ u8 lut_r[256];
+ u8 lut_g[256];
+ u8 lut_b[256];
+@@ -495,6 +495,7 @@ struct vc4_crtc_state {
+ struct drm_mm_node mm;
+ bool feed_txp;
+ bool txp_armed;
++ unsigned int assigned_channel;
+
+ struct {
+ unsigned int left;
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -11,6 +11,9 @@
+ * crtc, HDMI encoder).
+ */
+
++#include <linux/bitfield.h>
++#include <linux/bitops.h>
++
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+ #include <drm/drm_crtc.h>
+@@ -148,6 +151,72 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru
+ VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO));
+ }
+
++static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4,
++ struct drm_atomic_state *state)
++{
++ struct drm_crtc_state *crtc_state;
++ struct drm_crtc *crtc;
++ unsigned char dsp2_mux = 0;
++ unsigned char dsp3_mux = 3;
++ unsigned char dsp4_mux = 3;
++ unsigned char dsp5_mux = 3;
++ unsigned int i;
++ u32 reg;
++
++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++
++ if (!crtc_state->active)
++ continue;
++
++ switch (vc4_crtc->data->hvs_output) {
++ case 2:
++ dsp2_mux = (vc4_state->assigned_channel == 2) ? 1 : 0;
++ break;
++
++ case 3:
++ dsp3_mux = vc4_state->assigned_channel;
++ break;
++
++ case 4:
++ dsp4_mux = vc4_state->assigned_channel;
++ break;
++
++ case 5:
++ dsp5_mux = vc4_state->assigned_channel;
++ break;
++
++ default:
++ break;
++ }
++ }
++
++ reg = HVS_READ(SCALER_DISPECTRL);
++ if (FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg) != dsp2_mux)
++ HVS_WRITE(SCALER_DISPECTRL,
++ (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) |
++ VC4_SET_FIELD(dsp2_mux, SCALER_DISPECTRL_DSP2_MUX));
++
++ reg = HVS_READ(SCALER_DISPCTRL);
++ if (FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg) != dsp3_mux)
++ HVS_WRITE(SCALER_DISPCTRL,
++ (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) |
++ VC4_SET_FIELD(dsp3_mux, SCALER_DISPCTRL_DSP3_MUX));
++
++ reg = HVS_READ(SCALER_DISPEOLN);
++ if (FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg) != dsp4_mux)
++ HVS_WRITE(SCALER_DISPEOLN,
++ (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) |
++ VC4_SET_FIELD(dsp4_mux, SCALER_DISPEOLN_DSP4_MUX));
++
++ reg = HVS_READ(SCALER_DISPDITHER);
++ if (FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg) != dsp5_mux)
++ HVS_WRITE(SCALER_DISPDITHER,
++ (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) |
++ VC4_SET_FIELD(dsp5_mux, SCALER_DISPDITHER_DSP5_MUX));
++}
++
+ static void
+ vc4_atomic_complete_commit(struct drm_atomic_state *state)
+ {
+@@ -157,11 +226,15 @@ vc4_atomic_complete_commit(struct drm_at
+ int i;
+
+ for (i = 0; vc4->hvs && i < dev->mode_config.num_crtc; i++) {
+- if (!state->crtcs[i].ptr || !state->crtcs[i].commit)
++ struct __drm_crtcs_state *_state = &state->crtcs[i];
++ struct vc4_crtc_state *vc4_crtc_state;
++
++ if (!_state->ptr || !_state->commit)
+ continue;
+
+- vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr);
+- vc4_hvs_mask_underrun(dev, vc4_crtc->channel);
++ vc4_crtc = to_vc4_crtc(_state->ptr);
++ vc4_crtc_state = to_vc4_crtc_state(_state->state);
++ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
+ }
+
+ drm_atomic_helper_wait_for_fences(dev, state, false);
+@@ -170,8 +243,10 @@ vc4_atomic_complete_commit(struct drm_at
+
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+- if (!vc4->firmware_kms)
++ if (!vc4->firmware_kms) {
+ vc4_ctm_commit(vc4, state);
++ vc4_hvs_pv_muxing_commit(vc4, state);
++ }
+
+ drm_atomic_helper_commit_planes(dev, state, 0);
+
+@@ -380,8 +455,11 @@ vc4_ctm_atomic_check(struct drm_device *
+
+ /* CTM is being enabled or the matrix changed. */
+ if (new_crtc_state->ctm) {
++ struct vc4_crtc_state *vc4_crtc_state =
++ to_vc4_crtc_state(new_crtc_state);
++
+ /* fifo is 1-based since 0 disables CTM. */
+- int fifo = to_vc4_crtc(crtc)->channel + 1;
++ int fifo = vc4_crtc_state->assigned_channel + 1;
+
+ /* Check userland isn't trying to turn on CTM for more
+ * than one CRTC at a time.
+@@ -494,10 +572,66 @@ static const struct drm_private_state_fu
+ .atomic_destroy_state = vc4_load_tracker_destroy_state,
+ };
+
++#define NUM_OUTPUTS 6
++#define NUM_CHANNELS 3
++
+ static int
+ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
+ {
+- int ret;
++ unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0);
++ struct drm_crtc_state *crtc_state;
++ struct drm_crtc *crtc;
++ int i, ret;
++
++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
++ struct vc4_crtc_state *vc4_crtc_state =
++ to_vc4_crtc_state(crtc_state);
++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++ bool is_assigned = false;
++ unsigned int channel;
++
++ if (!crtc_state->active)
++ continue;
++
++ /*
++ * The problem we have to solve here is that we have
++ * up to 7 encoders, connected to up to 6 CRTCs.
++ *
++ * Those CRTCs, depending on the instance, can be
++ * routed to 1, 2 or 3 HVS FIFOs, and we need to set
++ * the change the muxing between FIFOs and outputs in
++ * the HVS accordingly.
++ *
++ * It would be pretty hard to come up with an
++ * algorithm that would generically solve
++ * this. However, the current routing trees we support
++ * allow us to simplify a bit the problem.
++ *
++ * Indeed, with the current supported layouts, if we
++ * try to assign in the ascending crtc index order the
++ * FIFOs, we can't fall into the situation where an
++ * earlier CRTC that had multiple routes is assigned
++ * one that was the only option for a later CRTC.
++ *
++ * If the layout changes and doesn't give us that in
++ * the future, we will need to have something smarter,
++ * but it works so far.
++ */
++ for_each_set_bit(channel, &unassigned_channels,
++ sizeof(unassigned_channels)) {
++
++ if (!(BIT(channel) & vc4_crtc->data->hvs_available_channels))
++ continue;
++
++ vc4_crtc_state->assigned_channel = channel;
++ unassigned_channels &= ~BIT(channel);
++ is_assigned = true;
++ break;
++ }
++
++ if (!is_assigned)
++ return -EINVAL;
++ }
+
+ ret = vc4_ctm_atomic_check(dev, state);
+ if (ret < 0)
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -287,9 +287,19 @@
+
+ #define SCALER_DISPID 0x00000008
+ #define SCALER_DISPECTRL 0x0000000c
++# define SCALER_DISPECTRL_DSP2_MUX_SHIFT 31
++# define SCALER_DISPECTRL_DSP2_MUX_MASK VC4_MASK(31, 31)
++
+ #define SCALER_DISPPROF 0x00000010
++
+ #define SCALER_DISPDITHER 0x00000014
++# define SCALER_DISPDITHER_DSP5_MUX_SHIFT 30
++# define SCALER_DISPDITHER_DSP5_MUX_MASK VC4_MASK(31, 30)
++
+ #define SCALER_DISPEOLN 0x00000018
++# define SCALER_DISPEOLN_DSP4_MUX_SHIFT 30
++# define SCALER_DISPEOLN_DSP4_MUX_MASK VC4_MASK(31, 30)
++
+ #define SCALER_DISPLIST0 0x00000020
+ #define SCALER_DISPLIST1 0x00000024
+ #define SCALER_DISPLIST2 0x00000028
diff --git a/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch b/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch
new file mode 100644
index 0000000000..e920a0be64
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch
@@ -0,0 +1,86 @@
+From a294de7c4782f91fe724e4e5b05fd99798d50760 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 13 Jan 2020 13:39:20 +0100
+Subject: [PATCH] drm/vc4: crtc: Add FIFO depth to vc4_crtc_data
+
+Not all pixelvalve FIFOs in vc5 have the same depth, so we need to add that
+to our vc4_crtc_data structure to be able to compute the fill level
+properly later on.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 20 ++++++++++++++++----
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 +++
+ 2 files changed, 19 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -250,11 +250,20 @@ vc4_crtc_update_gamma_lut(struct drm_crt
+ vc4_crtc_lut_load(crtc);
+ }
+
+-
+-static u32 vc4_get_fifo_full_level(u32 format)
++static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format)
+ {
+- static const u32 fifo_len_bytes = 64;
++ u32 fifo_len_bytes = vc4_crtc->data->fifo_depth;
+
++ /*
++ * Pixels are pulled from the HVS if the number of bytes is
++ * lower than the FIFO full level.
++ *
++ * The latency of the pixel fetch mechanism is 6 pixels, so we
++ * need to convert those 6 pixels in bytes, depending on the
++ * format, and then substract that from the length of the FIFO
++ * to make sure we never end up in a situation where the FIFO
++ * is full.
++ */
+ switch (format) {
+ case PV_CONTROL_FORMAT_DSIV_16:
+ case PV_CONTROL_FORMAT_DSIC_16:
+@@ -369,7 +378,7 @@ static void vc4_crtc_config_pv(struct dr
+
+ CRTC_WRITE(PV_CONTROL,
+ VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+- VC4_SET_FIELD(vc4_get_fifo_full_level(format),
++ VC4_SET_FIELD(vc4_get_fifo_full_level(vc4_crtc, format),
+ PV_CONTROL_FIFO_LEVEL) |
+ VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) |
+ PV_CONTROL_CLR_AT_START |
+@@ -1067,6 +1076,7 @@ static const struct vc4_crtc_data bcm283
+ .hvs_available_channels = BIT(0),
+ .hvs_output = 0,
+ .debugfs_name = "crtc0_regs",
++ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0,
+@@ -1078,6 +1088,7 @@ static const struct vc4_crtc_data bcm283
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
+ .debugfs_name = "crtc1_regs",
++ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1,
+@@ -1089,6 +1100,7 @@ static const struct vc4_crtc_data bcm283
+ .hvs_available_channels = BIT(1),
+ .hvs_output = 1,
+ .debugfs_name = "crtc2_regs",
++ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -452,6 +452,9 @@ to_vc4_encoder(struct drm_encoder *encod
+ }
+
+ struct vc4_crtc_data {
++ /* Depth of the PixelValve FIFO in bytes */
++ unsigned int fifo_depth;
++
+ /* Which channels of the HVS can the output source from */
+ unsigned int hvs_available_channels;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch b/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch
new file mode 100644
index 0000000000..77952c0eea
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch
@@ -0,0 +1,44 @@
+From 2c241c25b76d105f798881e1a3c6e3c09c3b27ff Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 13 Jan 2020 13:40:37 +0100
+Subject: [PATCH] drm/vc4: crtc: Add function to compute FIFO level
+ bits
+
+The longer FIFOs in vc5 pixelvalves means that the FIFO full level
+doesn't fit in the original register field and that we also have a
+secondary field. In order to prepare for this, let's move the registers
+fill part to a helper function.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -277,6 +277,14 @@ static u32 vc4_get_fifo_full_level(struc
+ }
+ }
+
++static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc,
++ u32 format)
++{
++ u32 level = vc4_get_fifo_full_level(vc4_crtc, format);
++ return VC4_SET_FIELD(level & 0x3f,
++ PV_CONTROL_FIFO_LEVEL);
++}
++
+ /*
+ * Returns the encoder attached to the CRTC.
+ *
+@@ -377,9 +385,8 @@ static void vc4_crtc_config_pv(struct dr
+ CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
+
+ CRTC_WRITE(PV_CONTROL,
++ vc4_crtc_get_fifo_full_level_bits(vc4_crtc, format) |
+ VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+- VC4_SET_FIELD(vc4_get_fifo_full_level(vc4_crtc, format),
+- PV_CONTROL_FIFO_LEVEL) |
+ VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) |
+ PV_CONTROL_CLR_AT_START |
+ PV_CONTROL_TRIGGER_UNDERFLOW |
diff --git a/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch b/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch
new file mode 100644
index 0000000000..17362dafe3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch
@@ -0,0 +1,49 @@
+From ad4c39a27e141626c93b7b97df621d258bfdcbbe Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 9 Jan 2020 18:35:13 +0100
+Subject: [PATCH] drm/vc4: crtc: Rename HDMI encoder type to HDMI0
+
+The previous generations were only supporting a single HDMI controller, but
+that's about to change, so put an index as well to differentiate between
+the two controllers.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 +-
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1110,7 +1110,7 @@ static const struct vc4_crtc_data bcm283
+ .fifo_depth = 64,
+ .pixels_per_clock = 1,
+ .encoder_types = {
+- [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI,
++ [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI0,
+ [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC,
+ },
+ };
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -431,7 +431,7 @@ to_vc4_plane_state(struct drm_plane_stat
+
+ enum vc4_encoder_type {
+ VC4_ENCODER_TYPE_NONE,
+- VC4_ENCODER_TYPE_HDMI,
++ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1319,7 +1319,7 @@ static int vc4_hdmi_bind(struct device *
+ GFP_KERNEL);
+ if (!vc4_hdmi_encoder)
+ return -ENOMEM;
+- vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI;
++ vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI0;
+ hdmi->encoder = &vc4_hdmi_encoder->base.base;
+
+ hdmi->pdev = pdev;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch b/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch
new file mode 100644
index 0000000000..00ad755492
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch
@@ -0,0 +1,23 @@
+From 9df4f0e2da72c825d86f4f637983c712173ed272 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 9 Jan 2020 18:39:30 +0100
+Subject: [PATCH] drm/vc4: crtc: Add HDMI1 encoder type
+
+The BCM2711 sports a second HDMI controller, so let's add that second HDMI
+encoder type.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -432,6 +432,7 @@ to_vc4_plane_state(struct drm_plane_stat
+ enum vc4_encoder_type {
+ VC4_ENCODER_TYPE_NONE,
+ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_HDMI1,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch b/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch
new file mode 100644
index 0000000000..5704952569
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch
@@ -0,0 +1,24 @@
+From 278c3da2ce8c2cda6cb60946c55b5e7040dfc35a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 21 Feb 2020 16:48:19 +0100
+Subject: [PATCH] drm/vc4: crtc: Remove redundant call to
+ drm_crtc_enable_color_mgmt
+
+The driver calls the helper to add the color management properties twice,
+which is redundant. Remove the first one.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1198,7 +1198,6 @@ static int vc4_crtc_bind(struct device *
+ &vc4_crtc_funcs, NULL);
+ drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+- drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+
+ /* We support CTM, but only for one CRTC at a time. It's therefore
+ * implemented as private driver state in vc4_kms, not here.
diff --git a/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch b/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch
new file mode 100644
index 0000000000..bd487df5c4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch
@@ -0,0 +1,54 @@
+From 9e134cea82d5c69e5d564e87cda2b5cf3ec14768 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 21 Feb 2020 16:54:21 +0100
+Subject: [PATCH] drm/vc4: crtc: Disable color management for HVS5
+
+The HVS5 uses different color matrices. Disable color management support
+for now.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 17 +++++++++++------
+ 1 file changed, 11 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -441,7 +441,7 @@ static void vc4_crtc_mode_set_nofb(struc
+
+ HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel),
+ SCALER_DISPBKGND_AUTOHS |
+- SCALER_DISPBKGND_GAMMA |
++ ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) |
+ (interlace ? SCALER_DISPBKGND_INTERLACE : 0));
+
+ /* Reload the LUT, since the SRAMs would have been disabled if
+@@ -1156,6 +1156,7 @@ static int vc4_crtc_bind(struct device *
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_crtc *vc4_crtc;
+ struct drm_crtc *crtc;
+ struct drm_plane *primary_plane, *destroy_plane, *temp;
+@@ -1197,12 +1198,16 @@ static int vc4_crtc_bind(struct device *
+ drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+ &vc4_crtc_funcs, NULL);
+ drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+- drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+
+- /* We support CTM, but only for one CRTC at a time. It's therefore
+- * implemented as private driver state in vc4_kms, not here.
+- */
+- drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
++ if (!vc4->hvs->hvs5) {
++ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
++
++ /* We support CTM, but only for one CRTC at a
++ * time. It's therefore implemented as private driver
++ * state in vc4_kms, not here.
++ */
++ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
++ }
+
+ CRTC_WRITE(PV_INTEN, 0);
+ CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch b/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch
new file mode 100644
index 0000000000..6bf6ab74c4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch
@@ -0,0 +1,30 @@
+From 26613c79b74224154703d75c4f2d2aa120cc2e84 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 17:07:02 +0100
+Subject: [PATCH] dt-bindings: display: vc4: pv: Add BCM2711 pixel
+ valves
+
+The BCM2711 comes with other pixelvalves that have different requirements
+and capabilities. Let's document their compatible.
+
+Cc: devicetree@vger.kernel.org
+Reviewed-by: Rob Herring <robh+dt@kernel.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/display/brcm,bcm2835-pixelvalve0.yaml | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
+@@ -15,6 +15,11 @@ properties:
+ - brcm,bcm2835-pixelvalve0
+ - brcm,bcm2835-pixelvalve1
+ - brcm,bcm2835-pixelvalve2
++ - brcm,bcm2711-pixelvalve0
++ - brcm,bcm2711-pixelvalve1
++ - brcm,bcm2711-pixelvalve2
++ - brcm,bcm2711-pixelvalve3
++ - brcm,bcm2711-pixelvalve4
+
+ reg:
+ maxItems: 1
diff --git a/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch b/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch
new file mode 100644
index 0000000000..3ff57ef3df
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch
@@ -0,0 +1,152 @@
+From aa43601d97bf9136b657259f44c03a6a30b70d07 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 11:35:58 +0100
+Subject: [PATCH] drm/vc4: crtc: Add BCM2711 pixelvalves
+
+The BCM2711 has 5 pixelvalves, so now that our driver is ready, let's add
+support for them.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 82 +++++++++++++++++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc4_regs.h | 6 +++
+ 2 files changed, 86 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -273,6 +273,13 @@ static u32 vc4_get_fifo_full_level(struc
+ case PV_CONTROL_FORMAT_24:
+ case PV_CONTROL_FORMAT_DSIV_24:
+ default:
++ /*
++ * For some reason, the pixelvalve4 doesn't work with
++ * the usual formula and will only work with 32.
++ */
++ if (vc4_crtc->data->hvs_output == 5)
++ return 32;
++
+ return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX;
+ }
+ }
+@@ -281,8 +288,14 @@ static u32 vc4_crtc_get_fifo_full_level_
+ u32 format)
+ {
+ u32 level = vc4_get_fifo_full_level(vc4_crtc, format);
+- return VC4_SET_FIELD(level & 0x3f,
+- PV_CONTROL_FIFO_LEVEL);
++ u32 ret = 0;
++
++ if (level > 0x3f)
++ ret |= VC4_SET_FIELD((level >> 6) & 0x3,
++ PV5_CONTROL_FIFO_LEVEL_HIGH);
++
++ return ret | VC4_SET_FIELD(level & 0x3f,
++ PV_CONTROL_FIFO_LEVEL);
+ }
+
+ /*
+@@ -328,6 +341,9 @@ static void vc4_crtc_config_pv(struct dr
+ CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
+ CRTC_WRITE(PV_CONTROL, 0);
+
++ CRTC_WRITE(PV_MUX_CFG,
++ VC4_SET_FIELD(8, PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
++
+ CRTC_WRITE(PV_HORZA,
+ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+ PV_HORZA_HBP) |
+@@ -1115,10 +1131,72 @@ static const struct vc4_crtc_data bcm283
+ },
+ };
+
++static const struct vc4_crtc_data bcm2711_pv0_data = {
++ .debugfs_name = "crtc0_regs",
++ .hvs_available_channels = BIT(0),
++ .hvs_output = 0,
++ .fifo_depth = 64,
++ .pixels_per_clock = 1,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_DSI0,
++ [1] = VC4_ENCODER_TYPE_DPI,
++ },
++};
++
++static const struct vc4_crtc_data bcm2711_pv1_data = {
++ .debugfs_name = "crtc1_regs",
++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
++ .hvs_output = 3,
++ .fifo_depth = 64,
++ .pixels_per_clock = 1,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_DSI1,
++ [1] = VC4_ENCODER_TYPE_SMI,
++ },
++};
++
++static const struct vc4_crtc_data bcm2711_pv2_data = {
++ .debugfs_name = "crtc2_regs",
++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
++ .hvs_output = 4,
++ .fifo_depth = 256,
++ .pixels_per_clock = 2,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_HDMI0,
++ },
++};
++
++static const struct vc4_crtc_data bcm2711_pv3_data = {
++ .debugfs_name = "crtc3_regs",
++ .hvs_available_channels = BIT(1),
++ .hvs_output = 1,
++ .fifo_depth = 64,
++ .pixels_per_clock = 1,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_VEC,
++ },
++};
++
++static const struct vc4_crtc_data bcm2711_pv4_data = {
++ .debugfs_name = "crtc4_regs",
++ .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
++ .hvs_output = 5,
++ .fifo_depth = 64,
++ .pixels_per_clock = 2,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_HDMI1,
++ },
++};
++
+ static const struct of_device_id vc4_crtc_dt_match[] = {
+ { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data },
+ { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data },
+ { .compatible = "brcm,bcm2835-pixelvalve2", .data = &bcm2835_pv2_data },
++ { .compatible = "brcm,bcm2711-pixelvalve0", .data = &bcm2711_pv0_data },
++ { .compatible = "brcm,bcm2711-pixelvalve1", .data = &bcm2711_pv1_data },
++ { .compatible = "brcm,bcm2711-pixelvalve2", .data = &bcm2711_pv2_data },
++ { .compatible = "brcm,bcm2711-pixelvalve3", .data = &bcm2711_pv3_data },
++ { .compatible = "brcm,bcm2711-pixelvalve4", .data = &bcm2711_pv4_data },
+ {}
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -130,6 +130,8 @@
+ #define V3D_ERRSTAT 0x00f20
+
+ #define PV_CONTROL 0x00
++# define PV5_CONTROL_FIFO_LEVEL_HIGH_MASK VC4_MASK(26, 25)
++# define PV5_CONTROL_FIFO_LEVEL_HIGH_SHIFT 25
+ # define PV_CONTROL_FORMAT_MASK VC4_MASK(23, 21)
+ # define PV_CONTROL_FORMAT_SHIFT 21
+ # define PV_CONTROL_FORMAT_24 0
+@@ -209,6 +211,10 @@
+
+ #define PV_HACT_ACT 0x30
+
++#define PV_MUX_CFG 0x34
++# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_MASK VC4_MASK(5, 2)
++# define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_SHIFT 2
++
+ #define SCALER_CHANNELS_COUNT 3
+
+ #define SCALER_DISPCTRL 0x00000000
diff --git a/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch b/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch
new file mode 100644
index 0000000000..89d8aae28d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch
@@ -0,0 +1,30 @@
+From 26fbd25a3f99a85c09d5f1ed44980c506a3eac81 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 14 Jan 2020 17:24:32 +0100
+Subject: [PATCH] drm/vc4: hdmi: Use debugfs private field
+
+We're calling vc4_debugfs_add_file with our struct vc4_hdmi pointer set
+in the private field, but we don't use that field and go through the
+main struct vc4_dev to get it.
+
+Let's use the private field directly, that will save us some trouble
+later on.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -182,9 +182,7 @@ static const struct debugfs_reg32 hd_reg
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+ {
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+- struct drm_device *dev = node->minor->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *hdmi = node->info_ent->data;
+ struct drm_printer p = drm_seq_file_printer(m);
+
+ drm_print_regset32(&p, &hdmi->hdmi_regset);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch b/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch
new file mode 100644
index 0000000000..430fc0459e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch
@@ -0,0 +1,195 @@
+From 13bb65d33681b0095214033a5e80186faa325854 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 18 Dec 2019 18:35:12 +0100
+Subject: [PATCH] drm/vc4: hdmi: Move structure to header
+
+We will need to share the vc4_hdmi and related structures with multiple
+files, so let's create a header for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 76 +-----------------------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 86 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 87 insertions(+), 75 deletions(-)
+ create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.h
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -48,87 +48,13 @@
+ #include <sound/soc.h>
+ #include "media/cec.h"
+ #include "vc4_drv.h"
++#include "vc4_hdmi.h"
+ #include "vc4_regs.h"
+
+ #define HSM_CLOCK_FREQ 163682864
+ #define CEC_CLOCK_FREQ 40000
+ #define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
+
+-/* HDMI audio information */
+-struct vc4_hdmi_audio {
+- struct snd_soc_card card;
+- struct snd_soc_dai_link link;
+- struct snd_soc_dai_link_component cpu;
+- struct snd_soc_dai_link_component codec;
+- struct snd_soc_dai_link_component platform;
+- int samplerate;
+- int channels;
+- struct snd_dmaengine_dai_dma_data dma_data;
+- struct snd_pcm_substream *substream;
+-};
+-
+-/* General HDMI hardware state. */
+-struct vc4_hdmi {
+- struct platform_device *pdev;
+-
+- struct drm_encoder *encoder;
+- struct drm_connector *connector;
+-
+- struct vc4_hdmi_audio audio;
+-
+- struct i2c_adapter *ddc;
+- void __iomem *hdmicore_regs;
+- void __iomem *hd_regs;
+- int hpd_gpio;
+- bool hpd_active_low;
+-
+- struct cec_adapter *cec_adap;
+- struct cec_msg cec_rx_msg;
+- bool cec_tx_ok;
+- bool cec_irq_was_rx;
+-
+- struct clk *pixel_clock;
+- struct clk *hsm_clock;
+-
+- struct debugfs_regset32 hdmi_regset;
+- struct debugfs_regset32 hd_regset;
+-};
+-
+-#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
+-#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
+-#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
+-#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
+-
+-/* VC4 HDMI encoder KMS struct */
+-struct vc4_hdmi_encoder {
+- struct vc4_encoder base;
+- bool hdmi_monitor;
+- bool limited_rgb_range;
+-};
+-
+-static inline struct vc4_hdmi_encoder *
+-to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+-{
+- return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+-}
+-
+-/* VC4 HDMI connector KMS struct */
+-struct vc4_hdmi_connector {
+- struct drm_connector base;
+-
+- /* Since the connector is attached to just the one encoder,
+- * this is the reference to it so we can do the best_encoder()
+- * hook.
+- */
+- struct drm_encoder *encoder;
+-};
+-
+-static inline struct vc4_hdmi_connector *
+-to_vc4_hdmi_connector(struct drm_connector *connector)
+-{
+- return container_of(connector, struct vc4_hdmi_connector, base);
+-}
+-
+ static const struct debugfs_reg32 hdmi_regs[] = {
+ VC4_REG32(VC4_HDMI_CORE_REV),
+ VC4_REG32(VC4_HDMI_SW_RESET_CONTROL),
+--- /dev/null
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -0,0 +1,86 @@
++#ifndef _VC4_HDMI_H_
++#define _VC4_HDMI_H_
++
++#include <drm/drm_connector.h>
++#include <media/cec.h>
++#include <sound/dmaengine_pcm.h>
++#include <sound/soc.h>
++
++#include "vc4_drv.h"
++
++/* HDMI audio information */
++struct vc4_hdmi_audio {
++ struct snd_soc_card card;
++ struct snd_soc_dai_link link;
++ struct snd_soc_dai_link_component cpu;
++ struct snd_soc_dai_link_component codec;
++ struct snd_soc_dai_link_component platform;
++ int samplerate;
++ int channels;
++ struct snd_dmaengine_dai_dma_data dma_data;
++ struct snd_pcm_substream *substream;
++};
++
++/* General HDMI hardware state. */
++struct vc4_hdmi {
++ struct platform_device *pdev;
++
++ struct drm_encoder *encoder;
++ struct drm_connector *connector;
++
++ struct vc4_hdmi_audio audio;
++
++ struct i2c_adapter *ddc;
++ void __iomem *hdmicore_regs;
++ void __iomem *hd_regs;
++ int hpd_gpio;
++ bool hpd_active_low;
++
++ struct cec_adapter *cec_adap;
++ struct cec_msg cec_rx_msg;
++ bool cec_tx_ok;
++ bool cec_irq_was_rx;
++
++ struct clk *pixel_clock;
++ struct clk *hsm_clock;
++
++ struct debugfs_regset32 hdmi_regset;
++ struct debugfs_regset32 hd_regset;
++};
++
++#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
++#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
++#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
++#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
++
++/* VC4 HDMI encoder KMS struct */
++struct vc4_hdmi_encoder {
++ struct vc4_encoder base;
++ bool hdmi_monitor;
++ bool limited_rgb_range;
++};
++
++static inline struct vc4_hdmi_encoder *
++to_vc4_hdmi_encoder(struct drm_encoder *encoder)
++{
++ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
++}
++
++/* VC4 HDMI connector KMS struct */
++struct vc4_hdmi_connector {
++ struct drm_connector base;
++
++ /* Since the connector is attached to just the one encoder,
++ * this is the reference to it so we can do the best_encoder()
++ * hook.
++ */
++ struct drm_encoder *encoder;
++};
++
++static inline struct vc4_hdmi_connector *
++to_vc4_hdmi_connector(struct drm_connector *connector)
++{
++ return container_of(connector, struct vc4_hdmi_connector, base);
++}
++
++#endif /* _VC4_HDMI_H_ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch b/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch
new file mode 100644
index 0000000000..71125e3e25
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch
@@ -0,0 +1,348 @@
+From 50e7e810cf403c4b217c68c8ae2544d16f8063d1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 17:17:29 +0100
+Subject: [PATCH] drm/vc4: hdmi: rework connectors and encoders
+
+the vc4_hdmi driver has some custom structures to hold the data it needs to
+associate with the drm_encoder and drm_connector structures.
+
+However, it allocates them separately from the vc4_hdmi structure which
+makes it more complicated than it needs to be.
+
+Move those structures to be contained by vc4_hdmi and update the code
+accordingly.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 84 ++++++++++++++++------------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 64 +++++++++++++-------------
+ 2 files changed, 71 insertions(+), 77 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -190,19 +190,14 @@ static const struct drm_connector_helper
+ .get_modes = vc4_hdmi_connector_get_modes,
+ };
+
+-static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
+- struct drm_encoder *encoder)
++static int vc4_hdmi_connector_init(struct drm_device *dev,
++ struct vc4_hdmi *vc4_hdmi)
+ {
+- struct drm_connector *connector;
+- struct vc4_hdmi_connector *hdmi_connector;
++ struct vc4_hdmi_connector *hdmi_connector = &vc4_hdmi->connector;
++ struct drm_connector *connector = &hdmi_connector->base;
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ int ret;
+
+- hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
+- GFP_KERNEL);
+- if (!hdmi_connector)
+- return ERR_PTR(-ENOMEM);
+- connector = &hdmi_connector->base;
+-
+ hdmi_connector->encoder = encoder;
+
+ drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
+@@ -212,7 +207,7 @@ static struct drm_connector *vc4_hdmi_co
+ /* Create and attach TV margin props to this connector. */
+ ret = drm_mode_create_tv_margin_properties(dev);
+ if (ret)
+- return ERR_PTR(ret);
++ return ret;
+
+ drm_connector_attach_tv_margin_properties(connector);
+
+@@ -224,7 +219,7 @@ static struct drm_connector *vc4_hdmi_co
+
+ drm_connector_attach_encoder(connector, encoder);
+
+- return connector;
++ return 0;
+ }
+
+ static void vc4_hdmi_encoder_destroy(struct drm_encoder *encoder)
+@@ -303,21 +298,22 @@ static void vc4_hdmi_set_avi_infoframe(s
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ struct vc4_dev *vc4 = encoder->dev->dev_private;
+ struct vc4_hdmi *hdmi = vc4->hdmi;
+- struct drm_connector_state *cstate = hdmi->connector->state;
++ struct drm_connector *connector = &hdmi->connector.base;
++ struct drm_connector_state *cstate = connector->state;
+ struct drm_crtc *crtc = encoder->crtc;
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ union hdmi_infoframe frame;
+ int ret;
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
+- hdmi->connector, mode);
++ connector, mode);
+ if (ret < 0) {
+ DRM_ERROR("couldn't fill AVI infoframe\n");
+ return;
+ }
+
+ drm_hdmi_avi_infoframe_quant_range(&frame.avi,
+- hdmi->connector, mode,
++ connector, mode,
+ vc4_encoder->limited_rgb_range ?
+ HDMI_QUANTIZATION_RANGE_LIMITED :
+ HDMI_QUANTIZATION_RANGE_FULL);
+@@ -636,7 +632,8 @@ static const struct drm_encoder_helper_f
+ /* HDMI audio codec callbacks */
+ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi)
+ {
+- struct drm_device *drm = hdmi->encoder->dev;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ u32 hsm_clock = clk_get_rate(hdmi->hsm_clock);
+ unsigned long n, m;
+@@ -655,7 +652,7 @@ static void vc4_hdmi_audio_set_mai_clock
+
+ static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
+ {
+- struct drm_encoder *encoder = hdmi->encoder;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+@@ -693,7 +690,8 @@ static int vc4_hdmi_audio_startup(struct
+ struct snd_soc_dai *dai)
+ {
+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = hdmi->encoder;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct drm_connector *connector = &hdmi->connector.base;
+ struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
+ int ret;
+
+@@ -710,8 +708,7 @@ static int vc4_hdmi_audio_startup(struct
+ VC4_HDMI_RAM_PACKET_ENABLE))
+ return -ENODEV;
+
+- ret = snd_pcm_hw_constraint_eld(substream->runtime,
+- hdmi->connector->eld);
++ ret = snd_pcm_hw_constraint_eld(substream->runtime, connector->eld);
+ if (ret)
+ return ret;
+
+@@ -725,7 +722,7 @@ static int vc4_hdmi_audio_set_fmt(struct
+
+ static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi)
+ {
+- struct drm_encoder *encoder = hdmi->encoder;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+ struct device *dev = &hdmi->pdev->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+@@ -759,7 +756,7 @@ static int vc4_hdmi_audio_hw_params(stru
+ struct snd_soc_dai *dai)
+ {
+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = hdmi->encoder;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+ struct device *dev = &hdmi->pdev->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+@@ -832,7 +829,7 @@ static int vc4_hdmi_audio_trigger(struct
+ struct snd_soc_dai *dai)
+ {
+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = hdmi->encoder;
++ struct drm_encoder *encoder = &hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+
+@@ -876,9 +873,10 @@ static int vc4_hdmi_audio_eld_ctl_info(s
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
++ struct drm_connector *connector = &hdmi->connector.base;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+- uinfo->count = sizeof(hdmi->connector->eld);
++ uinfo->count = sizeof(connector->eld);
+
+ return 0;
+ }
+@@ -888,9 +886,10 @@ static int vc4_hdmi_audio_eld_ctl_get(st
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
++ struct drm_connector *connector = &hdmi->connector.base;
+
+- memcpy(ucontrol->value.bytes.data, hdmi->connector->eld,
+- sizeof(hdmi->connector->eld));
++ memcpy(ucontrol->value.bytes.data, connector->eld,
++ sizeof(connector->eld));
+
+ return 0;
+ }
+@@ -1230,7 +1229,7 @@ static int vc4_hdmi_bind(struct device *
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = drm->dev_private;
+ struct vc4_hdmi *hdmi;
+- struct vc4_hdmi_encoder *vc4_hdmi_encoder;
++ struct drm_encoder *encoder;
+ struct device_node *ddc_node;
+ u32 value;
+ int ret;
+@@ -1239,14 +1238,10 @@ static int vc4_hdmi_bind(struct device *
+ if (!hdmi)
+ return -ENOMEM;
+
+- vc4_hdmi_encoder = devm_kzalloc(dev, sizeof(*vc4_hdmi_encoder),
+- GFP_KERNEL);
+- if (!vc4_hdmi_encoder)
+- return -ENOMEM;
+- vc4_hdmi_encoder->base.type = VC4_ENCODER_TYPE_HDMI0;
+- hdmi->encoder = &vc4_hdmi_encoder->base.base;
+-
+ hdmi->pdev = pdev;
++ encoder = &hdmi->encoder.base.base;
++ encoder->base.type = VC4_ENCODER_TYPE_HDMI0;
++
+ hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(hdmi->hdmicore_regs))
+ return PTR_ERR(hdmi->hdmicore_regs);
+@@ -1332,15 +1327,14 @@ static int vc4_hdmi_bind(struct device *
+ }
+ pm_runtime_enable(dev);
+
+- drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
++ drm_encoder_init(drm, encoder, &vc4_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+- drm_encoder_helper_add(hdmi->encoder, &vc4_hdmi_encoder_helper_funcs);
++ drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
+
+- hdmi->connector = vc4_hdmi_connector_init(drm, hdmi->encoder);
+- if (IS_ERR(hdmi->connector)) {
+- ret = PTR_ERR(hdmi->connector);
++ ret = vc4_hdmi_connector_init(drm, hdmi);
++ if (ret)
+ goto err_destroy_encoder;
+- }
++
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+ hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+ vc4, "vc4",
+@@ -1350,7 +1344,7 @@ static int vc4_hdmi_bind(struct device *
+ if (ret < 0)
+ goto err_destroy_conn;
+
+- cec_fill_conn_info_from_drm(&conn_info, hdmi->connector);
++ cec_fill_conn_info_from_drm(&conn_info, &hdmi->connector.base);
+ cec_s_conn_info(hdmi->cec_adap, &conn_info);
+
+ HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+@@ -1387,10 +1381,10 @@ static int vc4_hdmi_bind(struct device *
+ err_delete_cec_adap:
+ cec_delete_adapter(hdmi->cec_adap);
+ err_destroy_conn:
+- vc4_hdmi_connector_destroy(hdmi->connector);
++ vc4_hdmi_connector_destroy(&hdmi->connector.base);
+ #endif
+ err_destroy_encoder:
+- vc4_hdmi_encoder_destroy(hdmi->encoder);
++ vc4_hdmi_encoder_destroy(encoder);
+ err_unprepare_hsm:
+ clk_disable_unprepare(hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+@@ -1408,8 +1402,8 @@ static void vc4_hdmi_unbind(struct devic
+ struct vc4_hdmi *hdmi = vc4->hdmi;
+
+ cec_unregister_adapter(hdmi->cec_adap);
+- vc4_hdmi_connector_destroy(hdmi->connector);
+- vc4_hdmi_encoder_destroy(hdmi->encoder);
++ vc4_hdmi_connector_destroy(&hdmi->connector.base);
++ vc4_hdmi_encoder_destroy(&hdmi->encoder.base.base);
+
+ clk_disable_unprepare(hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -8,6 +8,36 @@
+
+ #include "vc4_drv.h"
+
++/* VC4 HDMI encoder KMS struct */
++struct vc4_hdmi_encoder {
++ struct vc4_encoder base;
++ bool hdmi_monitor;
++ bool limited_rgb_range;
++};
++
++static inline struct vc4_hdmi_encoder *
++to_vc4_hdmi_encoder(struct drm_encoder *encoder)
++{
++ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
++}
++
++/* VC4 HDMI connector KMS struct */
++struct vc4_hdmi_connector {
++ struct drm_connector base;
++
++ /* Since the connector is attached to just the one encoder,
++ * this is the reference to it so we can do the best_encoder()
++ * hook.
++ */
++ struct drm_encoder *encoder;
++};
++
++static inline struct vc4_hdmi_connector *
++to_vc4_hdmi_connector(struct drm_connector *connector)
++{
++ return container_of(connector, struct vc4_hdmi_connector, base);
++}
++
+ /* HDMI audio information */
+ struct vc4_hdmi_audio {
+ struct snd_soc_card card;
+@@ -25,8 +55,8 @@ struct vc4_hdmi_audio {
+ struct vc4_hdmi {
+ struct platform_device *pdev;
+
+- struct drm_encoder *encoder;
+- struct drm_connector *connector;
++ struct vc4_hdmi_encoder encoder;
++ struct vc4_hdmi_connector connector;
+
+ struct vc4_hdmi_audio audio;
+
+@@ -53,34 +83,4 @@ struct vc4_hdmi {
+ #define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
+ #define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
+
+-/* VC4 HDMI encoder KMS struct */
+-struct vc4_hdmi_encoder {
+- struct vc4_encoder base;
+- bool hdmi_monitor;
+- bool limited_rgb_range;
+-};
+-
+-static inline struct vc4_hdmi_encoder *
+-to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+-{
+- return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+-}
+-
+-/* VC4 HDMI connector KMS struct */
+-struct vc4_hdmi_connector {
+- struct drm_connector base;
+-
+- /* Since the connector is attached to just the one encoder,
+- * this is the reference to it so we can do the best_encoder()
+- * hook.
+- */
+- struct drm_encoder *encoder;
+-};
+-
+-static inline struct vc4_hdmi_connector *
+-to_vc4_hdmi_connector(struct drm_connector *connector)
+-{
+- return container_of(connector, struct vc4_hdmi_connector, base);
+-}
+-
+ #endif /* _VC4_HDMI_H_ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch b/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch
new file mode 100644
index 0000000000..75c44a564a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch
@@ -0,0 +1,682 @@
+From 02b7a6ed6b9fc110dd26598d26c31c0837af6184 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:07:05 +0100
+Subject: [PATCH] drm/vc4: hdmi: Rename hdmi to vc4_hdmi
+
+The driver isn't consistent with the name given to the vc4_hdmi
+structure pointer in its functions. Make sure to use a consistent name.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 271 +++++++++++++++++----------------
+ 1 file changed, 136 insertions(+), 135 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -108,11 +108,11 @@ static const struct debugfs_reg32 hd_reg
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+ {
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+- struct vc4_hdmi *hdmi = node->info_ent->data;
++ struct vc4_hdmi *vc4_hdmi = node->info_ent->data;
+ struct drm_printer p = drm_seq_file_printer(m);
+
+- drm_print_regset32(&p, &hdmi->hdmi_regset);
+- drm_print_regset32(&p, &hdmi->hd_regset);
++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
++ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+
+ return 0;
+ }
+@@ -297,8 +297,8 @@ static void vc4_hdmi_set_avi_infoframe(s
+ {
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ struct vc4_dev *vc4 = encoder->dev->dev_private;
+- struct vc4_hdmi *hdmi = vc4->hdmi;
+- struct drm_connector *connector = &hdmi->connector.base;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct drm_connector *connector = &vc4_hdmi->connector.base;
+ struct drm_connector_state *cstate = connector->state;
+ struct drm_crtc *crtc = encoder->crtc;
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+@@ -346,7 +346,7 @@ static void vc4_hdmi_set_audio_infoframe
+ {
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = drm->dev_private;
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ union hdmi_infoframe frame;
+ int ret;
+
+@@ -355,7 +355,7 @@ static void vc4_hdmi_set_audio_infoframe
+ frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+ frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+- frame.audio.channels = hdmi->audio.channels;
++ frame.audio.channels = vc4_hdmi->audio.channels;
+
+ vc4_hdmi_write_infoframe(encoder, &frame);
+ }
+@@ -370,7 +370,7 @@ static void vc4_hdmi_encoder_disable(str
+ {
+ struct drm_device *dev = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ int ret;
+
+ HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+@@ -379,9 +379,9 @@ static void vc4_hdmi_encoder_disable(str
+ HD_WRITE(VC4_HD_VID_CTL,
+ HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+- clk_disable_unprepare(hdmi->pixel_clock);
++ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+
+- ret = pm_runtime_put(&hdmi->pdev->dev);
++ ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
+ if (ret < 0)
+ DRM_ERROR("Failed to release power domain: %d\n", ret);
+ }
+@@ -392,7 +392,7 @@ static void vc4_hdmi_encoder_enable(stru
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ bool debug_dump_regs = false;
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+@@ -414,13 +414,13 @@ static void vc4_hdmi_encoder_enable(stru
+ u32 csc_ctl;
+ int ret;
+
+- ret = pm_runtime_get_sync(&hdmi->pdev->dev);
++ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev);
+ if (ret < 0) {
+ DRM_ERROR("Failed to retain power domain: %d\n", ret);
+ return;
+ }
+
+- ret = clk_set_rate(hdmi->pixel_clock,
++ ret = clk_set_rate(vc4_hdmi->pixel_clock,
+ mode->clock * 1000 *
+ ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
+ if (ret) {
+@@ -428,7 +428,7 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
+- ret = clk_prepare_enable(hdmi->pixel_clock);
++ ret = clk_prepare_enable(vc4_hdmi->pixel_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+ return;
+@@ -448,11 +448,11 @@ static void vc4_hdmi_encoder_enable(stru
+ HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
+
+ if (debug_dump_regs) {
+- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev);
++ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev);
+
+- dev_info(&hdmi->pdev->dev, "HDMI regs before:\n");
+- drm_print_regset32(&p, &hdmi->hdmi_regset);
+- drm_print_regset32(&p, &hdmi->hd_regset);
++ dev_info(&vc4_hdmi->pdev->dev, "HDMI regs before:\n");
++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
++ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+ }
+
+ HD_WRITE(VC4_HD_VID_CTL, 0);
+@@ -527,11 +527,11 @@ static void vc4_hdmi_encoder_enable(stru
+ HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+
+ if (debug_dump_regs) {
+- struct drm_printer p = drm_info_printer(&hdmi->pdev->dev);
++ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev);
+
+- dev_info(&hdmi->pdev->dev, "HDMI regs after:\n");
+- drm_print_regset32(&p, &hdmi->hdmi_regset);
+- drm_print_regset32(&p, &hdmi->hd_regset);
++ dev_info(&vc4_hdmi->pdev->dev, "HDMI regs after:\n");
++ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
++ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+ }
+
+ HD_WRITE(VC4_HD_VID_CTL,
+@@ -630,15 +630,15 @@ static const struct drm_encoder_helper_f
+ };
+
+ /* HDMI audio codec callbacks */
+-static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi)
++static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+- u32 hsm_clock = clk_get_rate(hdmi->hsm_clock);
++ u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock);
+ unsigned long n, m;
+
+- rational_best_approximation(hsm_clock, hdmi->audio.samplerate,
++ rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate,
+ VC4_HD_MAI_SMP_N_MASK >>
+ VC4_HD_MAI_SMP_N_SHIFT,
+ (VC4_HD_MAI_SMP_M_MASK >>
+@@ -650,14 +650,14 @@ static void vc4_hdmi_audio_set_mai_clock
+ VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
+ }
+
+-static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi)
++static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+- u32 samplerate = hdmi->audio.samplerate;
++ u32 samplerate = vc4_hdmi->audio.samplerate;
+ u32 n, cts;
+ u64 tmp;
+
+@@ -689,16 +689,16 @@ static inline struct vc4_hdmi *dai_to_hd
+ static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+ {
+- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
+- struct drm_connector *connector = &hdmi->connector.base;
++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
++ struct drm_connector *connector = &vc4_hdmi->connector.base;
+ struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
+ int ret;
+
+- if (hdmi->audio.substream && hdmi->audio.substream != substream)
++ if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream)
+ return -EINVAL;
+
+- hdmi->audio.substream = substream;
++ vc4_hdmi->audio.substream = substream;
+
+ /*
+ * If the HDMI encoder hasn't probed, or the encoder is
+@@ -720,11 +720,11 @@ static int vc4_hdmi_audio_set_fmt(struct
+ return 0;
+ }
+
+-static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi)
++static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+- struct device *dev = &hdmi->pdev->dev;
++ struct device *dev = &vc4_hdmi->pdev->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ int ret;
+
+@@ -740,14 +740,14 @@ static void vc4_hdmi_audio_reset(struct
+ static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+ {
+- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+
+- if (substream != hdmi->audio.substream)
++ if (substream != vc4_hdmi->audio.substream)
+ return;
+
+- vc4_hdmi_audio_reset(hdmi);
++ vc4_hdmi_audio_reset(vc4_hdmi);
+
+- hdmi->audio.substream = NULL;
++ vc4_hdmi->audio.substream = NULL;
+ }
+
+ /* HDMI audio codec callbacks */
+@@ -755,23 +755,23 @@ static int vc4_hdmi_audio_hw_params(stru
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+ {
+- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+- struct device *dev = &hdmi->pdev->dev;
++ struct device *dev = &vc4_hdmi->pdev->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ u32 audio_packet_config, channel_mask;
+ u32 channel_map, i;
+
+- if (substream != hdmi->audio.substream)
++ if (substream != vc4_hdmi->audio.substream)
+ return -EINVAL;
+
+ dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
+ params_rate(params), params_width(params),
+ params_channels(params));
+
+- hdmi->audio.channels = params_channels(params);
+- hdmi->audio.samplerate = params_rate(params);
++ vc4_hdmi->audio.channels = params_channels(params);
++ vc4_hdmi->audio.samplerate = params_rate(params);
+
+ HD_WRITE(VC4_HD_MAI_CTL,
+ VC4_HD_MAI_CTL_RESET |
+@@ -780,23 +780,23 @@ static int vc4_hdmi_audio_hw_params(stru
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
+
+- vc4_hdmi_audio_set_mai_clock(hdmi);
++ vc4_hdmi_audio_set_mai_clock(vc4_hdmi);
+
+ audio_packet_config =
+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT |
+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
+ VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
+
+- channel_mask = GENMASK(hdmi->audio.channels - 1, 0);
++ channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0);
+ audio_packet_config |= VC4_SET_FIELD(channel_mask,
+ VC4_HDMI_AUDIO_PACKET_CEA_MASK);
+
+ /* Set the MAI threshold. This logic mimics the firmware's. */
+- if (hdmi->audio.samplerate > 96000) {
++ if (vc4_hdmi->audio.samplerate > 96000) {
+ HD_WRITE(VC4_HD_MAI_THR,
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+- } else if (hdmi->audio.samplerate > 48000) {
++ } else if (vc4_hdmi->audio.samplerate > 48000) {
+ HD_WRITE(VC4_HD_MAI_THR,
+ VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+@@ -820,7 +820,7 @@ static int vc4_hdmi_audio_hw_params(stru
+
+ HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map);
+ HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
+- vc4_hdmi_set_n_cts(hdmi);
++ vc4_hdmi_set_n_cts(vc4_hdmi);
+
+ return 0;
+ }
+@@ -828,8 +828,8 @@ static int vc4_hdmi_audio_hw_params(stru
+ static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+ {
+- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = &hdmi->encoder.base.base;
++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
++ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_device *drm = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+
+@@ -840,7 +840,7 @@ static int vc4_hdmi_audio_trigger(struct
+ HDMI_READ(VC4_HDMI_TX_PHY_CTL0) &
+ ~VC4_HDMI_TX_PHY_RNG_PWRDN);
+ HD_WRITE(VC4_HD_MAI_CTL,
+- VC4_SET_FIELD(hdmi->audio.channels,
++ VC4_SET_FIELD(vc4_hdmi->audio.channels,
+ VC4_HD_MAI_CTL_CHNUM) |
+ VC4_HD_MAI_CTL_ENABLE);
+ break;
+@@ -872,8 +872,8 @@ static int vc4_hdmi_audio_eld_ctl_info(s
+ struct snd_ctl_elem_info *uinfo)
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
+- struct drm_connector *connector = &hdmi->connector.base;
++ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
++ struct drm_connector *connector = &vc4_hdmi->connector.base;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(connector->eld);
+@@ -885,8 +885,8 @@ static int vc4_hdmi_audio_eld_ctl_get(st
+ struct snd_ctl_elem_value *ucontrol)
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+- struct vc4_hdmi *hdmi = snd_component_to_hdmi(component);
+- struct drm_connector *connector = &hdmi->connector.base;
++ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
++ struct drm_connector *connector = &vc4_hdmi->connector.base;
+
+ memcpy(ucontrol->value.bytes.data, connector->eld,
+ sizeof(connector->eld));
+@@ -954,9 +954,9 @@ static const struct snd_soc_component_dr
+
+ static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai)
+ {
+- struct vc4_hdmi *hdmi = dai_to_hdmi(dai);
++ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+
+- snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL);
++ snd_soc_dai_init_dma_data(dai, &vc4_hdmi->audio.dma_data, NULL);
+
+ return 0;
+ }
+@@ -982,11 +982,11 @@ static const struct snd_dmaengine_pcm_co
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ };
+
+-static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi)
++static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct snd_soc_dai_link *dai_link = &hdmi->audio.link;
+- struct snd_soc_card *card = &hdmi->audio.card;
+- struct device *dev = &hdmi->pdev->dev;
++ struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link;
++ struct snd_soc_card *card = &vc4_hdmi->audio.card;
++ struct device *dev = &vc4_hdmi->pdev->dev;
+ const __be32 *addr;
+ int ret;
+ int len;
+@@ -1006,9 +1006,9 @@ static int vc4_hdmi_audio_init(struct vc
+ * This VC/MMU should probably be exposed to avoid this kind of hacks.
+ */
+ addr = of_get_address(dev->of_node, 1, NULL, NULL);
+- hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA;
+- hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+- hdmi->audio.dma_data.maxburst = 2;
++ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA;
++ vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ vc4_hdmi->audio.dma_data.maxburst = 2;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0);
+ if (ret) {
+@@ -1031,9 +1031,9 @@ static int vc4_hdmi_audio_init(struct vc
+ return ret;
+ }
+
+- dai_link->cpus = &hdmi->audio.cpu;
+- dai_link->codecs = &hdmi->audio.codec;
+- dai_link->platforms = &hdmi->audio.platform;
++ dai_link->cpus = &vc4_hdmi->audio.cpu;
++ dai_link->codecs = &vc4_hdmi->audio.codec;
++ dai_link->platforms = &vc4_hdmi->audio.platform;
+
+ dai_link->num_cpus = 1;
+ dai_link->num_codecs = 1;
+@@ -1058,7 +1058,7 @@ static int vc4_hdmi_audio_init(struct vc
+ * now stored in card->drvdata and should be retrieved with
+ * snd_soc_card_get_drvdata() if needed.
+ */
+- snd_soc_card_set_drvdata(card, hdmi);
++ snd_soc_card_set_drvdata(card, vc4_hdmi);
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ dev_err(dev, "Could not register sound card: %d\n", ret);
+@@ -1071,20 +1071,21 @@ static int vc4_hdmi_audio_init(struct vc
+ static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+ {
+ struct vc4_dev *vc4 = priv;
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+
+- if (hdmi->cec_irq_was_rx) {
+- if (hdmi->cec_rx_msg.len)
+- cec_received_msg(hdmi->cec_adap, &hdmi->cec_rx_msg);
+- } else if (hdmi->cec_tx_ok) {
+- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_OK,
++ if (vc4_hdmi->cec_irq_was_rx) {
++ if (vc4_hdmi->cec_rx_msg.len)
++ cec_received_msg(vc4_hdmi->cec_adap,
++ &vc4_hdmi->cec_rx_msg);
++ } else if (vc4_hdmi->cec_tx_ok) {
++ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
+ 0, 0, 0, 0);
+ } else {
+ /*
+ * This CEC implementation makes 1 retry, so if we
+ * get a NACK, then that means it made 2 attempts.
+ */
+- cec_transmit_done(hdmi->cec_adap, CEC_TX_STATUS_NACK,
++ cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK,
+ 0, 2, 0, 0);
+ }
+ return IRQ_HANDLED;
+@@ -1110,23 +1111,23 @@ static void vc4_cec_read_msg(struct vc4_
+ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+ {
+ struct vc4_dev *vc4 = priv;
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
+ u32 cntrl1, cntrl5;
+
+ if (!(stat & VC4_HDMI_CPU_CEC))
+ return IRQ_NONE;
+- hdmi->cec_rx_msg.len = 0;
++ vc4_hdmi->cec_rx_msg.len = 0;
+ cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+ cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+- hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+- if (hdmi->cec_irq_was_rx) {
++ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
++ if (vc4_hdmi->cec_irq_was_rx) {
+ vc4_cec_read_msg(vc4, cntrl1);
+ cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+ HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+ cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+ } else {
+- hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
++ vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+ cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+ }
+ HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+@@ -1228,44 +1229,44 @@ static int vc4_hdmi_bind(struct device *
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = drm->dev_private;
+- struct vc4_hdmi *hdmi;
++ struct vc4_hdmi *vc4_hdmi;
+ struct drm_encoder *encoder;
+ struct device_node *ddc_node;
+ u32 value;
+ int ret;
+
+- hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+- if (!hdmi)
++ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
++ if (!vc4_hdmi)
+ return -ENOMEM;
+
+- hdmi->pdev = pdev;
+- encoder = &hdmi->encoder.base.base;
++ vc4_hdmi->pdev = pdev;
++ encoder = &vc4_hdmi->encoder.base.base;
+ encoder->base.type = VC4_ENCODER_TYPE_HDMI0;
+
+- hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+- if (IS_ERR(hdmi->hdmicore_regs))
+- return PTR_ERR(hdmi->hdmicore_regs);
+-
+- hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+- if (IS_ERR(hdmi->hd_regs))
+- return PTR_ERR(hdmi->hd_regs);
+-
+- hdmi->hdmi_regset.base = hdmi->hdmicore_regs;
+- hdmi->hdmi_regset.regs = hdmi_regs;
+- hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
+- hdmi->hd_regset.base = hdmi->hd_regs;
+- hdmi->hd_regset.regs = hd_regs;
+- hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs);
++ vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
++ if (IS_ERR(vc4_hdmi->hdmicore_regs))
++ return PTR_ERR(vc4_hdmi->hdmicore_regs);
++
++ vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
++ if (IS_ERR(vc4_hdmi->hd_regs))
++ return PTR_ERR(vc4_hdmi->hd_regs);
++
++ vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs;
++ vc4_hdmi->hdmi_regset.regs = hdmi_regs;
++ vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
++ vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs;
++ vc4_hdmi->hd_regset.regs = hd_regs;
++ vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs);
+
+- hdmi->pixel_clock = devm_clk_get(dev, "pixel");
+- if (IS_ERR(hdmi->pixel_clock)) {
++ vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel");
++ if (IS_ERR(vc4_hdmi->pixel_clock)) {
+ DRM_ERROR("Failed to get pixel clock\n");
+- return PTR_ERR(hdmi->pixel_clock);
++ return PTR_ERR(vc4_hdmi->pixel_clock);
+ }
+- hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
+- if (IS_ERR(hdmi->hsm_clock)) {
++ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
++ if (IS_ERR(vc4_hdmi->hsm_clock)) {
+ DRM_ERROR("Failed to get HDMI state machine clock\n");
+- return PTR_ERR(hdmi->hsm_clock);
++ return PTR_ERR(vc4_hdmi->hsm_clock);
+ }
+
+ ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
+@@ -1274,9 +1275,9 @@ static int vc4_hdmi_bind(struct device *
+ return -ENODEV;
+ }
+
+- hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
++ vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+ of_node_put(ddc_node);
+- if (!hdmi->ddc) {
++ if (!vc4_hdmi->ddc) {
+ DRM_DEBUG("Failed to get ddc i2c adapter by node\n");
+ return -EPROBE_DEFER;
+ }
+@@ -1285,13 +1286,13 @@ static int vc4_hdmi_bind(struct device *
+ * needs to be a bit higher than the pixel clock rate
+ * (generally 148.5Mhz).
+ */
+- ret = clk_set_rate(hdmi->hsm_clock, HSM_CLOCK_FREQ);
++ ret = clk_set_rate(vc4_hdmi->hsm_clock, HSM_CLOCK_FREQ);
+ if (ret) {
+ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+ goto err_put_i2c;
+ }
+
+- ret = clk_prepare_enable(hdmi->hsm_clock);
++ ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+ ret);
+@@ -1304,18 +1305,18 @@ static int vc4_hdmi_bind(struct device *
+ if (of_find_property(dev->of_node, "hpd-gpios", &value)) {
+ enum of_gpio_flags hpd_gpio_flags;
+
+- hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
++ vc4_hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
+ "hpd-gpios", 0,
+ &hpd_gpio_flags);
+- if (hdmi->hpd_gpio < 0) {
+- ret = hdmi->hpd_gpio;
++ if (vc4_hdmi->hpd_gpio < 0) {
++ ret = vc4_hdmi->hpd_gpio;
+ goto err_unprepare_hsm;
+ }
+
+- hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
++ vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
+ }
+
+- vc4->hdmi = hdmi;
++ vc4->hdmi = vc4_hdmi;
+
+ /* HDMI core must be enabled. */
+ if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
+@@ -1331,21 +1332,21 @@ static int vc4_hdmi_bind(struct device *
+ DRM_MODE_ENCODER_TMDS, NULL);
+ drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
+
+- ret = vc4_hdmi_connector_init(drm, hdmi);
++ ret = vc4_hdmi_connector_init(drm, vc4_hdmi);
+ if (ret)
+ goto err_destroy_encoder;
+
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+- hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
++ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+ vc4, "vc4",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO, 1);
+- ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
++ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
+ if (ret < 0)
+ goto err_destroy_conn;
+
+- cec_fill_conn_info_from_drm(&conn_info, &hdmi->connector.base);
+- cec_s_conn_info(hdmi->cec_adap, &conn_info);
++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector.base);
++ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+
+ HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+ value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+@@ -1364,32 +1365,32 @@ static int vc4_hdmi_bind(struct device *
+ "vc4 hdmi cec", vc4);
+ if (ret)
+ goto err_delete_cec_adap;
+- ret = cec_register_adapter(hdmi->cec_adap, dev);
++ ret = cec_register_adapter(vc4_hdmi->cec_adap, dev);
+ if (ret < 0)
+ goto err_delete_cec_adap;
+ #endif
+
+- ret = vc4_hdmi_audio_init(hdmi);
++ ret = vc4_hdmi_audio_init(vc4_hdmi);
+ if (ret)
+ goto err_destroy_encoder;
+
+- vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, hdmi);
++ vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, vc4_hdmi);
+
+ return 0;
+
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+ err_delete_cec_adap:
+- cec_delete_adapter(hdmi->cec_adap);
++ cec_delete_adapter(vc4_hdmi->cec_adap);
+ err_destroy_conn:
+- vc4_hdmi_connector_destroy(&hdmi->connector.base);
++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base);
+ #endif
+ err_destroy_encoder:
+ vc4_hdmi_encoder_destroy(encoder);
+ err_unprepare_hsm:
+- clk_disable_unprepare(hdmi->hsm_clock);
++ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+ err_put_i2c:
+- put_device(&hdmi->ddc->dev);
++ put_device(&vc4_hdmi->ddc->dev);
+
+ return ret;
+ }
+@@ -1399,16 +1400,16 @@ static void vc4_hdmi_unbind(struct devic
+ {
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = drm->dev_private;
+- struct vc4_hdmi *hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+
+- cec_unregister_adapter(hdmi->cec_adap);
+- vc4_hdmi_connector_destroy(&hdmi->connector.base);
+- vc4_hdmi_encoder_destroy(&hdmi->encoder.base.base);
++ cec_unregister_adapter(vc4_hdmi->cec_adap);
++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base);
++ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base);
+
+- clk_disable_unprepare(hdmi->hsm_clock);
++ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+
+- put_device(&hdmi->ddc->dev);
++ put_device(&vc4_hdmi->ddc->dev);
+
+ vc4->hdmi = NULL;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch b/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch
new file mode 100644
index 0000000000..53296ecb4b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch
@@ -0,0 +1,152 @@
+From d1ced662ff5ed90a489b6610144d480bfd7a64e9 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:21:44 +0100
+Subject: [PATCH] drm/vc4: hdmi: Move accessors to vc4_hdmi
+
+The current driver only supports a single HDMI controller, and part of
+the issue is that the main vc4_dev structure holds a pointer to its
+(only) HDMI controller, and the HDMI registers accessors will use it to
+retrieve the mapped addresses.
+
+Let's modify those accessors to use directly the vc4_hdmi structure so
+that we can eventually get rid of that single global pointer.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 22 ++++++++--------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 8 ++++----
+ 2 files changed, 12 insertions(+), 18 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -122,6 +122,7 @@ vc4_hdmi_connector_detect(struct drm_con
+ {
+ struct drm_device *dev = connector->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+
+ if (vc4->hdmi->hpd_gpio) {
+ if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
+@@ -236,6 +237,7 @@ static int vc4_hdmi_stop_packet(struct d
+ {
+ struct drm_device *dev = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ u32 packet_id = type - 0x80;
+
+ HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+@@ -250,6 +252,7 @@ static void vc4_hdmi_write_infoframe(str
+ {
+ struct drm_device *dev = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ u32 packet_id = frame->any.type - 0x80;
+ u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id);
+ uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+@@ -632,9 +635,6 @@ static const struct drm_encoder_helper_f
+ /* HDMI audio codec callbacks */
+ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi)
+ {
+- struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+- struct drm_device *drm = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock);
+ unsigned long n, m;
+
+@@ -654,8 +654,6 @@ static void vc4_hdmi_set_n_cts(struct vc
+ {
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_crtc *crtc = encoder->crtc;
+- struct drm_device *drm = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ u32 samplerate = vc4_hdmi->audio.samplerate;
+ u32 n, cts;
+@@ -692,7 +690,6 @@ static int vc4_hdmi_audio_startup(struct
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ struct drm_connector *connector = &vc4_hdmi->connector.base;
+- struct vc4_dev *vc4 = to_vc4_dev(encoder->dev);
+ int ret;
+
+ if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream)
+@@ -723,9 +720,7 @@ static int vc4_hdmi_audio_set_fmt(struct
+ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
+ {
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+- struct drm_device *drm = encoder->dev;
+ struct device *dev = &vc4_hdmi->pdev->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ int ret;
+
+ ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO);
+@@ -756,10 +751,7 @@ static int vc4_hdmi_audio_hw_params(stru
+ struct snd_soc_dai *dai)
+ {
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+- struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+- struct drm_device *drm = encoder->dev;
+ struct device *dev = &vc4_hdmi->pdev->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+ u32 audio_packet_config, channel_mask;
+ u32 channel_map, i;
+
+@@ -830,8 +822,6 @@ static int vc4_hdmi_audio_trigger(struct
+ {
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+- struct drm_device *drm = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+@@ -1093,7 +1083,8 @@ static irqreturn_t vc4_cec_irq_handler_t
+
+ static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
+ {
+- struct cec_msg *msg = &vc4->hdmi->cec_rx_msg;
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct cec_msg *msg = &vc4_hdmi->cec_rx_msg;
+ unsigned int i;
+
+ msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+@@ -1139,6 +1130,7 @@ static irqreturn_t vc4_cec_irq_handler(i
+ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+ {
+ struct vc4_dev *vc4 = cec_get_drvdata(adap);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ /* clock period in microseconds */
+ const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
+ u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+@@ -1182,6 +1174,7 @@ static int vc4_hdmi_cec_adap_enable(stru
+ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+ {
+ struct vc4_dev *vc4 = cec_get_drvdata(adap);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+
+ HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
+ (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+@@ -1193,6 +1186,7 @@ static int vc4_hdmi_cec_adap_transmit(st
+ u32 signal_free_time, struct cec_msg *msg)
+ {
+ struct vc4_dev *vc4 = cec_get_drvdata(adap);
++ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ u32 val;
+ unsigned int i;
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -78,9 +78,9 @@ struct vc4_hdmi {
+ struct debugfs_regset32 hd_regset;
+ };
+
+-#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
+-#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
+-#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
+-#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
++#define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset)
++#define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset)
++#define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset)
++#define HD_WRITE(offset, val) writel(val, vc4_hdmi->hd_regs + offset)
+
+ #endif /* _VC4_HDMI_H_ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch b/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch
new file mode 100644
index 0000000000..9b60fbb72b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch
@@ -0,0 +1,45 @@
+From 985efd0f9da3d2b60e34d10efee969e4dfd85a12 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:44:36 +0100
+Subject: [PATCH] drm/vc4: hdmi: Use local vc4_hdmi directly
+
+The function vc4_hdmi_connector_detect access its vc4_hdmi struct by
+dereferencing the pointer in the structure vc4_dev. This will cause some
+issues when we will have multiple HDMI controllers, so let's just use the
+local variable for now instead of dereferencing that pointer all the time,
+and we'll fix the local variable later.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -124,20 +124,20 @@ vc4_hdmi_connector_detect(struct drm_con
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+
+- if (vc4->hdmi->hpd_gpio) {
+- if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
+- vc4->hdmi->hpd_active_low)
++ if (vc4_hdmi->hpd_gpio) {
++ if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
++ vc4_hdmi->hpd_active_low)
+ return connector_status_connected;
+- cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
++ cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
+ return connector_status_disconnected;
+ }
+
+- if (drm_probe_ddc(vc4->hdmi->ddc))
++ if (drm_probe_ddc(vc4_hdmi->ddc))
+ return connector_status_connected;
+
+ if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
+ return connector_status_connected;
+- cec_phys_addr_invalidate(vc4->hdmi->cec_adap);
++ cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
+ return connector_status_disconnected;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch b/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch
new file mode 100644
index 0000000000..1d70ca851c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch
@@ -0,0 +1,151 @@
+From fe19f02dbfd020df9b028cf2c580417c4edc31b3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:45:46 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add container_of macros for encoders
+ and connectors
+
+Whenever the code needs to access the vc4_hdmi structure from a DRM
+connector or encoder, it first accesses the drm_device associated to the
+connector, then retrieve the drm_dev private data which gives it a
+pointer to our vc4_dev, and will finally follow the vc4_hdmi pointer in
+that structure.
+
+That will also give us some trouble when having multiple controllers,
+but now that we have our encoder and connector structures that are part
+of vc4_hdmi, we can simply call container_of on the DRM connector or
+encoder and retrieve the vc4_hdmi structure directly.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 41 ++++++++++------------------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 16 +++++++++++++
+ 2 files changed, 28 insertions(+), 29 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -120,9 +120,7 @@ static int vc4_hdmi_debugfs_regs(struct
+ static enum drm_connector_status
+ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
+ {
+- struct drm_device *dev = connector->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+
+ if (vc4_hdmi->hpd_gpio) {
+ if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^
+@@ -149,17 +147,13 @@ static void vc4_hdmi_connector_destroy(s
+
+ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
+ {
+- struct vc4_hdmi_connector *vc4_connector =
+- to_vc4_hdmi_connector(connector);
+- struct drm_encoder *encoder = vc4_connector->encoder;
+- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+- struct drm_device *dev = connector->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
++ struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder;
+ int ret = 0;
+ struct edid *edid;
+
+- edid = drm_get_edid(connector, vc4->hdmi->ddc);
+- cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
++ edid = drm_get_edid(connector, vc4_hdmi->ddc);
++ cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
+ if (!edid)
+ return -ENODEV;
+
+@@ -235,9 +229,7 @@ static const struct drm_encoder_funcs vc
+ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
+ enum hdmi_infoframe_type type)
+ {
+- struct drm_device *dev = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ u32 packet_id = type - 0x80;
+
+ HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+@@ -250,9 +242,7 @@ static int vc4_hdmi_stop_packet(struct d
+ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
+ union hdmi_infoframe *frame)
+ {
+- struct drm_device *dev = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ u32 packet_id = frame->any.type - 0x80;
+ u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id);
+ uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+@@ -298,9 +288,8 @@ static void vc4_hdmi_write_infoframe(str
+
+ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+ {
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+- struct vc4_dev *vc4 = encoder->dev->dev_private;
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ struct drm_connector *connector = &vc4_hdmi->connector.base;
+ struct drm_connector_state *cstate = connector->state;
+ struct drm_crtc *crtc = encoder->crtc;
+@@ -347,9 +336,7 @@ static void vc4_hdmi_set_spd_infoframe(s
+
+ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder)
+ {
+- struct drm_device *drm = encoder->dev;
+- struct vc4_dev *vc4 = drm->dev_private;
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ union hdmi_infoframe frame;
+ int ret;
+
+@@ -371,9 +358,7 @@ static void vc4_hdmi_set_infoframes(stru
+
+ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+ {
+- struct drm_device *dev = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ int ret;
+
+ HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+@@ -392,10 +377,8 @@ static void vc4_hdmi_encoder_disable(str
+ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+ {
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+- struct drm_device *dev = encoder->dev;
+- struct vc4_dev *vc4 = to_vc4_dev(dev);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder;
+ bool debug_dump_regs = false;
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -78,6 +78,22 @@ struct vc4_hdmi {
+ struct debugfs_regset32 hd_regset;
+ };
+
++static inline struct vc4_hdmi *
++connector_to_vc4_hdmi(struct drm_connector *connector)
++{
++ struct vc4_hdmi_connector *_connector = to_vc4_hdmi_connector(connector);
++
++ return container_of(_connector, struct vc4_hdmi, connector);
++}
++
++static inline struct vc4_hdmi *
++encoder_to_vc4_hdmi(struct drm_encoder *encoder)
++{
++ struct vc4_hdmi_encoder *_encoder = to_vc4_hdmi_encoder(encoder);
++
++ return container_of(_encoder, struct vc4_hdmi, encoder);
++}
++
+ #define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset)
+ #define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset)
+ #define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch b/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch
new file mode 100644
index 0000000000..b0abafc699
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch
@@ -0,0 +1,107 @@
+From 8af2552e862100e843b8d1f36543b718dde393ad Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:47:53 +0100
+Subject: [PATCH] drm/vc4: hdmi: Pass vc4_hdmi to CEC code
+
+Our CEC code also retrieves the associated vc4_hdmi by setting the
+vc4_dev pointer as its private data, and then dereferences its vc4_hdmi
+pointer.
+
+In order to eventually get rid of that pointer, we can simply pass the
+vc4_hdmi pointer directly.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 24 +++++++++---------------
+ 1 file changed, 9 insertions(+), 15 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1043,8 +1043,7 @@ static int vc4_hdmi_audio_init(struct vc
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+ static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv)
+ {
+- struct vc4_dev *vc4 = priv;
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = priv;
+
+ if (vc4_hdmi->cec_irq_was_rx) {
+ if (vc4_hdmi->cec_rx_msg.len)
+@@ -1064,9 +1063,8 @@ static irqreturn_t vc4_cec_irq_handler_t
+ return IRQ_HANDLED;
+ }
+
+-static void vc4_cec_read_msg(struct vc4_dev *vc4, u32 cntrl1)
++static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1)
+ {
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
+ struct cec_msg *msg = &vc4_hdmi->cec_rx_msg;
+ unsigned int i;
+
+@@ -1084,8 +1082,7 @@ static void vc4_cec_read_msg(struct vc4_
+
+ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+ {
+- struct vc4_dev *vc4 = priv;
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = priv;
+ u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
+ u32 cntrl1, cntrl5;
+
+@@ -1096,7 +1093,7 @@ static irqreturn_t vc4_cec_irq_handler(i
+ cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+ if (vc4_hdmi->cec_irq_was_rx) {
+- vc4_cec_read_msg(vc4, cntrl1);
++ vc4_cec_read_msg(vc4_hdmi, cntrl1);
+ cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+ HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+ cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+@@ -1112,8 +1109,7 @@ static irqreturn_t vc4_cec_irq_handler(i
+
+ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+ {
+- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ /* clock period in microseconds */
+ const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
+ u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
+@@ -1156,8 +1152,7 @@ static int vc4_hdmi_cec_adap_enable(stru
+
+ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+ {
+- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+
+ HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
+ (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+@@ -1168,8 +1163,7 @@ static int vc4_hdmi_cec_adap_log_addr(st
+ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+ {
+- struct vc4_dev *vc4 = cec_get_drvdata(adap);
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ u32 val;
+ unsigned int i;
+
+@@ -1315,7 +1309,7 @@ static int vc4_hdmi_bind(struct device *
+
+ #ifdef CONFIG_DRM_VC4_HDMI_CEC
+ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+- vc4, "vc4",
++ vc4_hdmi, "vc4",
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_CONNECTOR_INFO, 1);
+ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
+@@ -1339,7 +1333,7 @@ static int vc4_hdmi_bind(struct device *
+ ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+ vc4_cec_irq_handler_thread, 0,
+- "vc4 hdmi cec", vc4);
++ "vc4 hdmi cec", vc4_hdmi);
+ if (ret)
+ goto err_delete_cec_adap;
+ ret = cec_register_adapter(vc4_hdmi->cec_adap, dev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch b/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch
new file mode 100644
index 0000000000..ff9fa1a158
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch
@@ -0,0 +1,67 @@
+From 9e56da09cb8d8f65a26cfa0a957e295646ca47f8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:49:11 +0100
+Subject: [PATCH] drm/vc4: hdmi: Remove vc4_dev hdmi pointer
+
+Now that we don't have any users anymore, we can kill that pointer.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 -
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 14 ++++++--------
+ 2 files changed, 6 insertions(+), 9 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -76,7 +76,6 @@ struct vc4_dev {
+ bool firmware_kms;
+ struct rpi_firmware *firmware;
+
+- struct vc4_hdmi *hdmi;
+ struct vc4_hvs *hvs;
+ struct vc4_v3d *v3d;
+ struct vc4_dpi *dpi;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1199,7 +1199,6 @@ static int vc4_hdmi_bind(struct device *
+ #endif
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
+- struct vc4_dev *vc4 = drm->dev_private;
+ struct vc4_hdmi *vc4_hdmi;
+ struct drm_encoder *encoder;
+ struct device_node *ddc_node;
+@@ -1287,8 +1286,6 @@ static int vc4_hdmi_bind(struct device *
+ vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
+ }
+
+- vc4->hdmi = vc4_hdmi;
+-
+ /* HDMI core must be enabled. */
+ if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
+ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+@@ -1369,9 +1366,12 @@ err_put_i2c:
+ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+ {
+- struct drm_device *drm = dev_get_drvdata(master);
+- struct vc4_dev *vc4 = drm->dev_private;
+- struct vc4_hdmi *vc4_hdmi = vc4->hdmi;
++ /*
++ * snd_soc_register_card will set the device drvdata pointer
++ * to the card being registered.
++ */
++ struct snd_soc_card *card = dev_get_drvdata(dev);
++ struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card);
+
+ cec_unregister_adapter(vc4_hdmi->cec_adap);
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base);
+@@ -1381,8 +1381,6 @@ static void vc4_hdmi_unbind(struct devic
+ pm_runtime_disable(dev);
+
+ put_device(&vc4_hdmi->ddc->dev);
+-
+- vc4->hdmi = NULL;
+ }
+
+ static const struct component_ops vc4_hdmi_ops = {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch b/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch
new file mode 100644
index 0000000000..4ef3d69b2c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch
@@ -0,0 +1,141 @@
+From 6fdf2c94a028e04e1e20791aae5e0adaf905df77 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 18:57:16 +0100
+Subject: [PATCH] drm/vc4: hdmi: Remove vc4_hdmi_connector
+
+The vc4_hdmi_connector was only used to switch between drm_connector to
+drm_encoder. However, we can now use vc4_hdmi to do the switch, so that
+structure is redundant.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 19 ++++++++-----------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 23 ++---------------------
+ 2 files changed, 10 insertions(+), 32 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -188,13 +188,10 @@ static const struct drm_connector_helper
+ static int vc4_hdmi_connector_init(struct drm_device *dev,
+ struct vc4_hdmi *vc4_hdmi)
+ {
+- struct vc4_hdmi_connector *hdmi_connector = &vc4_hdmi->connector;
+- struct drm_connector *connector = &hdmi_connector->base;
++ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+ int ret;
+
+- hdmi_connector->encoder = encoder;
+-
+ drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
+@@ -290,7 +287,7 @@ static void vc4_hdmi_set_avi_infoframe(s
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+- struct drm_connector *connector = &vc4_hdmi->connector.base;
++ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_connector_state *cstate = connector->state;
+ struct drm_crtc *crtc = encoder->crtc;
+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+@@ -672,7 +669,7 @@ static int vc4_hdmi_audio_startup(struct
+ {
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base;
+- struct drm_connector *connector = &vc4_hdmi->connector.base;
++ struct drm_connector *connector = &vc4_hdmi->connector;
+ int ret;
+
+ if (vc4_hdmi->audio.substream && vc4_hdmi->audio.substream != substream)
+@@ -846,7 +843,7 @@ static int vc4_hdmi_audio_eld_ctl_info(s
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
+- struct drm_connector *connector = &vc4_hdmi->connector.base;
++ struct drm_connector *connector = &vc4_hdmi->connector;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = sizeof(connector->eld);
+@@ -859,7 +856,7 @@ static int vc4_hdmi_audio_eld_ctl_get(st
+ {
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
+- struct drm_connector *connector = &vc4_hdmi->connector.base;
++ struct drm_connector *connector = &vc4_hdmi->connector;
+
+ memcpy(ucontrol->value.bytes.data, connector->eld,
+ sizeof(connector->eld));
+@@ -1313,7 +1310,7 @@ static int vc4_hdmi_bind(struct device *
+ if (ret < 0)
+ goto err_destroy_conn;
+
+- cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector.base);
++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
+ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+
+ HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+@@ -1350,7 +1347,7 @@ static int vc4_hdmi_bind(struct device *
+ err_delete_cec_adap:
+ cec_delete_adapter(vc4_hdmi->cec_adap);
+ err_destroy_conn:
+- vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base);
++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ #endif
+ err_destroy_encoder:
+ vc4_hdmi_encoder_destroy(encoder);
+@@ -1374,7 +1371,7 @@ static void vc4_hdmi_unbind(struct devic
+ struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card);
+
+ cec_unregister_adapter(vc4_hdmi->cec_adap);
+- vc4_hdmi_connector_destroy(&vc4_hdmi->connector.base);
++ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base);
+
+ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -21,23 +21,6 @@ to_vc4_hdmi_encoder(struct drm_encoder *
+ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+ }
+
+-/* VC4 HDMI connector KMS struct */
+-struct vc4_hdmi_connector {
+- struct drm_connector base;
+-
+- /* Since the connector is attached to just the one encoder,
+- * this is the reference to it so we can do the best_encoder()
+- * hook.
+- */
+- struct drm_encoder *encoder;
+-};
+-
+-static inline struct vc4_hdmi_connector *
+-to_vc4_hdmi_connector(struct drm_connector *connector)
+-{
+- return container_of(connector, struct vc4_hdmi_connector, base);
+-}
+-
+ /* HDMI audio information */
+ struct vc4_hdmi_audio {
+ struct snd_soc_card card;
+@@ -56,7 +39,7 @@ struct vc4_hdmi {
+ struct platform_device *pdev;
+
+ struct vc4_hdmi_encoder encoder;
+- struct vc4_hdmi_connector connector;
++ struct drm_connector connector;
+
+ struct vc4_hdmi_audio audio;
+
+@@ -81,9 +64,7 @@ struct vc4_hdmi {
+ static inline struct vc4_hdmi *
+ connector_to_vc4_hdmi(struct drm_connector *connector)
+ {
+- struct vc4_hdmi_connector *_connector = to_vc4_hdmi_connector(connector);
+-
+- return container_of(_connector, struct vc4_hdmi, connector);
++ return container_of(connector, struct vc4_hdmi, connector);
+ }
+
+ static inline struct vc4_hdmi *
diff --git a/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch b/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch
new file mode 100644
index 0000000000..82fae40246
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch
@@ -0,0 +1,151 @@
+From 9fa3342da883f6e111952768b36ca1df4d529660 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 18 Dec 2019 11:30:54 +0100
+Subject: [PATCH] drm/vc4: hdmi: Introduce resource init and variant
+
+The HDMI controllers found in the BCM2711 has a pretty different clock and
+registers areas than found in the older BCM283x SoCs.
+
+Let's create a variant structure to store the various adjustments we'll
+need later on, and a function to get the resources needed for one
+particular version.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 67 ++++++++++++++++++++++------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 10 +++++
+ 2 files changed, 54 insertions(+), 23 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1189,38 +1189,23 @@ static const struct cec_adap_ops vc4_hdm
+ };
+ #endif
+
+-static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
++static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+ {
+-#ifdef CONFIG_DRM_VC4_HDMI_CEC
+- struct cec_connector_info conn_info;
+-#endif
+- struct platform_device *pdev = to_platform_device(dev);
+- struct drm_device *drm = dev_get_drvdata(master);
+- struct vc4_hdmi *vc4_hdmi;
+- struct drm_encoder *encoder;
+- struct device_node *ddc_node;
+- u32 value;
+- int ret;
+-
+- vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
+- if (!vc4_hdmi)
+- return -ENOMEM;
+-
+- vc4_hdmi->pdev = pdev;
+- encoder = &vc4_hdmi->encoder.base.base;
+- encoder->base.type = VC4_ENCODER_TYPE_HDMI0;
++ struct platform_device *pdev = vc4_hdmi->pdev;
++ struct device *dev = &pdev->dev;
+
+ vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(vc4_hdmi->hdmicore_regs))
+ return PTR_ERR(vc4_hdmi->hdmicore_regs);
+
++ vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs;
++ vc4_hdmi->hdmi_regset.regs = hdmi_regs;
++ vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
++
+ vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+ if (IS_ERR(vc4_hdmi->hd_regs))
+ return PTR_ERR(vc4_hdmi->hd_regs);
+
+- vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs;
+- vc4_hdmi->hdmi_regset.regs = hdmi_regs;
+- vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
+ vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs;
+ vc4_hdmi->hd_regset.regs = hd_regs;
+ vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs);
+@@ -1230,12 +1215,44 @@ static int vc4_hdmi_bind(struct device *
+ DRM_ERROR("Failed to get pixel clock\n");
+ return PTR_ERR(vc4_hdmi->pixel_clock);
+ }
++
+ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
+ if (IS_ERR(vc4_hdmi->hsm_clock)) {
+ DRM_ERROR("Failed to get HDMI state machine clock\n");
+ return PTR_ERR(vc4_hdmi->hsm_clock);
+ }
+
++ return 0;
++}
++
++static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
++{
++#ifdef CONFIG_DRM_VC4_HDMI_CEC
++ struct cec_connector_info conn_info;
++#endif
++ struct platform_device *pdev = to_platform_device(dev);
++ struct drm_device *drm = dev_get_drvdata(master);
++ const struct vc4_hdmi_variant *variant;
++ struct vc4_hdmi *vc4_hdmi;
++ struct drm_encoder *encoder;
++ struct device_node *ddc_node;
++ u32 value;
++ int ret;
++
++ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
++ if (!vc4_hdmi)
++ return -ENOMEM;
++
++ vc4_hdmi->pdev = pdev;
++ variant = of_device_get_match_data(dev);
++ vc4_hdmi->variant = variant;
++ vc4_hdmi->encoder.base.type = VC4_ENCODER_TYPE_HDMI0;
++ encoder = &vc4_hdmi->encoder.base.base;
++
++ ret = variant->init_resources(vc4_hdmi);
++ if (ret)
++ return ret;
++
+ ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
+ if (!ddc_node) {
+ DRM_ERROR("Failed to find ddc node in device tree\n");
+@@ -1396,8 +1413,12 @@ static int vc4_hdmi_dev_remove(struct pl
+ return 0;
+ }
+
++static const struct vc4_hdmi_variant bcm2835_variant = {
++ .init_resources = vc4_hdmi_init_resources,
++};
++
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+- { .compatible = "brcm,bcm2835-hdmi" },
++ { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant },
+ {}
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -21,6 +21,15 @@ to_vc4_hdmi_encoder(struct drm_encoder *
+ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+ }
+
++struct vc4_hdmi;
++
++struct vc4_hdmi_variant {
++ /* Callback to get the resources (memory region, interrupts,
++ * clocks, etc) for that variant.
++ */
++ int (*init_resources)(struct vc4_hdmi *vc4_hdmi);
++};
++
+ /* HDMI audio information */
+ struct vc4_hdmi_audio {
+ struct snd_soc_card card;
+@@ -37,6 +46,7 @@ struct vc4_hdmi_audio {
+ /* General HDMI hardware state. */
+ struct vc4_hdmi {
+ struct platform_device *pdev;
++ const struct vc4_hdmi_variant *variant;
+
+ struct vc4_hdmi_encoder encoder;
+ struct drm_connector connector;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch b/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch
new file mode 100644
index 0000000000..eaeca92bfd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch
@@ -0,0 +1,1310 @@
+From 261b3072275937fe64af287c1b61cbb63aca830e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 18 Dec 2019 19:15:08 +0100
+Subject: [PATCH] drm/vc4: hdmi: Implement a register layout
+ abstraction
+
+The HDMI controllers found in the BCM2711 have most of the registers
+reorganized in multiple registers areas and at different offsets than
+previously found.
+
+The logic however remains pretty much the same, so it doesn't really make
+sense to create a whole new driver and we should share the code as much as
+possible.
+
+Let's implement some indirection to wrap around a register and depending on
+the variant will lookup the associated register on that particular variant.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 354 ++++++++++++++--------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 12 +-
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 250 ++++++++++++++++++++
+ drivers/gpu/drm/vc4/vc4_regs.h | 92 --------
+ 4 files changed, 437 insertions(+), 271 deletions(-)
+ create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -49,62 +49,13 @@
+ #include "media/cec.h"
+ #include "vc4_drv.h"
+ #include "vc4_hdmi.h"
++#include "vc4_hdmi_regs.h"
+ #include "vc4_regs.h"
+
+ #define HSM_CLOCK_FREQ 163682864
+ #define CEC_CLOCK_FREQ 40000
+ #define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
+
+-static const struct debugfs_reg32 hdmi_regs[] = {
+- VC4_REG32(VC4_HDMI_CORE_REV),
+- VC4_REG32(VC4_HDMI_SW_RESET_CONTROL),
+- VC4_REG32(VC4_HDMI_HOTPLUG_INT),
+- VC4_REG32(VC4_HDMI_HOTPLUG),
+- VC4_REG32(VC4_HDMI_MAI_CHANNEL_MAP),
+- VC4_REG32(VC4_HDMI_MAI_CONFIG),
+- VC4_REG32(VC4_HDMI_MAI_FORMAT),
+- VC4_REG32(VC4_HDMI_AUDIO_PACKET_CONFIG),
+- VC4_REG32(VC4_HDMI_RAM_PACKET_CONFIG),
+- VC4_REG32(VC4_HDMI_HORZA),
+- VC4_REG32(VC4_HDMI_HORZB),
+- VC4_REG32(VC4_HDMI_FIFO_CTL),
+- VC4_REG32(VC4_HDMI_SCHEDULER_CONTROL),
+- VC4_REG32(VC4_HDMI_VERTA0),
+- VC4_REG32(VC4_HDMI_VERTA1),
+- VC4_REG32(VC4_HDMI_VERTB0),
+- VC4_REG32(VC4_HDMI_VERTB1),
+- VC4_REG32(VC4_HDMI_TX_PHY_RESET_CTL),
+- VC4_REG32(VC4_HDMI_TX_PHY_CTL0),
+-
+- VC4_REG32(VC4_HDMI_CEC_CNTRL_1),
+- VC4_REG32(VC4_HDMI_CEC_CNTRL_2),
+- VC4_REG32(VC4_HDMI_CEC_CNTRL_3),
+- VC4_REG32(VC4_HDMI_CEC_CNTRL_4),
+- VC4_REG32(VC4_HDMI_CEC_CNTRL_5),
+- VC4_REG32(VC4_HDMI_CPU_STATUS),
+- VC4_REG32(VC4_HDMI_CPU_MASK_STATUS),
+-
+- VC4_REG32(VC4_HDMI_CEC_RX_DATA_1),
+- VC4_REG32(VC4_HDMI_CEC_RX_DATA_2),
+- VC4_REG32(VC4_HDMI_CEC_RX_DATA_3),
+- VC4_REG32(VC4_HDMI_CEC_RX_DATA_4),
+- VC4_REG32(VC4_HDMI_CEC_TX_DATA_1),
+- VC4_REG32(VC4_HDMI_CEC_TX_DATA_2),
+- VC4_REG32(VC4_HDMI_CEC_TX_DATA_3),
+- VC4_REG32(VC4_HDMI_CEC_TX_DATA_4),
+-};
+-
+-static const struct debugfs_reg32 hd_regs[] = {
+- VC4_REG32(VC4_HD_M_CTL),
+- VC4_REG32(VC4_HD_MAI_CTL),
+- VC4_REG32(VC4_HD_MAI_THR),
+- VC4_REG32(VC4_HD_MAI_FMT),
+- VC4_REG32(VC4_HD_MAI_SMP),
+- VC4_REG32(VC4_HD_VID_CTL),
+- VC4_REG32(VC4_HD_CSC_CTL),
+- VC4_REG32(VC4_HD_FRAME_COUNT),
+-};
+-
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+ {
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+@@ -133,7 +84,7 @@ vc4_hdmi_connector_detect(struct drm_con
+ if (drm_probe_ddc(vc4_hdmi->ddc))
+ return connector_status_connected;
+
+- if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
++ if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
+ return connector_status_connected;
+ cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
+ return connector_status_disconnected;
+@@ -229,10 +180,10 @@ static int vc4_hdmi_stop_packet(struct d
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ u32 packet_id = type - 0x80;
+
+- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
+
+- return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
++ return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
+ BIT(packet_id)), 100);
+ }
+
+@@ -241,12 +192,16 @@ static void vc4_hdmi_write_infoframe(str
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ u32 packet_id = frame->any.type - 0x80;
+- u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id);
++ const struct vc4_hdmi_register *ram_packet_start =
++ &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
++ u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id;
++ void __iomem *base = __vc4_hdmi_get_field_base(vc4_hdmi,
++ ram_packet_start->reg);
+ uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+ ssize_t len, i;
+ int ret;
+
+- WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
++ WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
+ VC4_HDMI_RAM_PACKET_ENABLE),
+ "Packet RAM has to be on to store the packet.");
+
+@@ -261,23 +216,23 @@ static void vc4_hdmi_write_infoframe(str
+ }
+
+ for (i = 0; i < len; i += 7) {
+- HDMI_WRITE(packet_reg,
+- buffer[i + 0] << 0 |
+- buffer[i + 1] << 8 |
+- buffer[i + 2] << 16);
++ writel(buffer[i + 0] << 0 |
++ buffer[i + 1] << 8 |
++ buffer[i + 2] << 16,
++ base + packet_reg);
+ packet_reg += 4;
+
+- HDMI_WRITE(packet_reg,
+- buffer[i + 3] << 0 |
+- buffer[i + 4] << 8 |
+- buffer[i + 5] << 16 |
+- buffer[i + 6] << 24);
++ writel(buffer[i + 3] << 0 |
++ buffer[i + 4] << 8 |
++ buffer[i + 5] << 16 |
++ buffer[i + 6] << 24,
++ base + packet_reg);
+ packet_reg += 4;
+ }
+
+- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
+- ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
++ ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) &
+ BIT(packet_id)), 100);
+ if (ret)
+ DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
+@@ -358,11 +313,11 @@ static void vc4_hdmi_encoder_disable(str
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ int ret;
+
+- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+
+- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+- HD_WRITE(VC4_HD_VID_CTL,
+- HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++ HDMI_WRITE(HDMI_VID_CTL,
++ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+
+@@ -417,18 +372,18 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
+- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
++ HDMI_WRITE(HDMI_SW_RESET_CONTROL,
+ VC4_HDMI_SW_RESET_HDMI |
+ VC4_HDMI_SW_RESET_FORMAT_DETECT);
+
+- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
+
+ /* PHY should be in reset, like
+ * vc4_hdmi_encoder_disable() does.
+ */
+- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+
+- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
+
+ if (debug_dump_regs) {
+ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev);
+@@ -438,20 +393,20 @@ static void vc4_hdmi_encoder_enable(stru
+ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+ }
+
+- HD_WRITE(VC4_HD_VID_CTL, 0);
++ HDMI_WRITE(HDMI_VID_CTL, 0);
+
+- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
++ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
+ VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
+ VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
+
+- HDMI_WRITE(VC4_HDMI_HORZA,
++ HDMI_WRITE(HDMI_HORZA,
+ (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+ (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
+ VC4_SET_FIELD(mode->hdisplay * pixel_rep,
+ VC4_HDMI_HORZA_HAP));
+
+- HDMI_WRITE(VC4_HDMI_HORZB,
++ HDMI_WRITE(HDMI_HORZB,
+ VC4_SET_FIELD((mode->htotal -
+ mode->hsync_end) * pixel_rep,
+ VC4_HDMI_HORZB_HBP) |
+@@ -462,13 +417,13 @@ static void vc4_hdmi_encoder_enable(stru
+ mode->hdisplay) * pixel_rep,
+ VC4_HDMI_HORZB_HFP));
+
+- HDMI_WRITE(VC4_HDMI_VERTA0, verta);
+- HDMI_WRITE(VC4_HDMI_VERTA1, verta);
++ HDMI_WRITE(HDMI_VERTA0, verta);
++ HDMI_WRITE(HDMI_VERTA1, verta);
+
+- HDMI_WRITE(VC4_HDMI_VERTB0, vertb_even);
+- HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
++ HDMI_WRITE(HDMI_VERTB0, vertb_even);
++ HDMI_WRITE(HDMI_VERTB1, vertb);
+
+- HD_WRITE(VC4_HD_VID_CTL,
++ HDMI_WRITE(HDMI_VID_CTL,
+ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+
+@@ -493,21 +448,21 @@ static void vc4_hdmi_encoder_enable(stru
+ csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+ VC4_HD_CSC_CTL_MODE);
+
+- HD_WRITE(VC4_HD_CSC_12_11, (0x000 << 16) | 0x000);
+- HD_WRITE(VC4_HD_CSC_14_13, (0x100 << 16) | 0x6e0);
+- HD_WRITE(VC4_HD_CSC_22_21, (0x6e0 << 16) | 0x000);
+- HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000);
+- HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0);
+- HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0);
++ HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0);
++ HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000);
+ vc4_encoder->limited_rgb_range = true;
+ } else {
+ vc4_encoder->limited_rgb_range = false;
+ }
+
+ /* The RGB order applies even when CSC is disabled. */
+- HD_WRITE(VC4_HD_CSC_CTL, csc_ctl);
++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+
+- HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
++ HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+
+ if (debug_dump_regs) {
+ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev);
+@@ -517,30 +472,30 @@ static void vc4_hdmi_encoder_enable(stru
+ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+ }
+
+- HD_WRITE(VC4_HD_VID_CTL,
+- HD_READ(VC4_HD_VID_CTL) |
+- VC4_HD_VID_CTL_ENABLE |
+- VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+- VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
++ HDMI_WRITE(HDMI_VID_CTL,
++ HDMI_READ(HDMI_VID_CTL) |
++ VC4_HD_VID_CTL_ENABLE |
++ VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
++ VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
+
+ if (vc4_encoder->hdmi_monitor) {
+- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
++ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
+ VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+- ret = wait_for(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
++ ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000);
+ WARN_ONCE(ret, "Timeout waiting for "
+ "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
+ } else {
+- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+- HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
++ HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
+ ~(VC4_HDMI_RAM_PACKET_ENABLE));
+- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
++ HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+- ret = wait_for(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
++ ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000);
+ WARN_ONCE(ret, "Timeout waiting for "
+ "!VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
+@@ -549,31 +504,31 @@ static void vc4_hdmi_encoder_enable(stru
+ if (vc4_encoder->hdmi_monitor) {
+ u32 drift;
+
+- WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
++ WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) &
+ VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
+- HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+- HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
++ HDMI_WRITE(HDMI_SCHEDULER_CONTROL,
++ HDMI_READ(HDMI_SCHEDULER_CONTROL) |
+ VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
+
+- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
++ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
+ VC4_HDMI_RAM_PACKET_ENABLE);
+
+ vc4_hdmi_set_infoframes(encoder);
+
+- drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
++ drift = HDMI_READ(HDMI_FIFO_CTL);
+ drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
+
+- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
++ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
++ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
+ usleep_range(1000, 1100);
+- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
++ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+- HDMI_WRITE(VC4_HDMI_FIFO_CTL,
++ HDMI_WRITE(HDMI_FIFO_CTL,
+ drift | VC4_HDMI_FIFO_CTL_RECENTER);
+
+- ret = wait_for(HDMI_READ(VC4_HDMI_FIFO_CTL) &
++ ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) &
+ VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
+ WARN_ONCE(ret, "Timeout waiting for "
+ "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
+@@ -625,7 +580,7 @@ static void vc4_hdmi_audio_set_mai_clock
+ VC4_HD_MAI_SMP_M_SHIFT) + 1,
+ &n, &m);
+
+- HD_WRITE(VC4_HD_MAI_SMP,
++ HDMI_WRITE(HDMI_MAI_SMP,
+ VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
+ VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
+ }
+@@ -644,7 +599,7 @@ static void vc4_hdmi_set_n_cts(struct vc
+ do_div(tmp, 128 * samplerate);
+ cts = tmp;
+
+- HDMI_WRITE(VC4_HDMI_CRP_CFG,
++ HDMI_WRITE(HDMI_CRP_CFG,
+ VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN |
+ VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N));
+
+@@ -653,8 +608,8 @@ static void vc4_hdmi_set_n_cts(struct vc
+ * providing a CTS_1 value. The two CTS values are alternated
+ * between based on the period fields
+ */
+- HDMI_WRITE(VC4_HDMI_CTS_0, cts);
+- HDMI_WRITE(VC4_HDMI_CTS_1, cts);
++ HDMI_WRITE(HDMI_CTS_0, cts);
++ HDMI_WRITE(HDMI_CTS_1, cts);
+ }
+
+ static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai)
+@@ -681,7 +636,7 @@ static int vc4_hdmi_audio_startup(struct
+ * If the HDMI encoder hasn't probed, or the encoder is
+ * currently in DVI mode, treat the codec dai as missing.
+ */
+- if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
++ if (!encoder->crtc || !(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
+ VC4_HDMI_RAM_PACKET_ENABLE))
+ return -ENODEV;
+
+@@ -707,9 +662,9 @@ static void vc4_hdmi_audio_reset(struct
+ if (ret)
+ dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
+
+- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET);
+- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
+- HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET);
++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF);
++ HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH);
+ }
+
+ static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream,
+@@ -745,7 +700,7 @@ static int vc4_hdmi_audio_hw_params(stru
+ vc4_hdmi->audio.channels = params_channels(params);
+ vc4_hdmi->audio.samplerate = params_rate(params);
+
+- HD_WRITE(VC4_HD_MAI_CTL,
++ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_RESET |
+ VC4_HD_MAI_CTL_FLUSH |
+ VC4_HD_MAI_CTL_DLATE |
+@@ -765,22 +720,22 @@ static int vc4_hdmi_audio_hw_params(stru
+
+ /* Set the MAI threshold. This logic mimics the firmware's. */
+ if (vc4_hdmi->audio.samplerate > 96000) {
+- HD_WRITE(VC4_HD_MAI_THR,
++ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+ } else if (vc4_hdmi->audio.samplerate > 48000) {
+- HD_WRITE(VC4_HD_MAI_THR,
++ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW));
+ } else {
+- HD_WRITE(VC4_HD_MAI_THR,
++ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW));
+ }
+
+- HDMI_WRITE(VC4_HDMI_MAI_CONFIG,
++ HDMI_WRITE(HDMI_MAI_CONFIG,
+ VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
+ VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK));
+
+@@ -790,8 +745,8 @@ static int vc4_hdmi_audio_hw_params(stru
+ channel_map |= i << (3 * i);
+ }
+
+- HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map);
+- HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
++ HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map);
++ HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
+ vc4_hdmi_set_n_cts(vc4_hdmi);
+
+ return 0;
+@@ -806,21 +761,22 @@ static int vc4_hdmi_audio_trigger(struct
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ vc4_hdmi_set_audio_infoframe(encoder);
+- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
+- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) &
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ HDMI_READ(HDMI_TX_PHY_CTL_0) &
+ ~VC4_HDMI_TX_PHY_RNG_PWRDN);
+- HD_WRITE(VC4_HD_MAI_CTL,
++
++ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_SET_FIELD(vc4_hdmi->audio.channels,
+ VC4_HD_MAI_CTL_CHNUM) |
+ VC4_HD_MAI_CTL_ENABLE);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+- HD_WRITE(VC4_HD_MAI_CTL,
++ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
+- HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0,
+- HDMI_READ(VC4_HDMI_TX_PHY_CTL0) |
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ HDMI_READ(HDMI_TX_PHY_CTL_0) |
+ VC4_HDMI_TX_PHY_RNG_PWRDN);
+ break;
+ default:
+@@ -954,6 +910,8 @@ static const struct snd_dmaengine_pcm_co
+
+ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
+ {
++ const struct vc4_hdmi_register *mai_data =
++ &vc4_hdmi->variant->registers[HDMI_MAI_DATA];
+ struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link;
+ struct snd_soc_card *card = &vc4_hdmi->audio.card;
+ struct device *dev = &vc4_hdmi->pdev->dev;
+@@ -968,6 +926,11 @@ static int vc4_hdmi_audio_init(struct vc
+ return 0;
+ }
+
++ if (mai_data->reg != VC4_HD) {
++ WARN_ONCE(true, "MAI isn't in the HD block\n");
++ return -EINVAL;
++ }
++
+ /*
+ * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve
+ * the bus address specified in the DT, because the physical address
+@@ -976,7 +939,7 @@ static int vc4_hdmi_audio_init(struct vc
+ * This VC/MMU should probably be exposed to avoid this kind of hacks.
+ */
+ addr = of_get_address(dev->of_node, 1, NULL, NULL);
+- vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA;
++ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
+ vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ vc4_hdmi->audio.dma_data.maxburst = 2;
+
+@@ -1068,7 +1031,7 @@ static void vc4_cec_read_msg(struct vc4_
+ msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+ VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
+ for (i = 0; i < msg->len; i += 4) {
+- u32 val = HDMI_READ(VC4_HDMI_CEC_RX_DATA_1 + i);
++ u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i);
+
+ msg->msg[i] = val & 0xff;
+ msg->msg[i + 1] = (val >> 8) & 0xff;
+@@ -1080,26 +1043,26 @@ static void vc4_cec_read_msg(struct vc4_
+ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
+ {
+ struct vc4_hdmi *vc4_hdmi = priv;
+- u32 stat = HDMI_READ(VC4_HDMI_CPU_STATUS);
++ u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
+ u32 cntrl1, cntrl5;
+
+ if (!(stat & VC4_HDMI_CPU_CEC))
+ return IRQ_NONE;
+ vc4_hdmi->cec_rx_msg.len = 0;
+- cntrl1 = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
+- cntrl5 = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
++ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
++ cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
+ vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT;
+ if (vc4_hdmi->cec_irq_was_rx) {
+ vc4_cec_read_msg(vc4_hdmi, cntrl1);
+ cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+ cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF;
+ } else {
+ vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD;
+ cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+ }
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, cntrl1);
+- HDMI_WRITE(VC4_HDMI_CPU_CLEAR, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
++ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
+
+ return IRQ_WAKE_THREAD;
+ }
+@@ -1109,7 +1072,7 @@ static int vc4_hdmi_cec_adap_enable(stru
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ /* clock period in microseconds */
+ const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
+- u32 val = HDMI_READ(VC4_HDMI_CEC_CNTRL_5);
++ u32 val = HDMI_READ(HDMI_CEC_CNTRL_5);
+
+ val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
+ VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
+@@ -1118,30 +1081,30 @@ static int vc4_hdmi_cec_adap_enable(stru
+ ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
+
+ if (enable) {
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val);
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_2,
++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val);
++ HDMI_WRITE(HDMI_CEC_CNTRL_2,
+ ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) |
+ ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) |
+ ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) |
+ ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) |
+ ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT));
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_3,
++ HDMI_WRITE(HDMI_CEC_CNTRL_3,
+ ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) |
+ ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) |
+ ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) |
+ ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT));
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_4,
++ HDMI_WRITE(HDMI_CEC_CNTRL_4,
+ ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) |
+ ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) |
+ ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+ ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+
+- HDMI_WRITE(VC4_HDMI_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
+ } else {
+- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_5, val |
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+ }
+ return 0;
+@@ -1151,8 +1114,8 @@ static int vc4_hdmi_cec_adap_log_addr(st
+ {
+ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1,
+- (HDMI_READ(VC4_HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
++ HDMI_WRITE(HDMI_CEC_CNTRL_1,
++ (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
+ (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
+ return 0;
+ }
+@@ -1165,20 +1128,20 @@ static int vc4_hdmi_cec_adap_transmit(st
+ unsigned int i;
+
+ for (i = 0; i < msg->len; i += 4)
+- HDMI_WRITE(VC4_HDMI_CEC_TX_DATA_1 + i,
++ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i,
+ (msg->msg[i]) |
+ (msg->msg[i + 1] << 8) |
+ (msg->msg[i + 2] << 16) |
+ (msg->msg[i + 3] << 24));
+
+- val = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
++ val = HDMI_READ(HDMI_CEC_CNTRL_1);
+ val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
+ val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK;
+ val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT;
+ val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
+
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, val);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
+ return 0;
+ }
+
+@@ -1189,26 +1152,63 @@ static const struct cec_adap_ops vc4_hdm
+ };
+ #endif
+
++static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
++ struct debugfs_regset32 *regset,
++ enum vc4_hdmi_regs reg)
++{
++ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
++ struct debugfs_reg32 *regs;
++ unsigned int count = 0;
++ unsigned int i;
++
++ regs = kzalloc(variant->num_registers * sizeof(*regs),
++ GFP_KERNEL);
++ if (!regs)
++ return -ENOMEM;
++
++ for (i = 0; i < variant->num_registers; i++) {
++ const struct vc4_hdmi_register *field = &variant->registers[i];
++
++ if (field->reg != reg)
++ continue;
++
++ regs[count].name = field->name;
++ regs[count].offset = field->offset;
++ count++;
++ }
++
++ regs = krealloc(regs, count * sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return -ENOMEM;
++
++ regset->base = __vc4_hdmi_get_field_base(vc4_hdmi, reg);
++ regset->regs = regs;
++ regset->nregs = count;
++
++ return 0;
++}
++
+ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+ {
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ struct device *dev = &pdev->dev;
++ int ret;
+
+ vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(vc4_hdmi->hdmicore_regs))
+ return PTR_ERR(vc4_hdmi->hdmicore_regs);
+
+- vc4_hdmi->hdmi_regset.base = vc4_hdmi->hdmicore_regs;
+- vc4_hdmi->hdmi_regset.regs = hdmi_regs;
+- vc4_hdmi->hdmi_regset.nregs = ARRAY_SIZE(hdmi_regs);
++ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
++ if (ret)
++ return ret;
+
+ vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+ if (IS_ERR(vc4_hdmi->hd_regs))
+ return PTR_ERR(vc4_hdmi->hd_regs);
+
+- vc4_hdmi->hd_regset.base = vc4_hdmi->hd_regs;
+- vc4_hdmi->hd_regset.regs = hd_regs;
+- vc4_hdmi->hd_regset.nregs = ARRAY_SIZE(hd_regs);
++ ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
++ if (ret)
++ return ret;
+
+ vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel");
+ if (IS_ERR(vc4_hdmi->pixel_clock)) {
+@@ -1301,12 +1301,12 @@ static int vc4_hdmi_bind(struct device *
+ }
+
+ /* HDMI core must be enabled. */
+- if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
+- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
++ if (!(HDMI_READ(HDMI_M_CTL) & VC4_HD_M_ENABLE)) {
++ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST);
+ udelay(1);
+- HD_WRITE(VC4_HD_M_CTL, 0);
++ HDMI_WRITE(HDMI_M_CTL, 0);
+
+- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
++ HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_ENABLE);
+ }
+ pm_runtime_enable(dev);
+
+@@ -1330,8 +1330,8 @@ static int vc4_hdmi_bind(struct device *
+ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
+ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+
+- HDMI_WRITE(VC4_HDMI_CPU_MASK_SET, 0xffffffff);
+- value = HDMI_READ(VC4_HDMI_CEC_CNTRL_1);
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
++ value = HDMI_READ(HDMI_CEC_CNTRL_1);
+ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+ /*
+ * Set the logical address to Unregistered and set the clock
+@@ -1340,7 +1340,7 @@ static int vc4_hdmi_bind(struct device *
+ */
+ value |= VC4_HDMI_CEC_ADDR_MASK |
+ (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+- HDMI_WRITE(VC4_HDMI_CEC_CNTRL_1, value);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+ ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+ vc4_cec_irq_handler_thread, 0,
+@@ -1387,6 +1387,9 @@ static void vc4_hdmi_unbind(struct devic
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+ struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(card);
+
++ kfree(vc4_hdmi->hdmi_regset.regs);
++ kfree(vc4_hdmi->hd_regset.regs);
++
+ cec_unregister_adapter(vc4_hdmi->cec_adap);
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base);
+@@ -1414,6 +1417,9 @@ static int vc4_hdmi_dev_remove(struct pl
+ }
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
++ .registers = vc4_hdmi_fields,
++ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
++
+ .init_resources = vc4_hdmi_init_resources,
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -22,8 +22,15 @@ to_vc4_hdmi_encoder(struct drm_encoder *
+ }
+
+ struct vc4_hdmi;
++struct vc4_hdmi_register;
+
+ struct vc4_hdmi_variant {
++ /* List of the registers available on that variant */
++ const struct vc4_hdmi_register *registers;
++
++ /* Number of registers on that variant */
++ unsigned int num_registers;
++
+ /* Callback to get the resources (memory region, interrupts,
+ * clocks, etc) for that variant.
+ */
+@@ -85,9 +92,4 @@ encoder_to_vc4_hdmi(struct drm_encoder *
+ return container_of(_encoder, struct vc4_hdmi, encoder);
+ }
+
+-#define HDMI_READ(offset) readl(vc4_hdmi->hdmicore_regs + offset)
+-#define HDMI_WRITE(offset, val) writel(val, vc4_hdmi->hdmicore_regs + offset)
+-#define HD_READ(offset) readl(vc4_hdmi->hd_regs + offset)
+-#define HD_WRITE(offset, val) writel(val, vc4_hdmi->hd_regs + offset)
+-
+ #endif /* _VC4_HDMI_H_ */
+--- /dev/null
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -0,0 +1,250 @@
++#ifndef _VC4_HDMI_REGS_H_
++#define _VC4_HDMI_REGS_H_
++
++#include "vc4_hdmi.h"
++
++#define VC4_MASK(high, low) ((u32)GENMASK(high, low))
++/* Using the GNU statement expression extension */
++#define VC4_SET_FIELD(value, field) \
++ ({ \
++ uint32_t fieldval = (value) << field##_SHIFT; \
++ WARN_ON((fieldval & ~field##_MASK) != 0); \
++ fieldval & field##_MASK; \
++ })
++
++#define VC4_HDMI_PACKET_STRIDE 0x24
++
++enum vc4_hdmi_regs {
++ VC4_INVALID = 0,
++ VC4_HDMI,
++ VC4_HD,
++};
++
++enum vc4_hdmi_field {
++ HDMI_AUDIO_PACKET_CONFIG,
++ HDMI_CEC_CNTRL_1,
++ HDMI_CEC_CNTRL_2,
++ HDMI_CEC_CNTRL_3,
++ HDMI_CEC_CNTRL_4,
++ HDMI_CEC_CNTRL_5,
++ HDMI_CEC_CPU_CLEAR,
++ HDMI_CEC_CPU_MASK_CLEAR,
++ HDMI_CEC_CPU_MASK_SET,
++ HDMI_CEC_CPU_MASK_STATUS,
++ HDMI_CEC_CPU_STATUS,
++
++ /*
++ * Transmit data, first byte is low byte of the 32-bit reg.
++ * MSB of each byte transmitted first.
++ */
++ HDMI_CEC_RX_DATA_1,
++ HDMI_CEC_RX_DATA_2,
++ HDMI_CEC_RX_DATA_3,
++ HDMI_CEC_RX_DATA_4,
++ HDMI_CEC_TX_DATA_1,
++ HDMI_CEC_TX_DATA_2,
++ HDMI_CEC_TX_DATA_3,
++ HDMI_CEC_TX_DATA_4,
++ HDMI_CORE_REV,
++ HDMI_CRP_CFG,
++ HDMI_CSC_12_11,
++ HDMI_CSC_14_13,
++ HDMI_CSC_22_21,
++ HDMI_CSC_24_23,
++ HDMI_CSC_32_31,
++ HDMI_CSC_34_33,
++ HDMI_CSC_CTL,
++
++ /*
++ * 20-bit fields containing CTS values to be transmitted if
++ * !EXTERNAL_CTS_EN
++ */
++ HDMI_CTS_0,
++ HDMI_CTS_1,
++ HDMI_FIFO_CTL,
++ HDMI_FRAME_COUNT,
++ HDMI_HORZA,
++ HDMI_HORZB,
++ HDMI_HOTPLUG,
++ HDMI_HOTPLUG_INT,
++
++ /*
++ * 3 bits per field, where each field maps from that
++ * corresponding MAI bus channel to the given HDMI channel.
++ */
++ HDMI_MAI_CHANNEL_MAP,
++ HDMI_MAI_CONFIG,
++ HDMI_MAI_CTL,
++
++ /*
++ * Register for DMAing in audio data to be transported over
++ * the MAI bus to the Falcon core.
++ */
++ HDMI_MAI_DATA,
++
++ /* Format header to be placed on the MAI data. Unused. */
++ HDMI_MAI_FMT,
++
++ /* Last received format word on the MAI bus. */
++ HDMI_MAI_FORMAT,
++ HDMI_MAI_SMP,
++ HDMI_MAI_THR,
++ HDMI_M_CTL,
++ HDMI_RAM_PACKET_CONFIG,
++ HDMI_RAM_PACKET_START,
++ HDMI_RAM_PACKET_STATUS,
++ HDMI_SCHEDULER_CONTROL,
++ HDMI_SW_RESET_CONTROL,
++ HDMI_TX_PHY_CTL_0,
++ HDMI_TX_PHY_RESET_CTL,
++ HDMI_VERTA0,
++ HDMI_VERTA1,
++ HDMI_VERTB0,
++ HDMI_VERTB1,
++ HDMI_VID_CTL,
++};
++
++struct vc4_hdmi_register {
++ char *name;
++ enum vc4_hdmi_regs reg;
++ unsigned int offset;
++};
++
++#define _VC4_REG(_base, _reg, _offset) \
++ [_reg] = { \
++ .name = #_reg, \
++ .reg = _base, \
++ .offset = _offset, \
++ }
++
++#define VC4_HD_REG(reg, offset) _VC4_REG(VC4_HD, reg, offset)
++#define VC4_HDMI_REG(reg, offset) _VC4_REG(VC4_HDMI, reg, offset)
++
++static const struct vc4_hdmi_register vc4_hdmi_fields[] = {
++ VC4_HD_REG(HDMI_M_CTL, 0x000c),
++ VC4_HD_REG(HDMI_MAI_CTL, 0x0014),
++ VC4_HD_REG(HDMI_MAI_THR, 0x0018),
++ VC4_HD_REG(HDMI_MAI_FMT, 0x001c),
++ VC4_HD_REG(HDMI_MAI_DATA, 0x0020),
++ VC4_HD_REG(HDMI_MAI_SMP, 0x002c),
++ VC4_HD_REG(HDMI_VID_CTL, 0x0038),
++ VC4_HD_REG(HDMI_CSC_CTL, 0x0040),
++ VC4_HD_REG(HDMI_CSC_12_11, 0x0044),
++ VC4_HD_REG(HDMI_CSC_14_13, 0x0048),
++ VC4_HD_REG(HDMI_CSC_22_21, 0x004c),
++ VC4_HD_REG(HDMI_CSC_24_23, 0x0050),
++ VC4_HD_REG(HDMI_CSC_32_31, 0x0054),
++ VC4_HD_REG(HDMI_CSC_34_33, 0x0058),
++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0068),
++
++ VC4_HDMI_REG(HDMI_CORE_REV, 0x0000),
++ VC4_HDMI_REG(HDMI_SW_RESET_CONTROL, 0x0004),
++ VC4_HDMI_REG(HDMI_HOTPLUG_INT, 0x0008),
++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x000c),
++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x005c),
++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0090),
++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0094),
++ VC4_HDMI_REG(HDMI_MAI_FORMAT, 0x0098),
++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x009c),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x00a0),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x00a4),
++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x00a8),
++ VC4_HDMI_REG(HDMI_CTS_0, 0x00ac),
++ VC4_HDMI_REG(HDMI_CTS_1, 0x00b0),
++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x00c0),
++ VC4_HDMI_REG(HDMI_HORZA, 0x00c4),
++ VC4_HDMI_REG(HDMI_HORZB, 0x00c8),
++ VC4_HDMI_REG(HDMI_VERTA0, 0x00cc),
++ VC4_HDMI_REG(HDMI_VERTB0, 0x00d0),
++ VC4_HDMI_REG(HDMI_VERTA1, 0x00d4),
++ VC4_HDMI_REG(HDMI_VERTB1, 0x00d8),
++ VC4_HDMI_REG(HDMI_CEC_CNTRL_1, 0x00e8),
++ VC4_HDMI_REG(HDMI_CEC_CNTRL_2, 0x00ec),
++ VC4_HDMI_REG(HDMI_CEC_CNTRL_3, 0x00f0),
++ VC4_HDMI_REG(HDMI_CEC_CNTRL_4, 0x00f4),
++ VC4_HDMI_REG(HDMI_CEC_CNTRL_5, 0x00f8),
++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_1, 0x00fc),
++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_2, 0x0100),
++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_3, 0x0104),
++ VC4_HDMI_REG(HDMI_CEC_TX_DATA_4, 0x0108),
++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_1, 0x010c),
++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_2, 0x0110),
++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_3, 0x0114),
++ VC4_HDMI_REG(HDMI_CEC_RX_DATA_4, 0x0118),
++ VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0),
++ VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4),
++ VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340),
++ VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348),
++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c),
++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c),
++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
++};
++
++static inline
++void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi,
++ enum vc4_hdmi_regs reg)
++{
++ switch (reg) {
++ case VC4_HD:
++ return hdmi->hd_regs;
++
++ case VC4_HDMI:
++ return hdmi->hdmicore_regs;
++
++ default:
++ return NULL;
++ }
++
++ return NULL;
++}
++
++static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi,
++ enum vc4_hdmi_regs reg)
++{
++ const struct vc4_hdmi_register *field;
++ const struct vc4_hdmi_variant *variant = hdmi->variant;
++ void __iomem *base;
++
++ if (reg > variant->num_registers) {
++ dev_warn(&hdmi->pdev->dev,
++ "Invalid register ID %u\n", reg);
++ return 0;
++ }
++
++ field = &variant->registers[reg];
++ base = __vc4_hdmi_get_field_base(hdmi, field->reg);
++ if (!base) {
++ dev_warn(&hdmi->pdev->dev,
++ "Unknown register ID %u\n", reg);
++ return 0;
++ }
++
++ return readl(base + field->offset);
++}
++#define HDMI_READ(reg) vc4_hdmi_read(vc4_hdmi, reg)
++
++static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi,
++ enum vc4_hdmi_regs reg,
++ u32 value)
++{
++ const struct vc4_hdmi_register *field;
++ const struct vc4_hdmi_variant *variant = hdmi->variant;
++ void __iomem *base;
++
++ if (reg > variant->num_registers) {
++ dev_warn(&hdmi->pdev->dev,
++ "Invalid register ID %u\n", reg);
++ return;
++ }
++
++ field = &variant->registers[reg];
++ base = __vc4_hdmi_get_field_base(hdmi, field->reg);
++ if (!base)
++ return;
++
++ writel(value, base + field->offset);
++}
++#define HDMI_WRITE(reg, val) vc4_hdmi_write(vc4_hdmi, reg, val)
++
++#endif /* _VC4_HDMI_REGS_H_ */
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -493,32 +493,16 @@
+
+ #define SCALER5_DLIST_START 0x00004000
+
+-#define VC4_HDMI_CORE_REV 0x000
+-
+-#define VC4_HDMI_SW_RESET_CONTROL 0x004
+ # define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1)
+ # define VC4_HDMI_SW_RESET_HDMI BIT(0)
+
+-#define VC4_HDMI_HOTPLUG_INT 0x008
+-
+-#define VC4_HDMI_HOTPLUG 0x00c
+ # define VC4_HDMI_HOTPLUG_CONNECTED BIT(0)
+
+-/* 3 bits per field, where each field maps from that corresponding MAI
+- * bus channel to the given HDMI channel.
+- */
+-#define VC4_HDMI_MAI_CHANNEL_MAP 0x090
+-
+-#define VC4_HDMI_MAI_CONFIG 0x094
+ # define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27)
+ # define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26)
+ # define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0)
+ # define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0
+
+-/* Last received format word on the MAI bus. */
+-#define VC4_HDMI_MAI_FORMAT 0x098
+-
+-#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c
+ # define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29)
+ # define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24)
+ # define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19)
+@@ -532,12 +516,8 @@
+ # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0)
+ # define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0
+
+-#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0
+ # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16)
+
+-#define VC4_HDMI_RAM_PACKET_STATUS 0x0a4
+-
+-#define VC4_HDMI_CRP_CFG 0x0a8
+ /* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead
+ * of pixel clock.
+ */
+@@ -551,23 +531,12 @@
+ # define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0)
+ # define VC4_HDMI_CRP_CFG_N_SHIFT 0
+
+-/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */
+-#define VC4_HDMI_CTS_0 0x0ac
+-#define VC4_HDMI_CTS_1 0x0b0
+-/* 20-bit fields containing number of clocks to send CTS0/1 before
+- * switching to the other one.
+- */
+-#define VC4_HDMI_CTS_PERIOD_0 0x0b4
+-#define VC4_HDMI_CTS_PERIOD_1 0x0b8
+-
+-#define VC4_HDMI_HORZA 0x0c4
+ # define VC4_HDMI_HORZA_VPOS BIT(14)
+ # define VC4_HDMI_HORZA_HPOS BIT(13)
+ /* Horizontal active pixels (hdisplay). */
+ # define VC4_HDMI_HORZA_HAP_MASK VC4_MASK(12, 0)
+ # define VC4_HDMI_HORZA_HAP_SHIFT 0
+
+-#define VC4_HDMI_HORZB 0x0c8
+ /* Horizontal pack porch (htotal - hsync_end). */
+ # define VC4_HDMI_HORZB_HBP_MASK VC4_MASK(29, 20)
+ # define VC4_HDMI_HORZB_HBP_SHIFT 20
+@@ -578,7 +547,6 @@
+ # define VC4_HDMI_HORZB_HFP_MASK VC4_MASK(9, 0)
+ # define VC4_HDMI_HORZB_HFP_SHIFT 0
+
+-#define VC4_HDMI_FIFO_CTL 0x05c
+ # define VC4_HDMI_FIFO_CTL_RECENTER_DONE BIT(14)
+ # define VC4_HDMI_FIFO_CTL_USE_EMPTY BIT(13)
+ # define VC4_HDMI_FIFO_CTL_ON_VB BIT(7)
+@@ -591,15 +559,12 @@
+ # define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N BIT(0)
+ # define VC4_HDMI_FIFO_VALID_WRITE_MASK 0xefff
+
+-#define VC4_HDMI_SCHEDULER_CONTROL 0x0c0
+ # define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT BIT(15)
+ # define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS BIT(5)
+ # define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT BIT(3)
+ # define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE BIT(1)
+ # define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI BIT(0)
+
+-#define VC4_HDMI_VERTA0 0x0cc
+-#define VC4_HDMI_VERTA1 0x0d4
+ /* Vertical sync pulse (vsync_end - vsync_start). */
+ # define VC4_HDMI_VERTA_VSP_MASK VC4_MASK(24, 20)
+ # define VC4_HDMI_VERTA_VSP_SHIFT 20
+@@ -610,8 +575,6 @@
+ # define VC4_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0)
+ # define VC4_HDMI_VERTA_VAL_SHIFT 0
+
+-#define VC4_HDMI_VERTB0 0x0d0
+-#define VC4_HDMI_VERTB1 0x0d8
+ /* Vertical sync pulse offset (for interlaced) */
+ # define VC4_HDMI_VERTB_VSPO_MASK VC4_MASK(21, 9)
+ # define VC4_HDMI_VERTB_VSPO_SHIFT 9
+@@ -619,7 +582,6 @@
+ # define VC4_HDMI_VERTB_VBP_MASK VC4_MASK(8, 0)
+ # define VC4_HDMI_VERTB_VBP_SHIFT 0
+
+-#define VC4_HDMI_CEC_CNTRL_1 0x0e8
+ /* Set when the transmission has ended. */
+ # define VC4_HDMI_CEC_TX_EOM BIT(31)
+ /* If set, transmission was acked on the 1st or 2nd attempt (only one
+@@ -660,7 +622,6 @@
+ /* Set these fields to how many bit clock cycles get to that many
+ * microseconds.
+ */
+-#define VC4_HDMI_CEC_CNTRL_2 0x0ec
+ # define VC4_HDMI_CEC_CNT_TO_1500_US_MASK VC4_MASK(30, 24)
+ # define VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT 24
+ # define VC4_HDMI_CEC_CNT_TO_1300_US_MASK VC4_MASK(23, 17)
+@@ -672,7 +633,6 @@
+ # define VC4_HDMI_CEC_CNT_TO_400_US_MASK VC4_MASK(4, 0)
+ # define VC4_HDMI_CEC_CNT_TO_400_US_SHIFT 0
+
+-#define VC4_HDMI_CEC_CNTRL_3 0x0f0
+ # define VC4_HDMI_CEC_CNT_TO_2750_US_MASK VC4_MASK(31, 24)
+ # define VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT 24
+ # define VC4_HDMI_CEC_CNT_TO_2400_US_MASK VC4_MASK(23, 16)
+@@ -682,7 +642,6 @@
+ # define VC4_HDMI_CEC_CNT_TO_1700_US_MASK VC4_MASK(7, 0)
+ # define VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT 0
+
+-#define VC4_HDMI_CEC_CNTRL_4 0x0f4
+ # define VC4_HDMI_CEC_CNT_TO_4300_US_MASK VC4_MASK(31, 24)
+ # define VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT 24
+ # define VC4_HDMI_CEC_CNT_TO_3900_US_MASK VC4_MASK(23, 16)
+@@ -692,7 +651,6 @@
+ # define VC4_HDMI_CEC_CNT_TO_3500_US_MASK VC4_MASK(7, 0)
+ # define VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT 0
+
+-#define VC4_HDMI_CEC_CNTRL_5 0x0f8
+ # define VC4_HDMI_CEC_TX_SW_RESET BIT(27)
+ # define VC4_HDMI_CEC_RX_SW_RESET BIT(26)
+ # define VC4_HDMI_CEC_PAD_SW_RESET BIT(25)
+@@ -705,39 +663,11 @@
+ # define VC4_HDMI_CEC_CNT_TO_4500_US_MASK VC4_MASK(7, 0)
+ # define VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT 0
+
+-/* Transmit data, first byte is low byte of the 32-bit reg. MSB of
+- * each byte transmitted first.
+- */
+-#define VC4_HDMI_CEC_TX_DATA_1 0x0fc
+-#define VC4_HDMI_CEC_TX_DATA_2 0x100
+-#define VC4_HDMI_CEC_TX_DATA_3 0x104
+-#define VC4_HDMI_CEC_TX_DATA_4 0x108
+-#define VC4_HDMI_CEC_RX_DATA_1 0x10c
+-#define VC4_HDMI_CEC_RX_DATA_2 0x110
+-#define VC4_HDMI_CEC_RX_DATA_3 0x114
+-#define VC4_HDMI_CEC_RX_DATA_4 0x118
+-
+-#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0
+-
+-#define VC4_HDMI_TX_PHY_CTL0 0x2c4
+ # define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25)
+
+-/* Interrupt status bits */
+-#define VC4_HDMI_CPU_STATUS 0x340
+-#define VC4_HDMI_CPU_SET 0x344
+-#define VC4_HDMI_CPU_CLEAR 0x348
+ # define VC4_HDMI_CPU_CEC BIT(6)
+ # define VC4_HDMI_CPU_HOTPLUG BIT(0)
+
+-#define VC4_HDMI_CPU_MASK_STATUS 0x34c
+-#define VC4_HDMI_CPU_MASK_SET 0x350
+-#define VC4_HDMI_CPU_MASK_CLEAR 0x354
+-
+-#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4))
+-#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24))
+-#define VC4_HDMI_PACKET_STRIDE 0x24
+-
+-#define VC4_HD_M_CTL 0x00c
+ /* Debug: Current receive value on the CEC pad. */
+ # define VC4_HD_CECRXD BIT(9)
+ /* Debug: Override CEC output to 0. */
+@@ -747,7 +677,6 @@
+ # define VC4_HD_M_SW_RST BIT(2)
+ # define VC4_HD_M_ENABLE BIT(0)
+
+-#define VC4_HD_MAI_CTL 0x014
+ /* Set when audio stream is received at a slower rate than the
+ * sampling period, so MAI fifo goes empty. Write 1 to clear.
+ */
+@@ -772,7 +701,6 @@
+ /* Single-shot reset bit. Read value is undefined. */
+ # define VC4_HD_MAI_CTL_RESET BIT(0)
+
+-#define VC4_HD_MAI_THR 0x018
+ # define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24)
+ # define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24
+ # define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16)
+@@ -782,31 +710,20 @@
+ # define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0)
+ # define VC4_HD_MAI_THR_DREQLOW_SHIFT 0
+
+-/* Format header to be placed on the MAI data. Unused. */
+-#define VC4_HD_MAI_FMT 0x01c
+-
+-/* Register for DMAing in audio data to be transported over the MAI
+- * bus to the Falcon core.
+- */
+-#define VC4_HD_MAI_DATA 0x020
+-
+ /* Divider from HDMI HSM clock to MAI serial clock. Sampling period
+ * converges to N / (M + 1) cycles.
+ */
+-#define VC4_HD_MAI_SMP 0x02c
+ # define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8)
+ # define VC4_HD_MAI_SMP_N_SHIFT 8
+ # define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0)
+ # define VC4_HD_MAI_SMP_M_SHIFT 0
+
+-#define VC4_HD_VID_CTL 0x038
+ # define VC4_HD_VID_CTL_ENABLE BIT(31)
+ # define VC4_HD_VID_CTL_UNDERFLOW_ENABLE BIT(30)
+ # define VC4_HD_VID_CTL_FRAME_COUNTER_RESET BIT(29)
+ # define VC4_HD_VID_CTL_VSYNC_LOW BIT(28)
+ # define VC4_HD_VID_CTL_HSYNC_LOW BIT(27)
+
+-#define VC4_HD_CSC_CTL 0x040
+ # define VC4_HD_CSC_CTL_ORDER_MASK VC4_MASK(7, 5)
+ # define VC4_HD_CSC_CTL_ORDER_SHIFT 5
+ # define VC4_HD_CSC_CTL_ORDER_RGB 0
+@@ -824,15 +741,6 @@
+ # define VC4_HD_CSC_CTL_RGB2YCC BIT(1)
+ # define VC4_HD_CSC_CTL_ENABLE BIT(0)
+
+-#define VC4_HD_CSC_12_11 0x044
+-#define VC4_HD_CSC_14_13 0x048
+-#define VC4_HD_CSC_22_21 0x04c
+-#define VC4_HD_CSC_24_23 0x050
+-#define VC4_HD_CSC_32_31 0x054
+-#define VC4_HD_CSC_34_33 0x058
+-
+-#define VC4_HD_FRAME_COUNT 0x068
+-
+ /* HVS display list information. */
+ #define HVS_BOOTLOADER_DLIST_END 32
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch
new file mode 100644
index 0000000000..53dd3e4405
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch
@@ -0,0 +1,66 @@
+From 3cf4a365b833d7a2e7622ad5569b9d54aebbe593 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 19 Dec 2019 16:25:26 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add reset callback
+
+The BCM2711 and BCM283x HDMI controllers use a slightly different reset
+sequence, so let's add a callback to reset the controller.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 17 ++++++++++++-----
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -68,6 +68,15 @@ static int vc4_hdmi_debugfs_regs(struct
+ return 0;
+ }
+
++static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_SW_RESET_CONTROL,
++ VC4_HDMI_SW_RESET_HDMI |
++ VC4_HDMI_SW_RESET_FORMAT_DETECT);
++
++ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
++}
++
+ static enum drm_connector_status
+ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
+ {
+@@ -372,11 +381,8 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
+- HDMI_WRITE(HDMI_SW_RESET_CONTROL,
+- VC4_HDMI_SW_RESET_HDMI |
+- VC4_HDMI_SW_RESET_FORMAT_DETECT);
+-
+- HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
++ if (vc4_hdmi->variant->reset)
++ vc4_hdmi->variant->reset(vc4_hdmi);
+
+ /* PHY should be in reset, like
+ * vc4_hdmi_encoder_disable() does.
+@@ -1421,6 +1427,7 @@ static const struct vc4_hdmi_variant bcm
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+ .init_resources = vc4_hdmi_init_resources,
++ .reset = vc4_hdmi_reset,
+ };
+
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -35,6 +35,9 @@ struct vc4_hdmi_variant {
+ * clocks, etc) for that variant.
+ */
+ int (*init_resources)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to reset the HDMI block */
++ void (*reset)(struct vc4_hdmi *vc4_hdmi);
+ };
+
+ /* HDMI audio information */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch b/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch
new file mode 100644
index 0000000000..6db26d40aa
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch
@@ -0,0 +1,128 @@
+From 9fe77147d40e0dc58e7297e79ba8b50e13b8269d Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 19 Dec 2019 16:53:33 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add PHY init and disable function
+
+The HDMI PHY in the BCM2711 HDMI controller is significantly more
+complicated to setup than in the older BCM283x SoCs.
+
+Let's add hooks to enable and disable the PHY.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/Makefile | 1 +
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 14 +++++++-------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 13 +++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 25 +++++++++++++++++++++++++
+ 4 files changed, 46 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+
+--- a/drivers/gpu/drm/vc4/Makefile
++++ b/drivers/gpu/drm/vc4/Makefile
+@@ -13,6 +13,7 @@ vc4-y := \
+ vc4_kms.o \
+ vc4_gem.o \
+ vc4_hdmi.o \
++ vc4_hdmi_phy.o \
+ vc4_vec.o \
+ vc4_hvs.o \
+ vc4_irq.o \
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -324,7 +324,9 @@ static void vc4_hdmi_encoder_disable(str
+
+ HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
+
+- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++ if (vc4_hdmi->variant->phy_disable)
++ vc4_hdmi->variant->phy_disable(vc4_hdmi);
++
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+@@ -384,12 +386,8 @@ static void vc4_hdmi_encoder_enable(stru
+ if (vc4_hdmi->variant->reset)
+ vc4_hdmi->variant->reset(vc4_hdmi);
+
+- /* PHY should be in reset, like
+- * vc4_hdmi_encoder_disable() does.
+- */
+- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+-
+- HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
++ if (vc4_hdmi->variant->phy_init)
++ vc4_hdmi->variant->phy_init(vc4_hdmi, mode);
+
+ if (debug_dump_regs) {
+ struct drm_printer p = drm_info_printer(&vc4_hdmi->pdev->dev);
+@@ -1428,6 +1426,8 @@ static const struct vc4_hdmi_variant bcm
+
+ .init_resources = vc4_hdmi_init_resources,
+ .reset = vc4_hdmi_reset,
++ .phy_init = vc4_hdmi_phy_init,
++ .phy_disable = vc4_hdmi_phy_disable,
+ };
+
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -21,6 +21,8 @@ to_vc4_hdmi_encoder(struct drm_encoder *
+ return container_of(encoder, struct vc4_hdmi_encoder, base.base);
+ }
+
++struct drm_display_mode;
++
+ struct vc4_hdmi;
+ struct vc4_hdmi_register;
+
+@@ -38,6 +40,13 @@ struct vc4_hdmi_variant {
+
+ /* Callback to reset the HDMI block */
+ void (*reset)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to initialize the PHY according to the mode */
++ void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode);
++
++ /* Callback to disable the PHY */
++ void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
+ };
+
+ /* HDMI audio information */
+@@ -95,4 +104,8 @@ encoder_to_vc4_hdmi(struct drm_encoder *
+ return container_of(_encoder, struct vc4_hdmi, encoder);
+ }
+
++void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode);
++void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
++
+ #endif /* _VC4_HDMI_H_ */
+--- /dev/null
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -0,0 +1,25 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2015 Broadcom
++ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
++ * Copyright (C) 2013 Red Hat
++ * Author: Rob Clark <robdclark@gmail.com>
++ */
++
++#include "vc4_hdmi.h"
++#include "vc4_hdmi_regs.h"
++
++void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
++{
++ /* PHY should be in reset, like
++ * vc4_hdmi_encoder_disable() does.
++ */
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
++}
++
++void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
++}
diff --git a/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch b/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch
new file mode 100644
index 0000000000..cea17a621e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch
@@ -0,0 +1,104 @@
+From ddf78df1db8752247e89a68231338a194e5dc52b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 19 Dec 2019 17:22:24 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add PHY RNG enable / disable function
+
+Let's continue the implementation of hooks for the parts that change in the
+BCM2711 SoC with the PHY RNG setup.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 15 +++++++++------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 8 ++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 15 +++++++++++++++
+ 3 files changed, 32 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -765,9 +765,9 @@ static int vc4_hdmi_audio_trigger(struct
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ vc4_hdmi_set_audio_infoframe(encoder);
+- HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+- HDMI_READ(HDMI_TX_PHY_CTL_0) &
+- ~VC4_HDMI_TX_PHY_RNG_PWRDN);
++
++ if (vc4_hdmi->variant->phy_rng_enable)
++ vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
+
+ HDMI_WRITE(HDMI_MAI_CTL,
+ VC4_SET_FIELD(vc4_hdmi->audio.channels,
+@@ -779,9 +779,10 @@ static int vc4_hdmi_audio_trigger(struct
+ VC4_HD_MAI_CTL_DLATE |
+ VC4_HD_MAI_CTL_ERRORE |
+ VC4_HD_MAI_CTL_ERRORF);
+- HDMI_WRITE(HDMI_TX_PHY_CTL_0,
+- HDMI_READ(HDMI_TX_PHY_CTL_0) |
+- VC4_HDMI_TX_PHY_RNG_PWRDN);
++
++ if (vc4_hdmi->variant->phy_rng_disable)
++ vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
++
+ break;
+ default:
+ break;
+@@ -1428,6 +1429,8 @@ static const struct vc4_hdmi_variant bcm
+ .reset = vc4_hdmi_reset,
+ .phy_init = vc4_hdmi_phy_init,
+ .phy_disable = vc4_hdmi_phy_disable,
++ .phy_rng_enable = vc4_hdmi_phy_rng_enable,
++ .phy_rng_disable = vc4_hdmi_phy_rng_disable,
+ };
+
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -47,6 +47,12 @@ struct vc4_hdmi_variant {
+
+ /* Callback to disable the PHY */
+ void (*phy_disable)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to enable the RNG in the PHY */
++ void (*phy_rng_enable)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to disable the RNG in the PHY */
++ void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi);
+ };
+
+ /* HDMI audio information */
+@@ -107,5 +113,7 @@ encoder_to_vc4_hdmi(struct drm_encoder *
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
+ void vc4_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
++void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
++void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
+
+ #endif /* _VC4_HDMI_H_ */
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -7,6 +7,7 @@
+ */
+
+ #include "vc4_hdmi.h"
++#include "vc4_regs.h"
+ #include "vc4_hdmi_regs.h"
+
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+@@ -23,3 +24,17 @@ void vc4_hdmi_phy_disable(struct vc4_hdm
+ {
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+ }
++
++void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ HDMI_READ(HDMI_TX_PHY_CTL_0) &
++ ~VC4_HDMI_TX_PHY_RNG_PWRDN);
++}
++
++void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ HDMI_READ(HDMI_TX_PHY_CTL_0) |
++ VC4_HDMI_TX_PHY_RNG_PWRDN);
++}
diff --git a/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch
new file mode 100644
index 0000000000..c9650bca08
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch
@@ -0,0 +1,134 @@
+From 110cf6bdc1d79f2ee7a435bc9d1ec900aba11ed5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 18:41:53 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add a CSC setup callback
+
+Similarly to the previous patches, the CSC setup is slightly different in
+the BCM2711 than in the previous generations. Let's add a callback for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 71 ++++++++++++++++++++--------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ++
+ 2 files changed, 45 insertions(+), 29 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -337,6 +337,41 @@ static void vc4_hdmi_encoder_disable(str
+ DRM_ERROR("Failed to release power domain: %d\n", ret);
+ }
+
++static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
++{
++ u32 csc_ctl;
++
++ csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
++ VC4_HD_CSC_CTL_ORDER);
++
++ if (enable) {
++ /* CEA VICs other than #1 requre limited range RGB
++ * output unless overridden by an AVI infoframe.
++ * Apply a colorspace conversion to squash 0-255 down
++ * to 16-235. The matrix here is:
++ *
++ * [ 0 0 0.8594 16]
++ * [ 0 0.8594 0 16]
++ * [ 0.8594 0 0 16]
++ * [ 0 0 0 1]
++ */
++ csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
++ csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
++ csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
++ VC4_HD_CSC_CTL_MODE);
++
++ HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0);
++ HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000);
++ HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0);
++ HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000);
++ }
++
++ /* The RGB order applies even when CSC is disabled. */
++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
++}
++
+ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+ {
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+@@ -360,7 +395,6 @@ static void vc4_hdmi_encoder_enable(stru
+ mode->crtc_vsync_end -
+ interlaced,
+ VC4_HDMI_VERTB_VBP));
+- u32 csc_ctl;
+ int ret;
+
+ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev);
+@@ -431,41 +465,19 @@ static void vc4_hdmi_encoder_enable(stru
+ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+
+- csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+- VC4_HD_CSC_CTL_ORDER);
+-
+ if (vc4_encoder->hdmi_monitor &&
+- drm_default_rgb_quant_range(mode) ==
+- HDMI_QUANTIZATION_RANGE_LIMITED) {
+- /* CEA VICs other than #1 requre limited range RGB
+- * output unless overridden by an AVI infoframe.
+- * Apply a colorspace conversion to squash 0-255 down
+- * to 16-235. The matrix here is:
+- *
+- * [ 0 0 0.8594 16]
+- * [ 0 0.8594 0 16]
+- * [ 0.8594 0 0 16]
+- * [ 0 0 0 1]
+- */
+- csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
+- csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
+- csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+- VC4_HD_CSC_CTL_MODE);
++ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
++ if (vc4_hdmi->variant->csc_setup)
++ vc4_hdmi->variant->csc_setup(vc4_hdmi, true);
+
+- HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000);
+- HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0);
+- HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000);
+- HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000);
+- HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0);
+- HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000);
+ vc4_encoder->limited_rgb_range = true;
+ } else {
++ if (vc4_hdmi->variant->csc_setup)
++ vc4_hdmi->variant->csc_setup(vc4_hdmi, false);
++
+ vc4_encoder->limited_rgb_range = false;
+ }
+
+- /* The RGB order applies even when CSC is disabled. */
+- HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+-
+ HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+
+ if (debug_dump_regs) {
+@@ -1426,6 +1438,7 @@ static const struct vc4_hdmi_variant bcm
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+ .init_resources = vc4_hdmi_init_resources,
++ .csc_setup = vc4_hdmi_csc_setup,
+ .reset = vc4_hdmi_reset,
+ .phy_init = vc4_hdmi_phy_init,
+ .phy_disable = vc4_hdmi_phy_disable,
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -41,6 +41,9 @@ struct vc4_hdmi_variant {
+ /* Callback to reset the HDMI block */
+ void (*reset)(struct vc4_hdmi *vc4_hdmi);
+
++ /* Callback to enable / disable the CSC */
++ void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable);
++
+ /* Callback to initialize the PHY according to the mode */
+ void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch b/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch
new file mode 100644
index 0000000000..a39a8ac109
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch
@@ -0,0 +1,133 @@
+From b9c57901c600e09b100942b637c6bb01e52b7326 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 6 Jan 2020 13:43:27 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add a set_timings callback
+
+Similarly to the previous patches, the timings setup in the HDMI controller
+of the BCM2711 is slightly different, mostly because it supports higher
+resolutions and thus needed more spaces for the various timings, resulting
+in the register layout changing.
+
+Let's add a callback for that as well.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 71 +++++++++++++++++++---------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 4 ++
+ 2 files changed, 44 insertions(+), 31 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -372,12 +372,9 @@ static void vc4_hdmi_csc_setup(struct vc
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+ }
+
+-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
++static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode)
+ {
+- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+- struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+- struct vc4_hdmi_encoder *vc4_encoder = &vc4_hdmi->encoder;
+- bool debug_dump_regs = false;
+ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
+ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
+ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+@@ -395,6 +392,41 @@ static void vc4_hdmi_encoder_enable(stru
+ mode->crtc_vsync_end -
+ interlaced,
+ VC4_HDMI_VERTB_VBP));
++
++ HDMI_WRITE(HDMI_HORZA,
++ (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
++ (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
++ VC4_SET_FIELD(mode->hdisplay * pixel_rep,
++ VC4_HDMI_HORZA_HAP));
++
++ HDMI_WRITE(HDMI_HORZB,
++ VC4_SET_FIELD((mode->htotal -
++ mode->hsync_end) * pixel_rep,
++ VC4_HDMI_HORZB_HBP) |
++ VC4_SET_FIELD((mode->hsync_end -
++ mode->hsync_start) * pixel_rep,
++ VC4_HDMI_HORZB_HSP) |
++ VC4_SET_FIELD((mode->hsync_start -
++ mode->hdisplay) * pixel_rep,
++ VC4_HDMI_HORZB_HFP));
++
++ HDMI_WRITE(HDMI_VERTA0, verta);
++ HDMI_WRITE(HDMI_VERTA1, verta);
++
++ HDMI_WRITE(HDMI_VERTB0, vertb_even);
++ HDMI_WRITE(HDMI_VERTB1, vertb);
++
++ HDMI_WRITE(HDMI_VID_CTL,
++ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
++ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
++}
++
++static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
++{
++ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
++ bool debug_dump_regs = false;
+ int ret;
+
+ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev);
+@@ -438,32 +470,8 @@ static void vc4_hdmi_encoder_enable(stru
+ VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
+ VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
+
+- HDMI_WRITE(HDMI_HORZA,
+- (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+- (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
+- VC4_SET_FIELD(mode->hdisplay * pixel_rep,
+- VC4_HDMI_HORZA_HAP));
+-
+- HDMI_WRITE(HDMI_HORZB,
+- VC4_SET_FIELD((mode->htotal -
+- mode->hsync_end) * pixel_rep,
+- VC4_HDMI_HORZB_HBP) |
+- VC4_SET_FIELD((mode->hsync_end -
+- mode->hsync_start) * pixel_rep,
+- VC4_HDMI_HORZB_HSP) |
+- VC4_SET_FIELD((mode->hsync_start -
+- mode->hdisplay) * pixel_rep,
+- VC4_HDMI_HORZB_HFP));
+-
+- HDMI_WRITE(HDMI_VERTA0, verta);
+- HDMI_WRITE(HDMI_VERTA1, verta);
+-
+- HDMI_WRITE(HDMI_VERTB0, vertb_even);
+- HDMI_WRITE(HDMI_VERTB1, vertb);
+-
+- HDMI_WRITE(HDMI_VID_CTL,
+- (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+- (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
++ if (vc4_hdmi->variant->set_timings)
++ vc4_hdmi->variant->set_timings(vc4_hdmi, mode);
+
+ if (vc4_encoder->hdmi_monitor &&
+ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
+@@ -1440,6 +1448,7 @@ static const struct vc4_hdmi_variant bcm
+ .init_resources = vc4_hdmi_init_resources,
+ .csc_setup = vc4_hdmi_csc_setup,
+ .reset = vc4_hdmi_reset,
++ .set_timings = vc4_hdmi_set_timings,
+ .phy_init = vc4_hdmi_phy_init,
+ .phy_disable = vc4_hdmi_phy_disable,
+ .phy_rng_enable = vc4_hdmi_phy_rng_enable,
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -44,6 +44,10 @@ struct vc4_hdmi_variant {
+ /* Callback to enable / disable the CSC */
+ void (*csc_setup)(struct vc4_hdmi *vc4_hdmi, bool enable);
+
++ /* Callback to configure the video timings in the HDMI block */
++ void (*set_timings)(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode);
++
+ /* Callback to initialize the PHY according to the mode */
+ void (*phy_init)(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch b/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch
new file mode 100644
index 0000000000..b6de732379
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch
@@ -0,0 +1,46 @@
+From 84c1a6034e361078d540c9b3bc672ccae623dc03 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 7 Jan 2020 13:14:07 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add HDMI ID
+
+Some operations will need us to have the raw ID of the HDMI controller
+in the BCM2711, such as the encoder type to register, the name of the
+debugfs files, etc.
+
+Let's add it to our variant structure.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 3 +--
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 5 +++++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1267,11 +1267,10 @@ static int vc4_hdmi_bind(struct device *
+ vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
+ if (!vc4_hdmi)
+ return -ENOMEM;
+-
+ vc4_hdmi->pdev = pdev;
+ variant = of_device_get_match_data(dev);
+ vc4_hdmi->variant = variant;
+- vc4_hdmi->encoder.base.type = VC4_ENCODER_TYPE_HDMI0;
++ vc4_hdmi->encoder.base.type = variant->id ? VC4_ENCODER_TYPE_HDMI1 : VC4_ENCODER_TYPE_HDMI0;
+ encoder = &vc4_hdmi->encoder.base.base;
+
+ ret = variant->init_resources(vc4_hdmi);
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -27,6 +27,11 @@ struct vc4_hdmi;
+ struct vc4_hdmi_register;
+
+ struct vc4_hdmi_variant {
++ /* On devices that have multiple, different instances (like
++ * the BCM2711), which instance is that variant useful for.
++ */
++ unsigned int id;
++
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch b/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch
new file mode 100644
index 0000000000..746eff91d5
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch
@@ -0,0 +1,33 @@
+From a1f24e24c065b91f833e3f546c1507f69fb04bc7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 16 Jan 2020 14:27:56 +0100
+Subject: [PATCH] drm/vc4: hdmi: Deal with multiple debugfs files
+
+The HDMI driver was registering a single debugfs file so far with the name
+hdmi_regs.
+
+Obviously, this is not going to work anymore when will have multiple HDMI
+controllers since we will end up trying to register two files with the same
+name.
+
+Let's use the ID to avoid that name conflict.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1380,7 +1380,10 @@ static int vc4_hdmi_bind(struct device *
+ if (ret)
+ goto err_destroy_encoder;
+
+- vc4_debugfs_add_file(drm, "hdmi_regs", vc4_hdmi_debugfs_regs, vc4_hdmi);
++ vc4_debugfs_add_file(drm,
++ variant->id ? "hdmi1_regs" : "hdmi_regs",
++ vc4_hdmi_debugfs_regs,
++ vc4_hdmi);
+
+ return 0;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch
new file mode 100644
index 0000000000..cc6cd86887
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch
@@ -0,0 +1,47 @@
+From 6154f7383e2defe48eea7fddb6ce646a0069828b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 16:21:45 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add an audio support flag
+
+The BCM2711 audio support doesn't work yet, so let's add a boolean to
+indicate whether or not it's supported, and only register a sound card if
+that boolean is set.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -944,6 +944,9 @@ static int vc4_hdmi_audio_init(struct vc
+ int ret;
+ int len;
+
++ if (!vc4_hdmi->variant->audio_available)
++ return 0;
++
+ if (!of_find_property(dev->of_node, "dmas", &len) ||
+ len == 0) {
+ dev_warn(dev,
+@@ -1444,6 +1447,7 @@ static int vc4_hdmi_dev_remove(struct pl
+ }
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
++ .audio_available = true,
+ .registers = vc4_hdmi_fields,
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -32,6 +32,9 @@ struct vc4_hdmi_variant {
+ */
+ unsigned int id;
+
++ /* Set to true when the audio support is available */
++ bool audio_available;
++
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch b/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch
new file mode 100644
index 0000000000..533eb89e7a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch
@@ -0,0 +1,165 @@
+From 9efd6edc4c7d01c74a92f2011ba285329ba956e4 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 16:22:13 +0100
+Subject: [PATCH] drm/vc4: hdmi: Move CEC init to its own function
+
+The CEC init code was put directly into the bind function, which was quite
+inconsistent with how the audio support was done, and would prevent us from
+further changes to skip that initialisation entirely.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 108 ++++++++++++++++++++-------------
+ 1 file changed, 67 insertions(+), 41 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1178,6 +1178,67 @@ static const struct cec_adap_ops vc4_hdm
+ .adap_log_addr = vc4_hdmi_cec_adap_log_addr,
+ .adap_transmit = vc4_hdmi_cec_adap_transmit,
+ };
++
++static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
++{
++ struct cec_connector_info conn_info;
++ struct platform_device *pdev = vc4_hdmi->pdev;
++ u32 value;
++ int ret;
++
++ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
++ vc4_hdmi, "vc4",
++ CEC_CAP_DEFAULTS |
++ CEC_CAP_CONNECTOR_INFO, 1);
++ ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
++ if (ret < 0)
++ return ret;
++
++ cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
++ cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
++
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
++ value = HDMI_READ(HDMI_CEC_CNTRL_1);
++ value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
++ /*
++ * Set the logical address to Unregistered and set the clock
++ * divider: the hsm_clock rate and this divider setting will
++ * give a 40 kHz CEC clock.
++ */
++ value |= VC4_HDMI_CEC_ADDR_MASK |
++ (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
++ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
++ ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
++ vc4_cec_irq_handler,
++ vc4_cec_irq_handler_thread, 0,
++ "vc4 hdmi cec", vc4_hdmi);
++ if (ret)
++ goto err_delete_cec_adap;
++
++ ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
++ if (ret < 0)
++ goto err_delete_cec_adap;
++
++ return 0;
++
++err_delete_cec_adap:
++ cec_delete_adapter(vc4_hdmi->cec_adap);
++
++ return ret;
++}
++
++static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi)
++{
++ cec_unregister_adapter(vc4_hdmi->cec_adap);
++}
++#else
++static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
++{
++ return 0;
++}
++
++static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {};
++
+ #endif
+
+ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
+@@ -1255,9 +1316,6 @@ static int vc4_hdmi_init_resources(struc
+
+ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+ {
+-#ifdef CONFIG_DRM_VC4_HDMI_CEC
+- struct cec_connector_info conn_info;
+-#endif
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
+ const struct vc4_hdmi_variant *variant;
+@@ -1345,43 +1403,13 @@ static int vc4_hdmi_bind(struct device *
+ if (ret)
+ goto err_destroy_encoder;
+
+-#ifdef CONFIG_DRM_VC4_HDMI_CEC
+- vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+- vc4_hdmi, "vc4",
+- CEC_CAP_DEFAULTS |
+- CEC_CAP_CONNECTOR_INFO, 1);
+- ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
+- if (ret < 0)
+- goto err_destroy_conn;
+-
+- cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
+- cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
+-
+- HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
+- value = HDMI_READ(HDMI_CEC_CNTRL_1);
+- value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
+- /*
+- * Set the logical address to Unregistered and set the clock
+- * divider: the hsm_clock rate and this divider setting will
+- * give a 40 kHz CEC clock.
+- */
+- value |= VC4_HDMI_CEC_ADDR_MASK |
+- (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+- HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+- ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+- vc4_cec_irq_handler,
+- vc4_cec_irq_handler_thread, 0,
+- "vc4 hdmi cec", vc4_hdmi);
++ ret = vc4_hdmi_cec_init(vc4_hdmi);
+ if (ret)
+- goto err_delete_cec_adap;
+- ret = cec_register_adapter(vc4_hdmi->cec_adap, dev);
+- if (ret < 0)
+- goto err_delete_cec_adap;
+-#endif
++ goto err_destroy_conn;
+
+ ret = vc4_hdmi_audio_init(vc4_hdmi);
+ if (ret)
+- goto err_destroy_encoder;
++ goto err_free_cec;
+
+ vc4_debugfs_add_file(drm,
+ variant->id ? "hdmi1_regs" : "hdmi_regs",
+@@ -1390,12 +1418,10 @@ static int vc4_hdmi_bind(struct device *
+
+ return 0;
+
+-#ifdef CONFIG_DRM_VC4_HDMI_CEC
+-err_delete_cec_adap:
+- cec_delete_adapter(vc4_hdmi->cec_adap);
++err_free_cec:
++ vc4_hdmi_cec_exit(vc4_hdmi);
+ err_destroy_conn:
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+-#endif
+ err_destroy_encoder:
+ vc4_hdmi_encoder_destroy(encoder);
+ err_unprepare_hsm:
+@@ -1420,7 +1446,7 @@ static void vc4_hdmi_unbind(struct devic
+ kfree(vc4_hdmi->hdmi_regset.regs);
+ kfree(vc4_hdmi->hd_regset.regs);
+
+- cec_unregister_adapter(vc4_hdmi->cec_adap);
++ vc4_hdmi_cec_exit(vc4_hdmi);
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch
new file mode 100644
index 0000000000..33ecf44a27
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch
@@ -0,0 +1,47 @@
+From 0f626dc8443a93138806b4a3f351bac346036358 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 6 Feb 2020 16:22:50 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add CEC support flag
+
+Similarly to the audio support, CEC support is not there yet for the
+BCM2711, so let's skip entirely the CEC initialization through a variant
+flag.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1186,6 +1186,9 @@ static int vc4_hdmi_cec_init(struct vc4_
+ u32 value;
+ int ret;
+
++ if (!vc4_hdmi->variant->cec_available)
++ return 0;
++
+ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+ vc4_hdmi, "vc4",
+ CEC_CAP_DEFAULTS |
+@@ -1474,6 +1477,7 @@ static int vc4_hdmi_dev_remove(struct pl
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
+ .audio_available = true,
++ .cec_available = true,
+ .registers = vc4_hdmi_fields,
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -35,6 +35,9 @@ struct vc4_hdmi_variant {
+ /* Set to true when the audio support is available */
+ bool audio_available;
+
++ /* Set to true when the CEC support is available */
++ bool cec_available;
++
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch b/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch
new file mode 100644
index 0000000000..4befec6c05
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch
@@ -0,0 +1,23 @@
+From a1a87ba39e7fad93cbbb5ea178a12d0c669a9812 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 10 Feb 2020 15:15:47 +0100
+Subject: [PATCH] drm/vc4: hdmi: Remove unused CEC_CLOCK_DIV define
+
+The CEC_CLOCK_DIV define is not used anywhere in the driver, let's remove
+it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -54,7 +54,6 @@
+
+ #define HSM_CLOCK_FREQ 163682864
+ #define CEC_CLOCK_FREQ 40000
+-#define CEC_CLOCK_DIV (HSM_CLOCK_FREQ / CEC_CLOCK_FREQ)
+
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+ {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch b/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch
new file mode 100644
index 0000000000..5c0ab9768b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch
@@ -0,0 +1,26 @@
+From dfc6e670144207251dc0902d1756bc89ef6cd1dc Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 12:31:09 +0100
+Subject: [PATCH] drm/vc4: hdmi: Rename drm_encoder pointer in
+ mode_valid
+
+The mode_valid hook on the encoder uses a pointer to a drm_encoder called
+crtc, which is pretty confusing. Let's rename it to encoder to make it
+clear what it is.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -559,7 +559,7 @@ static void vc4_hdmi_encoder_enable(stru
+ }
+
+ static enum drm_mode_status
+-vc4_hdmi_encoder_mode_valid(struct drm_encoder *crtc,
++vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode)
+ {
+ /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch b/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch
new file mode 100644
index 0000000000..4dba81b035
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch
@@ -0,0 +1,163 @@
+From 3c33724058852d7c58d77d03e11ca545fb04256a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 10 Feb 2020 15:23:06 +0100
+Subject: [PATCH] drm/vc4: hdmi: Adjust HSM clock rate depending on
+ pixel rate
+
+The HSM clock needs to be setup at around 110% of the pixel rate. This
+was done previously by setting the clock rate to 148.5MHz * 108% at
+probe time and only check in mode_valid whether the mode pixel clock was
+under 148.5MHz or not.
+
+However, with 4k we need to change that frequency to a higher frequency
+than 148.5MHz.
+
+Let's change that logic a bit by setting the clock rate of the HSM clock
+to the pixel rate at encoder_enable time. This would work for the
+BCM2711 that support 4k resolutions and has a clock that can provide it,
+but we still have to take care of a 4k panel plugged on a BCM283x SoCs
+that wouldn't be able to use those modes, so let's define the limit in
+the variant.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 51 +++++++++++++++++-----------------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ++
+ 2 files changed, 29 insertions(+), 25 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -52,7 +52,6 @@
+ #include "vc4_hdmi_regs.h"
+ #include "vc4_regs.h"
+
+-#define HSM_CLOCK_FREQ 163682864
+ #define CEC_CLOCK_FREQ 40000
+
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+@@ -329,6 +328,7 @@ static void vc4_hdmi_encoder_disable(str
+ HDMI_WRITE(HDMI_VID_CTL,
+ HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
++ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+
+ ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
+@@ -426,6 +426,7 @@ static void vc4_hdmi_encoder_enable(stru
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+ bool debug_dump_regs = false;
++ unsigned long pixel_rate, hsm_rate;
+ int ret;
+
+ ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev);
+@@ -434,9 +435,8 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
+- ret = clk_set_rate(vc4_hdmi->pixel_clock,
+- mode->clock * 1000 *
+- ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
++ pixel_rate = mode->clock * 1000 * ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1);
++ ret = clk_set_rate(vc4_hdmi->pixel_clock, pixel_rate);
+ if (ret) {
+ DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+ return;
+@@ -448,6 +448,24 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
++ /*
++ * The HSM rate needs to be at 108% of the pixel clock, with a
++ * minimum of 108MHz.
++ */
++ hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 108);
++ ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate);
++ if (ret) {
++ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
++ return;
++ }
++
++ ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
++ if (ret) {
++ DRM_ERROR("Failed to turn on HSM clock: %d\n", ret);
++ clk_disable_unprepare(vc4_hdmi->pixel_clock);
++ return;
++ }
++
+ if (vc4_hdmi->variant->reset)
+ vc4_hdmi->variant->reset(vc4_hdmi);
+
+@@ -578,7 +596,9 @@ vc4_hdmi_encoder_mode_valid(struct drm_e
+ * Additionally, the AXI clock needs to be at least 25% of
+ * pixel clock, but HSM ends up being the limiting factor.
+ */
+- if (mode->clock > HSM_CLOCK_FREQ / (1000 * 101 / 100))
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++
++ if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+@@ -1353,23 +1373,6 @@ static int vc4_hdmi_bind(struct device *
+ return -EPROBE_DEFER;
+ }
+
+- /* This is the rate that is set by the firmware. The number
+- * needs to be a bit higher than the pixel clock rate
+- * (generally 148.5Mhz).
+- */
+- ret = clk_set_rate(vc4_hdmi->hsm_clock, HSM_CLOCK_FREQ);
+- if (ret) {
+- DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+- goto err_put_i2c;
+- }
+-
+- ret = clk_prepare_enable(vc4_hdmi->hsm_clock);
+- if (ret) {
+- DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+- ret);
+- goto err_put_i2c;
+- }
+-
+ /* Only use the GPIO HPD pin if present in the DT, otherwise
+ * we'll use the HDMI core's register.
+ */
+@@ -1427,9 +1430,7 @@ err_destroy_conn:
+ err_destroy_encoder:
+ vc4_hdmi_encoder_destroy(encoder);
+ err_unprepare_hsm:
+- clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+-err_put_i2c:
+ put_device(&vc4_hdmi->ddc->dev);
+
+ return ret;
+@@ -1452,7 +1453,6 @@ static void vc4_hdmi_unbind(struct devic
+ vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+ vc4_hdmi_encoder_destroy(&vc4_hdmi->encoder.base.base);
+
+- clk_disable_unprepare(vc4_hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+
+ put_device(&vc4_hdmi->ddc->dev);
+@@ -1475,6 +1475,7 @@ static int vc4_hdmi_dev_remove(struct pl
+ }
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
++ .max_pixel_clock = 148500000,
+ .audio_available = true,
+ .cec_available = true,
+ .registers = vc4_hdmi_fields,
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -38,6 +38,9 @@ struct vc4_hdmi_variant {
+ /* Set to true when the CEC support is available */
+ bool cec_available;
+
++ /* Maximum pixel clock supported by the controller (in Hz) */
++ unsigned long long max_pixel_clock;
++
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch b/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch
new file mode 100644
index 0000000000..d511f1e212
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch
@@ -0,0 +1,1131 @@
+From d0931317c51f14bf65af65e7c3f2df6bb26d7c97 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 17 Dec 2019 11:48:37 +0100
+Subject: [PATCH] drm/vc4: hdmi: Support the BCM2711 HDMI controllers
+
+Now that the driver is ready for it, let's bring in the HDMI controllers
+variants for the BCM2711.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 254 +++++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 35 +++
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 469 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 201 ++++++++++++
+ 4 files changed, 959 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -42,6 +42,7 @@
+ #include <linux/of_platform.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/rational.h>
++#include <linux/reset.h>
+ #include <sound/dmaengine_pcm.h>
+ #include <sound/pcm_drm_eld.h>
+ #include <sound/pcm_params.h>
+@@ -52,6 +53,31 @@
+ #include "vc4_hdmi_regs.h"
+ #include "vc4_regs.h"
+
++#define VC5_HDMI_HORZA_HFP_SHIFT 16
++#define VC5_HDMI_HORZA_HFP_MASK VC4_MASK(28, 16)
++#define VC5_HDMI_HORZA_VPOS BIT(15)
++#define VC5_HDMI_HORZA_HPOS BIT(14)
++#define VC5_HDMI_HORZA_HAP_SHIFT 0
++#define VC5_HDMI_HORZA_HAP_MASK VC4_MASK(13, 0)
++
++#define VC5_HDMI_HORZB_HBP_SHIFT 16
++#define VC5_HDMI_HORZB_HBP_MASK VC4_MASK(26, 16)
++#define VC5_HDMI_HORZB_HSP_SHIFT 0
++#define VC5_HDMI_HORZB_HSP_MASK VC4_MASK(10, 0)
++
++#define VC5_HDMI_VERTA_VSP_SHIFT 24
++#define VC5_HDMI_VERTA_VSP_MASK VC4_MASK(28, 24)
++#define VC5_HDMI_VERTA_VFP_SHIFT 16
++#define VC5_HDMI_VERTA_VFP_MASK VC4_MASK(22, 16)
++#define VC5_HDMI_VERTA_VAL_SHIFT 0
++#define VC5_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0)
++
++#define VC5_HDMI_VERTB_VSPO_SHIFT 16
++#define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16)
++
++# define VC4_HD_M_SW_RST BIT(2)
++# define VC4_HD_M_ENABLE BIT(0)
++
+ #define CEC_CLOCK_FREQ 40000
+
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+@@ -75,6 +101,13 @@ static void vc4_hdmi_reset(struct vc4_hd
+ HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
+ }
+
++static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
++{
++ reset_control_reset(vc4_hdmi->reset);
++
++ HDMI_WRITE(HDMI_DVP_CTL, 0);
++}
++
+ static enum drm_connector_status
+ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
+ {
+@@ -371,6 +404,45 @@ static void vc4_hdmi_csc_setup(struct vc
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+ }
+
++static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, bool enable)
++{
++ u32 csc_ctl;
++
++ csc_ctl = 0x07; /* RGB_CONVERT_MODE = custom matrix, || USE_RGB_TO_YCBCR */
++
++ if (enable) {
++ /* CEA VICs other than #1 requre limited range RGB
++ * output unless overridden by an AVI infoframe.
++ * Apply a colorspace conversion to squash 0-255 down
++ * to 16-235. The matrix here is:
++ *
++ * [ 0.8594 0 0 16]
++ * [ 0 0.8594 0 16]
++ * [ 0 0 0.8594 16]
++ * [ 0 0 0 1]
++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
++ */
++ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x1b80);
++ HDMI_WRITE(HDMI_CSC_14_13, (0x0400 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_22_21, (0x1b80 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_24_23, (0x0400 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_34_33, (0x0400 << 16) | 0x1b80);
++ } else {
++ /* Still use the matrix for full range, but make it unity.
++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
++ */
++ HDMI_WRITE(HDMI_CSC_12_11, (0x0000 << 16) | 0x2000);
++ HDMI_WRITE(HDMI_CSC_14_13, (0x0000 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_22_21, (0x2000 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_24_23, (0x0000 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_32_31, (0x0000 << 16) | 0x0000);
++ HDMI_WRITE(HDMI_CSC_34_33, (0x0000 << 16) | 0x2000);
++ }
++
++ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
++}
++
+ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
+ struct drm_display_mode *mode)
+ {
+@@ -420,6 +492,58 @@ static void vc4_hdmi_set_timings(struct
+ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+ }
+
++static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode)
++{
++ bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
++ bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
++ bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
++ u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
++ u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
++ VC5_HDMI_VERTA_VSP) |
++ VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
++ VC5_HDMI_VERTA_VFP) |
++ VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
++ u32 vertb = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
++ VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
++ VC4_HDMI_VERTB_VBP));
++ u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
++ VC4_SET_FIELD(mode->crtc_vtotal -
++ mode->crtc_vsync_end -
++ interlaced,
++ VC4_HDMI_VERTB_VBP));
++
++ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
++ HDMI_WRITE(HDMI_HORZA,
++ (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) |
++ (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) |
++ VC4_SET_FIELD(mode->hdisplay * pixel_rep,
++ VC5_HDMI_HORZA_HAP) |
++ VC4_SET_FIELD((mode->hsync_start -
++ mode->hdisplay) * pixel_rep,
++ VC5_HDMI_HORZA_HFP));
++
++ HDMI_WRITE(HDMI_HORZB,
++ VC4_SET_FIELD((mode->htotal -
++ mode->hsync_end) * pixel_rep,
++ VC5_HDMI_HORZB_HBP) |
++ VC4_SET_FIELD((mode->hsync_end -
++ mode->hsync_start) * pixel_rep,
++ VC5_HDMI_HORZB_HSP));
++
++ HDMI_WRITE(HDMI_VERTA0, verta);
++ HDMI_WRITE(HDMI_VERTA1, verta);
++
++ HDMI_WRITE(HDMI_VERTB0, vertb_even);
++ HDMI_WRITE(HDMI_VERTB1, vertb);
++
++ HDMI_WRITE(HDMI_VID_CTL,
++ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
++ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
++
++ HDMI_WRITE(HDMI_CLOCK_STOP, 0);
++}
++
+ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+ {
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
+@@ -1336,6 +1460,92 @@ static int vc4_hdmi_init_resources(struc
+ return 0;
+ }
+
++static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
++{
++ struct platform_device *pdev = vc4_hdmi->pdev;
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->hdmicore_regs = devm_ioremap(dev, res->start,
++ resource_size(res));
++ if (IS_ERR(vc4_hdmi->hdmicore_regs))
++ return PTR_ERR(vc4_hdmi->hdmicore_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hd");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->hd_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->hd_regs))
++ return PTR_ERR(vc4_hdmi->hd_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cec");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->cec_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->cec_regs))
++ return PTR_ERR(vc4_hdmi->cec_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csc");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->csc_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->csc_regs))
++ return PTR_ERR(vc4_hdmi->csc_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvp");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->dvp_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->dvp_regs))
++ return PTR_ERR(vc4_hdmi->dvp_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->phy_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->phy_regs))
++ return PTR_ERR(vc4_hdmi->phy_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "packet");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->ram_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->ram_regs))
++ return PTR_ERR(vc4_hdmi->ram_regs);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rm");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->rm_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->rm_regs))
++ return PTR_ERR(vc4_hdmi->rm_regs);
++
++ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
++ if (IS_ERR(vc4_hdmi->hsm_clock)) {
++ DRM_ERROR("Failed to get HDMI state machine clock\n");
++ return PTR_ERR(vc4_hdmi->hsm_clock);
++ }
++
++ vc4_hdmi->reset = devm_reset_control_get(dev, NULL);
++ if (IS_ERR(vc4_hdmi->reset)) {
++ DRM_ERROR("Failed to get HDMI reset line\n");
++ return PTR_ERR(vc4_hdmi->reset);
++ }
++
++ return 0;
++}
++
+ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+@@ -1491,8 +1701,52 @@ static const struct vc4_hdmi_variant bcm
+ .phy_rng_disable = vc4_hdmi_phy_rng_disable,
+ };
+
++static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
++ .id = 0,
++ .max_pixel_clock = 297000000,
++ .registers = vc5_hdmi_hdmi0_fields,
++ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
++ .phy_lane_mapping = {
++ PHY_LANE_0,
++ PHY_LANE_1,
++ PHY_LANE_2,
++ PHY_LANE_CK,
++ },
++
++ .init_resources = vc5_hdmi_init_resources,
++ .csc_setup = vc5_hdmi_csc_setup,
++ .reset = vc5_hdmi_reset,
++ .set_timings = vc5_hdmi_set_timings,
++ .phy_init = vc5_hdmi_phy_init,
++ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
++ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
++};
++
++static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
++ .id = 1,
++ .max_pixel_clock = 297000000,
++ .registers = vc5_hdmi_hdmi1_fields,
++ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
++ .phy_lane_mapping = {
++ PHY_LANE_1,
++ PHY_LANE_0,
++ PHY_LANE_CK,
++ PHY_LANE_2,
++ },
++
++ .init_resources = vc5_hdmi_init_resources,
++ .csc_setup = vc5_hdmi_csc_setup,
++ .reset = vc5_hdmi_reset,
++ .set_timings = vc5_hdmi_set_timings,
++ .phy_init = vc5_hdmi_phy_init,
++ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
++ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
++};
++
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+ { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant },
++ { .compatible = "brcm,bcm2711-hdmi0", .data = &bcm2711_hdmi0_variant },
++ { .compatible = "brcm,bcm2711-hdmi1", .data = &bcm2711_hdmi1_variant },
+ {}
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -26,6 +26,13 @@ struct drm_display_mode;
+ struct vc4_hdmi;
+ struct vc4_hdmi_register;
+
++enum vc4_hdmi_phy_channel {
++ PHY_LANE_0 = 0,
++ PHY_LANE_1,
++ PHY_LANE_2,
++ PHY_LANE_CK,
++};
++
+ struct vc4_hdmi_variant {
+ /* On devices that have multiple, different instances (like
+ * the BCM2711), which instance is that variant useful for.
+@@ -47,6 +54,13 @@ struct vc4_hdmi_variant {
+ /* Number of registers on that variant */
+ unsigned int num_registers;
+
++ /* BCM2711 Only.
++ * The variants don't map the lane in the same order in the
++ * PHY, so this is an array mapping the HDMI channel (index)
++ * to the PHY lane (value).
++ */
++ enum vc4_hdmi_phy_channel phy_lane_mapping[4];
++
+ /* Callback to get the resources (memory region, interrupts,
+ * clocks, etc) for that variant.
+ */
+@@ -102,6 +116,20 @@ struct vc4_hdmi {
+ struct i2c_adapter *ddc;
+ void __iomem *hdmicore_regs;
+ void __iomem *hd_regs;
++
++ /* VC5 Only */
++ void __iomem *cec_regs;
++ /* VC5 Only */
++ void __iomem *csc_regs;
++ /* VC5 Only */
++ void __iomem *dvp_regs;
++ /* VC5 Only */
++ void __iomem *phy_regs;
++ /* VC5 Only */
++ void __iomem *ram_regs;
++ /* VC5 Only */
++ void __iomem *rm_regs;
++
+ int hpd_gpio;
+ bool hpd_active_low;
+
+@@ -113,6 +141,8 @@ struct vc4_hdmi {
+ struct clk *pixel_clock;
+ struct clk *hsm_clock;
+
++ struct reset_control *reset;
++
+ struct debugfs_regset32 hdmi_regset;
+ struct debugfs_regset32 hd_regset;
+ };
+@@ -137,4 +167,9 @@ void vc4_hdmi_phy_disable(struct vc4_hdm
+ void vc4_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
+ void vc4_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
+
++void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++ struct drm_display_mode *mode);
++void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
++void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
++
+ #endif /* _VC4_HDMI_H_ */
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -10,6 +10,123 @@
+ #include "vc4_regs.h"
+ #include "vc4_hdmi_regs.h"
+
++#define VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB BIT(5)
++#define VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB BIT(4)
++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET BIT(3)
++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET BIT(2)
++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET BIT(1)
++#define VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET BIT(0)
++
++#define VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN BIT(4)
++
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_SHIFT 29
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP_MASK VC4_MASK(31, 29)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_SHIFT 24
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV_MASK VC4_MASK(28, 24)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_SHIFT 21
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP_MASK VC4_MASK(23, 21)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_SHIFT 16
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV_MASK VC4_MASK(20, 16)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_SHIFT 13
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP_MASK VC4_MASK(15, 13)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_SHIFT 8
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV_MASK VC4_MASK(12, 8)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_SHIFT 5
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP_MASK VC4_MASK(7, 5)
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_SHIFT 0
++#define VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV_MASK VC4_MASK(4, 0)
++
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_SHIFT 15
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2_MASK VC4_MASK(19, 15)
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_SHIFT 10
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1_MASK VC4_MASK(14, 10)
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_SHIFT 5
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0_MASK VC4_MASK(9, 5)
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_SHIFT 0
++#define VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK_MASK VC4_MASK(4, 0)
++
++#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_SHIFT 16
++#define VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN_MASK VC4_MASK(19, 16)
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_SHIFT 12
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2_MASK VC4_MASK(15, 12)
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_SHIFT 8
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1_MASK VC4_MASK(11, 8)
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_SHIFT 4
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0_MASK VC4_MASK(7, 4)
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_SHIFT 0
++#define VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK_MASK VC4_MASK(3, 0)
++
++#define VC4_HDMI_TX_PHY_CTL_3_RP_SHIFT 17
++#define VC4_HDMI_TX_PHY_CTL_3_RP_MASK VC4_MASK(19, 17)
++#define VC4_HDMI_TX_PHY_CTL_3_RZ_SHIFT 12
++#define VC4_HDMI_TX_PHY_CTL_3_RZ_MASK VC4_MASK(16, 12)
++#define VC4_HDMI_TX_PHY_CTL_3_CP1_SHIFT 10
++#define VC4_HDMI_TX_PHY_CTL_3_CP1_MASK VC4_MASK(11, 10)
++#define VC4_HDMI_TX_PHY_CTL_3_CP_SHIFT 8
++#define VC4_HDMI_TX_PHY_CTL_3_CP_MASK VC4_MASK(9, 8)
++#define VC4_HDMI_TX_PHY_CTL_3_CZ_SHIFT 6
++#define VC4_HDMI_TX_PHY_CTL_3_CZ_MASK VC4_MASK(7, 6)
++#define VC4_HDMI_TX_PHY_CTL_3_ICP_SHIFT 0
++#define VC4_HDMI_TX_PHY_CTL_3_ICP_MASK VC4_MASK(5, 0)
++
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE BIT(13)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VC_RANGE_EN BIT(12)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_LOW BIT(11)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_EMULATE_VC_HIGH BIT(10)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_SHIFT 9
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL_MASK VC4_MASK(9, 9)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_FB_DIV2 BIT(8)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_POST_DIV2 BIT(7)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN BIT(6)
++#define VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK BIT(5)
++
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_SHIFT 16
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_CPP_MASK VC4_MASK(27, 16)
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_SHIFT 14
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY_MASK VC4_MASK(15, 14)
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE BIT(13)
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_SHIFT 11
++#define VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL_MASK VC4_MASK(12, 11)
++
++#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_SHIFT 8
++#define VC4_HDMI_TX_PHY_CLK_DIV_VCO_MASK VC4_MASK(15, 8)
++
++#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_SHIFT 0
++#define VC4_HDMI_TX_PHY_PLL_CFG_PDIV_MASK VC4_MASK(3, 0)
++
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_MASK VC4_MASK(13, 12)
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL_SHIFT 12
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_MASK VC4_MASK(9, 8)
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL_SHIFT 8
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_MASK VC4_MASK(5, 4)
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL_SHIFT 4
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_MASK VC4_MASK(1, 0)
++#define VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL_SHIFT 0
++
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK VC4_MASK(27, 0)
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_SHIFT 0
++
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK VC4_MASK(27, 0)
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_SHIFT 0
++
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_MASK VC4_MASK(31, 16)
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD_SHIFT 16
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_MASK VC4_MASK(15, 0)
++#define VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD_SHIFT 0
++
++#define VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS BIT(19)
++#define VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR BIT(17)
++#define VC4_HDMI_RM_CONTROL_FREE_RUN BIT(4)
++
++#define VC4_HDMI_RM_OFFSET_ONLY BIT(31)
++#define VC4_HDMI_RM_OFFSET_OFFSET_SHIFT 0
++#define VC4_HDMI_RM_OFFSET_OFFSET_MASK VC4_MASK(30, 0)
++
++#define VC4_HDMI_RM_FORMAT_SHIFT_SHIFT 24
++#define VC4_HDMI_RM_FORMAT_SHIFT_MASK VC4_MASK(25, 24)
++
++#define OSCILLATOR_FREQUENCY 54000000
++
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
+ {
+ /* PHY should be in reset, like
+@@ -38,3 +155,355 @@ void vc4_hdmi_phy_rng_disable(struct vc4
+ HDMI_READ(HDMI_TX_PHY_CTL_0) |
+ VC4_HDMI_TX_PHY_RNG_PWRDN);
+ }
++
++static unsigned long long
++phy_get_vco_freq(unsigned long long clock, u8 *vco_sel, u8 *vco_div)
++{
++ unsigned long long vco_freq = clock;
++ unsigned int _vco_div = 0;
++ unsigned int _vco_sel = 0;
++
++ while (vco_freq < 3000000000ULL) {
++ _vco_div++;
++ vco_freq = clock * _vco_div * 10;
++ }
++
++ if (vco_freq > 4500000000ULL)
++ _vco_sel = 1;
++
++ *vco_sel = _vco_sel;
++ *vco_div = _vco_div;
++
++ return vco_freq;
++}
++
++static u8 phy_get_cp_current(unsigned long vco_freq)
++{
++ if (vco_freq < 3700000000ULL)
++ return 0x1c;
++
++ return 0xc8;
++}
++
++static u32 phy_get_rm_offset(unsigned long long vco_freq)
++{
++ unsigned long long fref = OSCILLATOR_FREQUENCY;
++ uint64_t offset = 0;
++
++ /* RM offset is stored as 9.22 format */
++ offset = vco_freq * 2;
++ do_div(offset, fref);
++ offset = offset << 22;
++ offset >>= 2;
++
++ return offset;
++}
++
++static u8 phy_get_vco_gain(unsigned long long vco_freq)
++{
++ if (vco_freq < 3350000000ULL)
++ return 0xf;
++
++ if (vco_freq < 3700000000ULL)
++ return 0xc;
++
++ if (vco_freq < 4050000000ULL)
++ return 0x6;
++
++ if (vco_freq < 4800000000ULL)
++ return 0x5;
++
++ if (vco_freq < 5200000000ULL)
++ return 0x7;
++
++ return 0x2;
++}
++
++struct phy_lane_settings {
++ struct {
++ u8 preemphasis;
++ u8 main_driver;
++ } amplitude;
++
++ u8 res_sel_data;
++ u8 term_res_sel_data;
++};
++
++struct phy_settings {
++ unsigned long long min_rate;
++ unsigned long long max_rate;
++ struct phy_lane_settings channel[3];
++ struct phy_lane_settings clock;
++};
++
++static const struct phy_settings vc5_hdmi_phy_settings[] =
++{
++ {
++ 0, 50000000,
++ {
++ {{0x0, 0x0A}, 0x12, 0x0},
++ {{0x0, 0x0A}, 0x12, 0x0},
++ {{0x0, 0x0A}, 0x12, 0x0}
++ },
++ {{0x0, 0x0A}, 0x18, 0x0},
++ },
++ {
++ 50000001, 75000000,
++ {
++ {{0x0, 0x09}, 0x12, 0x0},
++ {{0x0, 0x09}, 0x12, 0x0},
++ {{0x0, 0x09}, 0x12, 0x0}
++ },
++ {{0x0, 0x0C}, 0x18, 0x3},
++ },
++ {
++ 75000001, 165000000,
++ {
++ {{0x0, 0x09}, 0x12, 0x0},
++ {{0x0, 0x09}, 0x12, 0x0},
++ {{0x0, 0x09}, 0x12, 0x0}
++ },
++ {{0x0, 0x0C}, 0x18, 0x3},
++ },
++ {
++ 165000001, 250000000,
++ {
++ {{0x0, 0x0F}, 0x12, 0x1},
++ {{0x0, 0x0F}, 0x12, 0x1},
++ {{0x0, 0x0F}, 0x12, 0x1}
++ },
++ {{0x0, 0x0C}, 0x18, 0x3},
++ },
++ {
++ 250000001, 340000000,
++ {
++ {{0x2, 0x0D}, 0x12, 0x1},
++ {{0x2, 0x0D}, 0x12, 0x1},
++ {{0x2, 0x0D}, 0x12, 0x1}
++ },
++ {{0x0, 0x0C}, 0x18, 0xF},
++ },
++ {
++ 340000001, 450000000,
++ {
++ {{0x0, 0x1B}, 0x12, 0xF},
++ {{0x0, 0x1B}, 0x12, 0xF},
++ {{0x0, 0x1B}, 0x12, 0xF}
++ },
++ {{0x0, 0x0A}, 0x12, 0xF},
++ },
++ {
++ 450000001, 600000000,
++ {
++ {{0x0, 0x1C}, 0x12, 0xF},
++ {{0x0, 0x1C}, 0x12, 0xF},
++ {{0x0, 0x1C}, 0x12, 0xF}
++ },
++ {{0x0, 0x0B}, 0x13, 0xF},
++ },
++};
++
++static const struct phy_settings *phy_get_settings(unsigned long long tmds_rate)
++{
++ unsigned int count = ARRAY_SIZE(vc5_hdmi_phy_settings);
++ unsigned int i;
++
++ for (i = 0; i < count; i++) {
++ const struct phy_settings *s = &vc5_hdmi_phy_settings[i];
++
++ if (tmds_rate >= s->min_rate && tmds_rate <= s->max_rate)
++ return s;
++ }
++
++ /*
++ * If the pixel clock exceeds our max setting, try the max
++ * setting anyway.
++ */
++ return &vc5_hdmi_phy_settings[count - 1];
++}
++
++static const struct phy_lane_settings *
++phy_get_channel_settings(enum vc4_hdmi_phy_channel chan,
++ unsigned long long tmds_rate)
++{
++ const struct phy_settings *settings = phy_get_settings(tmds_rate);
++
++ if (chan == PHY_LANE_CK)
++ return &settings->clock;
++
++ return &settings->channel[chan];
++}
++
++void vc5_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi, struct drm_display_mode *mode)
++{
++ const struct phy_lane_settings *chan0_settings, *chan1_settings, *chan2_settings, *clock_settings;
++ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
++ unsigned long long pixel_freq = mode->clock * 1000;
++ unsigned long long vco_freq;
++ unsigned char word_sel;
++ u8 vco_sel, vco_div;
++
++ vco_freq = phy_get_vco_freq(pixel_freq, &vco_sel, &vco_div);
++
++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
++ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) &
++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_0_RESET &
++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_1_RESET &
++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_2_RESET &
++ ~VC4_HDMI_TX_PHY_RESET_CTL_TX_CK_RESET);
++
++ HDMI_WRITE(HDMI_RM_CONTROL,
++ HDMI_READ(HDMI_RM_CONTROL) |
++ VC4_HDMI_RM_CONTROL_EN_FREEZE_COUNTERS |
++ VC4_HDMI_RM_CONTROL_EN_LOAD_INTEGRATOR |
++ VC4_HDMI_RM_CONTROL_FREE_RUN);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
++ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1) &
++ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT_MASK) |
++ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1_MIN_LIMIT));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
++ (HDMI_READ(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2) &
++ ~VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT_MASK) |
++ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2_MAX_LIMIT));
++
++ HDMI_WRITE(HDMI_RM_OFFSET,
++ VC4_SET_FIELD(phy_get_rm_offset(vco_freq),
++ VC4_HDMI_RM_OFFSET_OFFSET) |
++ VC4_HDMI_RM_OFFSET_ONLY);
++
++ HDMI_WRITE(HDMI_TX_PHY_CLK_DIV,
++ VC4_SET_FIELD(vco_div, VC4_HDMI_TX_PHY_CLK_DIV_VCO));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
++ VC4_SET_FIELD(0xe147, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_HOLD_THRESHOLD) |
++ VC4_SET_FIELD(0xe14, VC4_HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4_STABLE_THRESHOLD));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_0,
++ VC4_HDMI_TX_PHY_PLL_CTL_0_ENA_VCO_CLK |
++ VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_CONT_EN |
++ VC4_HDMI_TX_PHY_PLL_CTL_0_MASH11_MODE |
++ VC4_SET_FIELD(vco_sel, VC4_HDMI_TX_PHY_PLL_CTL_0_VCO_SEL));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CTL_1,
++ HDMI_READ(HDMI_TX_PHY_PLL_CTL_1) |
++ VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_ENABLE |
++ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_PLL_CTL_1_POST_RST_SEL) |
++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CTL_1_FREQ_DOUBLER_DELAY) |
++ VC4_SET_FIELD(0x8a, VC4_HDMI_TX_PHY_PLL_CTL_1_CPP));
++
++ HDMI_WRITE(HDMI_RM_FORMAT,
++ HDMI_READ(HDMI_RM_FORMAT) |
++ VC4_SET_FIELD(2, VC4_HDMI_RM_FORMAT_SHIFT));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CFG,
++ HDMI_READ(HDMI_TX_PHY_PLL_CFG) |
++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_PLL_CFG_PDIV));
++
++ if (pixel_freq >= 340000000)
++ word_sel = 3;
++ else
++ word_sel = 0;
++ HDMI_WRITE(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, word_sel);
++
++ HDMI_WRITE(HDMI_TX_PHY_CTL_3,
++ VC4_SET_FIELD(phy_get_cp_current(vco_freq),
++ VC4_HDMI_TX_PHY_CTL_3_ICP) |
++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP) |
++ VC4_SET_FIELD(1, VC4_HDMI_TX_PHY_CTL_3_CP1) |
++ VC4_SET_FIELD(3, VC4_HDMI_TX_PHY_CTL_3_CZ) |
++ VC4_SET_FIELD(4, VC4_HDMI_TX_PHY_CTL_3_RP) |
++ VC4_SET_FIELD(6, VC4_HDMI_TX_PHY_CTL_3_RZ));
++
++ chan0_settings =
++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_0],
++ pixel_freq);
++ chan1_settings =
++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_1],
++ pixel_freq);
++ chan2_settings =
++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_2],
++ pixel_freq);
++ clock_settings =
++ phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_CK],
++ pixel_freq);
++
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ VC4_SET_FIELD(chan0_settings->amplitude.preemphasis,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_PREEMP) |
++ VC4_SET_FIELD(chan0_settings->amplitude.main_driver,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_0_MAINDRV) |
++ VC4_SET_FIELD(chan1_settings->amplitude.preemphasis,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_PREEMP) |
++ VC4_SET_FIELD(chan1_settings->amplitude.main_driver,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_1_MAINDRV) |
++ VC4_SET_FIELD(chan2_settings->amplitude.preemphasis,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_PREEMP) |
++ VC4_SET_FIELD(chan2_settings->amplitude.main_driver,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_2_MAINDRV) |
++ VC4_SET_FIELD(clock_settings->amplitude.preemphasis,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_PREEMP) |
++ VC4_SET_FIELD(clock_settings->amplitude.main_driver,
++ VC4_HDMI_TX_PHY_CTL_0_PREEMP_CK_MAINDRV));
++
++ HDMI_WRITE(HDMI_TX_PHY_CTL_1,
++ HDMI_READ(HDMI_TX_PHY_CTL_1) |
++ VC4_SET_FIELD(chan0_settings->res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA0) |
++ VC4_SET_FIELD(chan1_settings->res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA1) |
++ VC4_SET_FIELD(chan2_settings->res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_DATA2) |
++ VC4_SET_FIELD(clock_settings->res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_1_RES_SEL_CK));
++
++ HDMI_WRITE(HDMI_TX_PHY_CTL_2,
++ VC4_SET_FIELD(chan0_settings->term_res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA0) |
++ VC4_SET_FIELD(chan1_settings->term_res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA1) |
++ VC4_SET_FIELD(chan2_settings->term_res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELDATA2) |
++ VC4_SET_FIELD(clock_settings->term_res_sel_data,
++ VC4_HDMI_TX_PHY_CTL_2_TERM_RES_SELCK) |
++ VC4_SET_FIELD(phy_get_vco_gain(vco_freq),
++ VC4_HDMI_TX_PHY_CTL_2_VCO_GAIN));
++
++ HDMI_WRITE(HDMI_TX_PHY_CHANNEL_SWAP,
++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_0],
++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX0_OUT_SEL) |
++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_1],
++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX1_OUT_SEL) |
++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_2],
++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TX2_OUT_SEL) |
++ VC4_SET_FIELD(variant->phy_lane_mapping[PHY_LANE_CK],
++ VC4_HDMI_TX_PHY_CHANNEL_SWAP_TXCK_OUT_SEL));
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) &
++ ~(VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB |
++ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB));
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL,
++ HDMI_READ(HDMI_TX_PHY_RESET_CTL) |
++ VC4_HDMI_TX_PHY_RESET_CTL_PLL_RESETB |
++ VC4_HDMI_TX_PHY_RESET_CTL_PLLDIV_RESETB);
++}
++
++void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
++ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) &
++ ~VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
++}
++
++void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi)
++{
++ HDMI_WRITE(HDMI_TX_PHY_POWERDOWN_CTL,
++ HDMI_READ(HDMI_TX_PHY_POWERDOWN_CTL) |
++ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
++}
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -18,6 +18,12 @@ enum vc4_hdmi_regs {
+ VC4_INVALID = 0,
+ VC4_HDMI,
+ VC4_HD,
++ VC5_CEC,
++ VC5_CSC,
++ VC5_DVP,
++ VC5_PHY,
++ VC5_RAM,
++ VC5_RM,
+ };
+
+ enum vc4_hdmi_field {
+@@ -45,6 +51,7 @@ enum vc4_hdmi_field {
+ HDMI_CEC_TX_DATA_2,
+ HDMI_CEC_TX_DATA_3,
+ HDMI_CEC_TX_DATA_4,
++ HDMI_CLOCK_STOP,
+ HDMI_CORE_REV,
+ HDMI_CRP_CFG,
+ HDMI_CSC_12_11,
+@@ -61,6 +68,7 @@ enum vc4_hdmi_field {
+ */
+ HDMI_CTS_0,
+ HDMI_CTS_1,
++ HDMI_DVP_CTL,
+ HDMI_FIFO_CTL,
+ HDMI_FRAME_COUNT,
+ HDMI_HORZA,
+@@ -93,10 +101,27 @@ enum vc4_hdmi_field {
+ HDMI_RAM_PACKET_CONFIG,
+ HDMI_RAM_PACKET_START,
+ HDMI_RAM_PACKET_STATUS,
++ HDMI_RM_CONTROL,
++ HDMI_RM_FORMAT,
++ HDMI_RM_OFFSET,
+ HDMI_SCHEDULER_CONTROL,
+ HDMI_SW_RESET_CONTROL,
++ HDMI_TX_PHY_CHANNEL_SWAP,
++ HDMI_TX_PHY_CLK_DIV,
+ HDMI_TX_PHY_CTL_0,
++ HDMI_TX_PHY_CTL_1,
++ HDMI_TX_PHY_CTL_2,
++ HDMI_TX_PHY_CTL_3,
++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
++ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
++ HDMI_TX_PHY_PLL_CFG,
++ HDMI_TX_PHY_PLL_CTL_0,
++ HDMI_TX_PHY_PLL_CTL_1,
++ HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_TX_PHY_RESET_CTL,
++ HDMI_TX_PHY_TMDS_CLK_WORD_SEL,
++ HDMI_VEC_INTERFACE_XBAR,
+ HDMI_VERTA0,
+ HDMI_VERTA1,
+ HDMI_VERTB0,
+@@ -119,6 +144,12 @@ struct vc4_hdmi_register {
+
+ #define VC4_HD_REG(reg, offset) _VC4_REG(VC4_HD, reg, offset)
+ #define VC4_HDMI_REG(reg, offset) _VC4_REG(VC4_HDMI, reg, offset)
++#define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset)
++#define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset)
++#define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset)
++#define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset)
++#define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset)
++#define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset)
+
+ static const struct vc4_hdmi_register vc4_hdmi_fields[] = {
+ VC4_HD_REG(HDMI_M_CTL, 0x000c),
+@@ -181,6 +212,158 @@ static const struct vc4_hdmi_register vc
+ VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
+ };
+
++static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = {
++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++ VC4_HD_REG(HDMI_MAI_CTL, 0x0010),
++ VC4_HD_REG(HDMI_MAI_THR, 0x0014),
++ VC4_HD_REG(HDMI_MAI_FMT, 0x0018),
++ VC4_HD_REG(HDMI_MAI_DATA, 0x001c),
++ VC4_HD_REG(HDMI_MAI_SMP, 0x0020),
++ VC4_HD_REG(HDMI_VID_CTL, 0x0044),
++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0060),
++
++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074),
++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4),
++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8),
++ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc),
++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0),
++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0),
++ VC4_HDMI_REG(HDMI_HORZA, 0x0e4),
++ VC4_HDMI_REG(HDMI_HORZB, 0x0e8),
++ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec),
++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
++ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
++ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
++
++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
++
++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020),
++ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034),
++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044),
++ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c),
++
++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++};
++
++static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = {
++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++ VC4_HD_REG(HDMI_MAI_CTL, 0x0030),
++ VC4_HD_REG(HDMI_MAI_THR, 0x0034),
++ VC4_HD_REG(HDMI_MAI_FMT, 0x0038),
++ VC4_HD_REG(HDMI_MAI_DATA, 0x003c),
++ VC4_HD_REG(HDMI_MAI_SMP, 0x0040),
++ VC4_HD_REG(HDMI_VID_CTL, 0x0048),
++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0064),
++
++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x074),
++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0b8),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0bc),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0c4),
++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0c8),
++ VC4_HDMI_REG(HDMI_CTS_0, 0x0cc),
++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d0),
++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e0),
++ VC4_HDMI_REG(HDMI_HORZA, 0x0e4),
++ VC4_HDMI_REG(HDMI_HORZB, 0x0e8),
++ VC4_HDMI_REG(HDMI_VERTA0, 0x0ec),
++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f0),
++ VC4_HDMI_REG(HDMI_VERTA1, 0x0f4),
++ VC4_HDMI_REG(HDMI_VERTB1, 0x0f8),
++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x09c),
++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a0),
++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8),
++
++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
++
++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++ VC5_PHY_REG(HDMI_TX_PHY_POWERDOWN_CTL, 0x004),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_3, 0x014),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_0, 0x01c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CTL_1, 0x020),
++ VC5_PHY_REG(HDMI_TX_PHY_CLK_DIV, 0x028),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x034),
++ VC5_PHY_REG(HDMI_TX_PHY_CHANNEL_SWAP, 0x04c),
++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x044),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1, 0x050),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2, 0x054),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4, 0x05c),
++
++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++};
++
+ static inline
+ void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi,
+ enum vc4_hdmi_regs reg)
+@@ -192,6 +375,24 @@ void __iomem *__vc4_hdmi_get_field_base(
+ case VC4_HDMI:
+ return hdmi->hdmicore_regs;
+
++ case VC5_CSC:
++ return hdmi->csc_regs;
++
++ case VC5_CEC:
++ return hdmi->cec_regs;
++
++ case VC5_DVP:
++ return hdmi->dvp_regs;
++
++ case VC5_PHY:
++ return hdmi->phy_regs;
++
++ case VC5_RAM:
++ return hdmi->ram_regs;
++
++ case VC5_RM:
++ return hdmi->rm_regs;
++
+ default:
+ return NULL;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch b/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch
new file mode 100644
index 0000000000..c41e063fde
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch
@@ -0,0 +1,174 @@
+From 965351ba5a271c0a4a7776193b7af78871370f7a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Feb 2020 16:45:24 +0100
+Subject: [PATCH] dt-bindings: display: vc4: hdmi: Add BCM2711 HDMI
+ controllers bindings
+
+The HDMI controllers found in the BCM2711 SoC need some adjustments to the
+bindings, especially since the registers have been shuffled around in more
+register ranges.
+
+Cc: Rob Herring <robh+dt@kernel.org>
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../bindings/display/brcm,bcm2835-hdmi.yaml | 118 ++++++++++++++++--
+ 1 file changed, 109 insertions(+), 9 deletions(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hdmi.yaml
+@@ -11,24 +11,58 @@ maintainers:
+
+ properties:
+ compatible:
+- const: brcm,bcm2835-hdmi
++ enum:
++ - brcm,bcm2835-hdmi
++ - brcm,bcm2711-hdmi0
++ - brcm,bcm2711-hdmi1
+
+ reg:
++ oneOf:
++ - items:
++ - description: HDMI register range
++ - description: HD register range
++
++ - items:
++ - description: HDMI controller register range
++ - description: DVP register range
++ - description: HDMI PHY register range
++ - description: Rate Manager register range
++ - description: Packet RAM register range
++ - description: Metadata RAM register range
++ - description: CSC register range
++ - description: CEC register range
++ - description: HD register range
++
++ reg-names:
+ items:
+- - description: HDMI register range
+- - description: HD register range
++ - const: hdmi
++ - const: dvp
++ - const: phy
++ - const: rm
++ - const: packet
++ - const: metadata
++ - const: csc
++ - const: cec
++ - const: hd
+
+ interrupts:
+ minItems: 2
+
+ clocks:
+- items:
+- - description: The pixel clock
+- - description: The HDMI state machine clock
++ oneOf:
++ - items:
++ - description: The pixel clock
++ - description: The HDMI state machine clock
++
++ - items:
++ - description: The HDMI state machine clock
+
+ clock-names:
+- items:
+- - const: pixel
++ oneOf:
++ - items:
++ - const: pixel
++ - const: hdmi
++
+ - const: hdmi
+
+ ddc:
+@@ -51,15 +85,54 @@ properties:
+ dma-names:
+ const: audio-rx
+
++ resets:
++ maxItems: 1
++
+ required:
+ - compatible
+ - reg
+- - interrupts
+ - clocks
+ - ddc
+
+ additionalProperties: false
+
++if:
++ properties:
++ compatible:
++ contains:
++ enum:
++ - brcm,bcm2711-hdmi0
++ - brcm,bcm2711-hdmi1
++
++then:
++ properties:
++ reg:
++ minItems: 9
++
++ clocks:
++ maxItems: 1
++
++ clock-names:
++ maxItems: 1
++
++ required:
++ - reg-names
++ - resets
++
++else:
++ properties:
++ reg:
++ maxItems: 2
++
++ clocks:
++ minItems: 2
++
++ clock-names:
++ minItems: 2
++
++ required:
++ - interrupts
++
+ examples:
+ - |
+ #include <dt-bindings/clock/bcm2835.h>
+@@ -77,4 +150,31 @@ examples:
+ clock-names = "pixel", "hdmi";
+ };
+
++ - |
++ hdmi0: hdmi@7ef00700 {
++ compatible = "brcm,bcm2711-hdmi0";
++ reg = <0x7ef00700 0x300>,
++ <0x7ef00300 0x200>,
++ <0x7ef00f00 0x80>,
++ <0x7ef00f80 0x80>,
++ <0x7ef01b00 0x200>,
++ <0x7ef01f00 0x400>,
++ <0x7ef00200 0x80>,
++ <0x7ef04300 0x100>,
++ <0x7ef20000 0x100>;
++ reg-names = "hdmi",
++ "dvp",
++ "phy",
++ "rm",
++ "packet",
++ "metadata",
++ "csc",
++ "cec",
++ "hd";
++ clocks = <&firmware_clocks 13>;
++ clock-names = "hdmi";
++ resets = <&dvp 0>;
++ ddc = <&ddc0>;
++ };
++
+ ...
diff --git a/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch b/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch
new file mode 100644
index 0000000000..aae9b9e00d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch
@@ -0,0 +1,210 @@
+From 661edd663841d94bded4e95acfd0a4947cb079b5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 12 Feb 2020 12:26:40 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Enable the display pipeline
+
+Now that all the drivers have been adjusted for it, let's bring in the
+necessary device tree changes.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 40 ++++++++++
+ arch/arm/boot/dts/bcm2711.dtsi | 110 ++++++++++++++++++++++++++
+ 2 files changed, 150 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -138,6 +138,46 @@
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
++&vc4 {
++ status = "okay";
++};
++
++&pixelvalve0 {
++ status = "okay";
++};
++
++&pixelvalve1 {
++ status = "okay";
++};
++
++&pixelvalve2 {
++ status = "okay";
++};
++
++&pixelvalve3 {
++ status = "okay";
++};
++
++&pixelvalve4 {
++ status = "okay";
++};
++
++&hdmi0 {
++ status = "okay";
++};
++
++&ddc0 {
++ status = "okay";
++};
++
++&hdmi1 {
++ status = "okay";
++};
++
++&ddc1 {
++ status = "okay";
++};
++
+ // =============================================
+ // Downstream rpi- changes
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -31,6 +31,11 @@
+ };
+ };
+
++ vc4: gpu {
++ compatible = "brcm,bcm2711-vc5";
++ status = "disabled";
++ };
++
+ clk_108MHz: clk-108M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+@@ -254,6 +259,27 @@
+ status = "disabled";
+ };
+
++ pixelvalve0: pixelvalve@7e206000 {
++ compatible = "brcm,bcm2711-pixelvalve0";
++ reg = <0x7e206000 0x100>;
++ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ pixelvalve1: pixelvalve@7e207000 {
++ compatible = "brcm,bcm2711-pixelvalve1";
++ reg = <0x7e207000 0x100>;
++ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ pixelvalve2: pixelvalve@7e20a000 {
++ compatible = "brcm,bcm2711-pixelvalve2";
++ reg = <0x7e20a000 0x100>;
++ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
+ pwm1: pwm@7e20c800 {
+ compatible = "brcm,bcm2835-pwm";
+ reg = <0x7e20c800 0x28>;
+@@ -264,6 +290,13 @@
+ status = "disabled";
+ };
+
++ pixelvalve4: pixelvalve@7e216000 {
++ compatible = "brcm,bcm2711-pixelvalve4";
++ reg = <0x7e216000 0x100>;
++ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
+ emmc2: emmc2@7e340000 {
+ compatible = "brcm,bcm2711-emmc2";
+ reg = <0x7e340000 0x100>;
+@@ -276,6 +309,13 @@
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
++ pixelvalve3: pixelvalve@7ec12000 {
++ compatible = "brcm,bcm2711-pixelvalve3";
++ reg = <0x7ec12000 0x100>;
++ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
+ dvp: clock@7ef00000 {
+ compatible = "brcm,brcm2711-dvp";
+ reg = <0x7ef00000 0x10>;
+@@ -283,6 +323,76 @@
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
++
++ hdmi0: hdmi@7ef00700 {
++ compatible = "brcm,bcm2711-hdmi0";
++ reg = <0x7ef00700 0x300>,
++ <0x7ef00300 0x200>,
++ <0x7ef00f00 0x80>,
++ <0x7ef00f80 0x80>,
++ <0x7ef01b00 0x200>,
++ <0x7ef01f00 0x400>,
++ <0x7ef00200 0x80>,
++ <0x7ef04300 0x100>,
++ <0x7ef20000 0x100>;
++ reg-names = "hdmi",
++ "dvp",
++ "phy",
++ "rm",
++ "packet",
++ "metadata",
++ "csc",
++ "cec",
++ "hd";
++ clocks = <&firmware_clocks 13>;
++ clock-names = "hdmi";
++ resets = <&dvp 0>;
++ ddc = <&ddc0>;
++ status = "disabled";
++ };
++
++ ddc0: i2c@7ef04500 {
++ compatible = "brcm,bcm2711-hdmi-i2c";
++ reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>;
++ reg-names = "bsc", "auto-i2c";
++ clock-frequency = <390000>;
++ status = "disabled";
++ };
++
++ hdmi1: hdmi@7ef05700 {
++ compatible = "brcm,bcm2711-hdmi1";
++ reg = <0x7ef05700 0x300>,
++ <0x7ef05300 0x200>,
++ <0x7ef05f00 0x80>,
++ <0x7ef05f80 0x80>,
++ <0x7ef06b00 0x200>,
++ <0x7ef06f00 0x400>,
++ <0x7ef00280 0x80>,
++ <0x7ef09300 0x100>,
++ <0x7ef20000 0x100>;
++ reg-names = "hdmi",
++ "dvp",
++ "phy",
++ "rm",
++ "packet",
++ "metadata",
++ "csc",
++ "cec",
++ "hd";
++ ddc = <&ddc1>;
++ clocks = <&firmware_clocks 13>;
++ clock-names = "hdmi";
++ resets = <&dvp 1>;
++ status = "disabled";
++ };
++
++ ddc1: i2c@7ef09500 {
++ compatible = "brcm,bcm2711-hdmi-i2c";
++ reg = <0x7ef09500 0x100>, <0x7ef05b00 0x300>;
++ reg-names = "bsc", "auto-i2c";
++ clock-frequency = <390000>;
++ status = "disabled";
++ };
+ };
+
+ arm-pmu {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch b/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch
new file mode 100644
index 0000000000..b27d355340
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch
@@ -0,0 +1,90 @@
+From 46369abfb7dd4c33637da4340fa47a5f76f7f1c2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 21 Feb 2020 17:10:45 +0100
+Subject: [PATCH] ARM: dts: rpi4: Disable KMS driver by
+ default
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 48 +++++++++++++++++++++++++++
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 5 ---
+ 2 files changed, 48 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -182,6 +182,14 @@
+ // Downstream rpi- changes
+
+ #include "bcm270x.dtsi"
++
++/ {
++ soc {
++ /delete-node/ pixelvalve@7e807000;
++ /delete-node/ hdmi@7e902000;
++ };
++};
++
+ #include "bcm2711-rpi.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
+
+@@ -476,6 +484,46 @@
+ pinctrl-0 = <&audio_pins>;
+ };
+
++&vc4 {
++ status = "disabled";
++};
++
++&pixelvalve0 {
++ status = "disabled";
++};
++
++&pixelvalve1 {
++ status = "disabled";
++};
++
++&pixelvalve2 {
++ status = "disabled";
++};
++
++&pixelvalve3 {
++ status = "disabled";
++};
++
++&pixelvalve4 {
++ status = "disabled";
++};
++
++&hdmi0 {
++ status = "disabled";
++};
++
++&ddc0 {
++ status = "disabled";
++};
++
++&hdmi1 {
++ status = "disabled";
++};
++
++&ddc1 {
++ status = "disabled";
++};
++
+ / {
+ __overrides__ {
+ act_led_gpio = <&act_led>,"gpios:4";
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -55,11 +55,6 @@
+ status = "okay";
+ };
+
+- vc4: gpu {
+- compatible = "brcm,bcm2835-vc4";
+- status = "disabled";
+- };
+-
+ /delete-node/ audio;
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch b/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch
new file mode 100644
index 0000000000..1c46fa4485
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch
@@ -0,0 +1,241 @@
+From 7f9f7a113e9c5d6efd997de7de93af31ec286174 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.org>
+Date: Fri, 20 Sep 2019 17:20:01 +0100
+Subject: [PATCH] dtoverlays: Add Pi4 version of vc4-kms-v3d
+
+The Pi4 version of the KMS drivers is a work in progress, some
+blocks need alternate configuration, and some blocks currently
+need to remain disabled (eg the VEC).
+
+Add a new overlay (vc4-kms-v3d-pi4) that loads the parts of
+vc4-kms that do work on Pi4.
+This has been tested with DPI and HDMI (not 100% reliable on mode
+switching)
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 14 ++
+ .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 183 ++++++++++++++++++
+ 3 files changed, 198 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -191,6 +191,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ vc4-fkms-v3d.dtbo \
+ vc4-kms-kippah-7inch.dtbo \
+ vc4-kms-v3d.dtbo \
++ vc4-kms-v3d-pi4.dtbo \
+ vga666.dtbo \
+ w1-gpio.dtbo \
+ w1-gpio-pullup.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2684,6 +2684,20 @@ Params: cma-256 CMA is 2
+ audio Enable or disable audio over HDMI (default "on")
+
+
++Name: vc4-kms-v3d-pi4
++Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver for Pi4.
++Load: dtoverlay=vc4-kms-v3d-pi4,<param>
++Params: cma-256 CMA is 256MB
++ cma-192 CMA is 192MB
++ cma-128 CMA is 128MB
++ cma-96 CMA is 96MB
++ cma-64 CMA is 64MB
++ audio Enable or disable audio over HDMI0 (default
++ "on")
++ audio1 Enable or disable audio over HDMI1 (default
++ "on")
++
++
+ Name: vga666
+ Info: Overlay for the Fen Logic VGA666 board
+ This uses GPIOs 2-21 (so no I2C), and activates the output 2-3 seconds
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
+@@ -0,0 +1,183 @@
++/*
++ * vc4-kms-v3d-pi4-overlay.dts
++ */
++
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/bcm2835.h>
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target-path = "/chosen";
++ __overlay__ {
++ bootargs = "cma=256M";
++ };
++ };
++
++ fragment@1 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=192M";
++ };
++ };
++
++ fragment@2 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=128M";
++ };
++ };
++
++ fragment@3 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=96M";
++ };
++ };
++
++ fragment@4 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=64M";
++ };
++ };
++
++ fragment@5 {
++ target = <&ddc0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@6 {
++ target = <&ddc1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@7 {
++ target = <&hdmi0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@8 {
++ target = <&hdmi1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@9 {
++ target = <&hvs>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@10 {
++ target = <&pixelvalve0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@11 {
++ target = <&pixelvalve1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@12 {
++ target = <&pixelvalve2>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@13 {
++ target = <&pixelvalve3>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@14 {
++ target = <&pixelvalve4>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@15 {
++ target = <&v3d>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@16 {
++ target = <&vc4>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@17 {
++ target = <&txp>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@18 {
++ target = <&fb>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@19 {
++ target = <&firmwarekms>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@20 {
++ target = <&vec>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@21 {
++ target = <&hdmi0>;
++ __dormant__ {
++ dmas;
++ };
++ };
++
++ fragment@22 {
++ target = <&hdmi1>;
++ __dormant__ {
++ dmas;
++ };
++ };
++
++ __overrides__ {
++ cma-256 = <0>,"+0-1-2-3-4";
++ cma-192 = <0>,"-0+1-2-3-4";
++ cma-128 = <0>,"-0-1+2-3-4";
++ cma-96 = <0>,"-0-1-2+3-4";
++ cma-64 = <0>,"-0-1-2-3+4";
++ audio = <0>,"!21";
++ audio1 = <0>,"!22";
++ };
++};
diff --git a/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch b/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch
new file mode 100644
index 0000000000..3f0aa4aa02
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch
@@ -0,0 +1,39 @@
+From 60ef8af4bc2d5f8643adbcb69bb1f52e491a96ae Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 27 Jan 2020 10:22:44 +0000
+Subject: [PATCH] drm: Checking of the pitch is only valid for linear
+ formats
+
+framebuffer_check was computing a minimum pitch value and ensuring
+that the provided value was greater than this.
+That check is only valid if the format is linear.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_framebuffer.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/drm_framebuffer.c
++++ b/drivers/gpu/drm/drm_framebuffer.c
+@@ -217,12 +217,16 @@ static int framebuffer_check(struct drm_
+ if (min_pitch > UINT_MAX)
+ return -ERANGE;
+
+- if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
+- return -ERANGE;
++ if (r->modifier[i] == DRM_FORMAT_MOD_LINEAR) {
++ if ((uint64_t)height * r->pitches[i] + r->offsets[i] >
++ UINT_MAX)
++ return -ERANGE;
+
+- if (block_size && r->pitches[i] < min_pitch) {
+- DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
+- return -EINVAL;
++ if (block_size && r->pitches[i] < min_pitch) {
++ DRM_DEBUG_KMS("bad pitch %u for plane %d\n",
++ r->pitches[i], i);
++ return -EINVAL;
++ }
+ }
+
+ if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch b/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch
new file mode 100644
index 0000000000..9d83d8a3dd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch
@@ -0,0 +1,174 @@
+From 87c4b03b9d1180c2f878b19363ec0609b5f24c75 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:25:41 +0000
+Subject: [PATCH] drm/vc4: Add support for DRM_FORMAT_P030 to vc4
+ planes
+
+This currently doesn't handle non-zero source rectangles correctly,
+but add support for DRM_FORMAT_P030 with DRM_FORMAT_MOD_BROADCOM_SAND128
+modifier to planes when running on HVS5.
+
+WIP still for source cropping SAND/P030 formats
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 83 +++++++++++++++++++++++----------
+ 1 file changed, 59 insertions(+), 24 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -33,6 +33,7 @@ static const struct hvs_format {
+ u32 hvs; /* HVS_FORMAT_* */
+ u32 pixel_order;
+ u32 pixel_order_hvs5;
++ bool hvs5_only;
+ } hvs_formats[] = {
+ {
+ .drm = DRM_FORMAT_XRGB8888,
+@@ -128,6 +129,12 @@ static const struct hvs_format {
+ .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
+ .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
+ },
++ {
++ .drm = DRM_FORMAT_P030,
++ .hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT,
++ .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
++ .hvs5_only = true,
++ },
+ };
+
+ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
+@@ -801,27 +808,33 @@ static int vc4_plane_mode_set(struct drm
+ uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
+ u32 tile_w, tile, x_off, pix_per_tile;
+
+- hvs_format = HVS_PIXEL_FORMAT_H264;
+-
+- switch (base_format_mod) {
+- case DRM_FORMAT_MOD_BROADCOM_SAND64:
+- tiling = SCALER_CTL0_TILING_64B;
+- tile_w = 64;
+- break;
+- case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ if (fb->format->format == DRM_FORMAT_P030) {
++ hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
+ tiling = SCALER_CTL0_TILING_128B;
+- tile_w = 128;
+- break;
+- case DRM_FORMAT_MOD_BROADCOM_SAND256:
+- tiling = SCALER_CTL0_TILING_256B_OR_T;
+- tile_w = 256;
+- break;
+- default:
+- break;
+- }
++ tile_w = 96;
++ } else {
++ hvs_format = HVS_PIXEL_FORMAT_H264;
+
++ switch (base_format_mod) {
++ case DRM_FORMAT_MOD_BROADCOM_SAND64:
++ tiling = SCALER_CTL0_TILING_64B;
++ tile_w = 64;
++ break;
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ tiling = SCALER_CTL0_TILING_128B;
++ tile_w = 128;
++ break;
++ case DRM_FORMAT_MOD_BROADCOM_SAND256:
++ tiling = SCALER_CTL0_TILING_256B_OR_T;
++ tile_w = 256;
++ break;
++ default:
++ break;
++ }
++ }
+ if (param > SCALER_TILE_HEIGHT_MASK) {
+- DRM_DEBUG_KMS("SAND height too large (%d)\n", param);
++ DRM_DEBUG_KMS("SAND height too large (%d)\n",
++ param);
+ return -EINVAL;
+ }
+
+@@ -831,6 +844,13 @@ static int vc4_plane_mode_set(struct drm
+
+ /* Adjust the base pointer to the first pixel to be scanned
+ * out.
++ *
++ * For P030, y_ptr [31:4] is the 128bit word for the start pixel
++ * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
++ * word that should be taken as the first pixel.
++ * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
++ * element within the 128bit word, eg for pixel 3 the value
++ * should be 6.
+ */
+ for (i = 0; i < num_planes; i++) {
+ vc4_state->offsets[i] += param * tile_w * tile;
+@@ -943,8 +963,8 @@ static int vc4_plane_mode_set(struct drm
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(state->alpha >> 4,
+ SCALER5_CTL2_ALPHA) |
+- fb->format->has_alpha ?
+- SCALER5_CTL2_ALPHA_PREMULT : 0 |
++ (fb->format->has_alpha ?
++ SCALER5_CTL2_ALPHA_PREMULT : 0) |
+ (mix_plane_alpha ?
+ SCALER5_CTL2_ALPHA_MIX : 0) |
+ VC4_SET_FIELD(fb->format->has_alpha ?
+@@ -992,7 +1012,8 @@ static int vc4_plane_mode_set(struct drm
+
+ /* Pitch word 1/2 */
+ for (i = 1; i < num_planes; i++) {
+- if (hvs_format != HVS_PIXEL_FORMAT_H264) {
++ if (hvs_format != HVS_PIXEL_FORMAT_H264 &&
++ hvs_format != HVS_PIXEL_FORMAT_YCBCR_10BIT) {
+ vc4_dlist_write(vc4_state,
+ VC4_SET_FIELD(fb->pitches[i],
+ SCALER_SRC_PITCH));
+@@ -1361,6 +1382,13 @@ static bool vc4_format_mod_supported(str
+ default:
+ return false;
+ }
++ case DRM_FORMAT_P030:
++ switch (fourcc_mod_broadcom_mod(modifier)) {
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ return true;
++ default:
++ return false;
++ }
+ case DRM_FORMAT_RGBX1010102:
+ case DRM_FORMAT_BGRX1010102:
+ case DRM_FORMAT_RGBA1010102:
+@@ -1393,8 +1421,11 @@ struct drm_plane *vc4_plane_init(struct
+ struct drm_plane *plane = NULL;
+ struct vc4_plane *vc4_plane;
+ u32 formats[ARRAY_SIZE(hvs_formats)];
++ int num_formats = 0;
+ int ret = 0;
+ unsigned i;
++ bool hvs5 = of_device_is_compatible(dev->dev->of_node,
++ "brcm,bcm2711-vc5");
+ static const uint64_t modifiers[] = {
+ DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
+ DRM_FORMAT_MOD_BROADCOM_SAND128,
+@@ -1409,13 +1440,17 @@ struct drm_plane *vc4_plane_init(struct
+ if (!vc4_plane)
+ return ERR_PTR(-ENOMEM);
+
+- for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
+- formats[i] = hvs_formats[i].drm;
++ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
++ if (hvs_formats[i].hvs5_only || hvs5) {
++ formats[num_formats] = hvs_formats[i].drm;
++ num_formats++;
++ }
++ }
+
+ plane = &vc4_plane->base;
+ ret = drm_universal_plane_init(dev, plane, 0,
+ &vc4_plane_funcs,
+- formats, ARRAY_SIZE(formats),
++ formats, num_formats,
+ modifiers, type, NULL);
+
+ drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch b/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch
new file mode 100644
index 0000000000..a24127e6f2
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch
@@ -0,0 +1,26 @@
+From 63423f4f48afc96949a63c53203faa904a85670b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 25 Feb 2020 17:35:10 +0000
+Subject: [PATCH] Fixup P030 support
+
+I got the logic wrong for enabling pixel formats, resulting in
+Pi0-3 only getting a single, invalid, format (P030 SAND).
+
+Fixes: e07ef1d drm/vc4: Add support for DRM_FORMAT_P030 to vc4 planes
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1441,7 +1441,7 @@ struct drm_plane *vc4_plane_init(struct
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+- if (hvs_formats[i].hvs5_only || hvs5) {
++ if (!hvs_formats[i].hvs5_only || hvs5) {
+ formats[num_formats] = hvs_formats[i].drm;
+ num_formats++;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch b/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch
new file mode 100644
index 0000000000..00a65a9b11
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch
@@ -0,0 +1,33 @@
+From 76534156ad6e835ad89135210d565dd5f58e91ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 11 Feb 2020 15:36:59 +0000
+Subject: [PATCH] drm/vc4: The check for assigned HVS channels is not
+ applicable firmware_kms
+
+Channel assignments is only in full KMS, so skip the check
+if in firmware kms mode.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -579,6 +579,7 @@ static int
+ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
+ {
+ unsigned long unassigned_channels = GENMASK(NUM_CHANNELS - 1, 0);
++ struct vc4_dev *vc4 = to_vc4_dev(state->dev);
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int i, ret;
+@@ -590,7 +591,7 @@ vc4_atomic_check(struct drm_device *dev,
+ bool is_assigned = false;
+ unsigned int channel;
+
+- if (!crtc_state->active)
++ if (!crtc_state->active || vc4->firmware_kms)
+ continue;
+
+ /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch
new file mode 100644
index 0000000000..b67f38782f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch
@@ -0,0 +1,23 @@
+From 310d91d120b672d13d83fd4ab7cfb9cff485a1de Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 17 Feb 2020 11:37:21 +0000
+Subject: [PATCH] dt: Update v3d to use firmware_clocks.
+
+Use the updated DT clock-names property to map the v3d clock
+to the firmware_clocks driver, instead of the older clkdev API.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -34,6 +34,7 @@
+ power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
+ resets = <&pm BCM2835_RESET_V3D>;
+ clocks = <&firmware_clocks 5>;
++ clocks-names = "v3d";
+ interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch b/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch
new file mode 100644
index 0000000000..56106a4274
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch
@@ -0,0 +1,72 @@
+From 3e45488069e20b07b83d8cbba88c7fa2b205e559 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:01:04 +0000
+Subject: [PATCH] drm/vc4: Reset audio infoframe on encoder_enable if
+ previously streaming
+
+If the encoder is disabled and re-enabled (eg mode change) all infoframes
+are reset, whilst the audio subsystem know nothing about this change.
+The driver therefore needs to reinstate the audio infoframe for
+itself.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 12 ++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 2 ++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -344,8 +344,16 @@ static void vc4_hdmi_set_audio_infoframe
+
+ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
+ {
++ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
++
+ vc4_hdmi_set_avi_infoframe(encoder);
+ vc4_hdmi_set_spd_infoframe(encoder);
++ /*
++ * If audio was streaming, then we need to reenabled the audio
++ * infoframe here during encoder_enable.
++ */
++ if (vc4_hdmi->audio.streaming)
++ vc4_hdmi_set_audio_infoframe(encoder);
+ }
+
+ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+@@ -825,6 +833,7 @@ static void vc4_hdmi_audio_reset(struct
+ struct device *dev = &vc4_hdmi->pdev->dev;
+ int ret;
+
++ vc4_hdmi->audio.streaming = false;
+ ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO);
+ if (ret)
+ dev_err(dev, "Failed to stop audio infoframe: %d\n", ret);
+@@ -928,6 +937,7 @@ static int vc4_hdmi_audio_trigger(struct
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ vc4_hdmi_set_audio_infoframe(encoder);
++ vc4_hdmi->audio.streaming = true;
+
+ if (vc4_hdmi->variant->phy_rng_enable)
+ vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
+@@ -946,6 +956,8 @@ static int vc4_hdmi_audio_trigger(struct
+ if (vc4_hdmi->variant->phy_rng_disable)
+ vc4_hdmi->variant->phy_rng_disable(vc4_hdmi);
+
++ vc4_hdmi->audio.streaming = false;
++
+ break;
+ default:
+ break;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -101,6 +101,8 @@ struct vc4_hdmi_audio {
+ int channels;
+ struct snd_dmaengine_dai_dma_data dma_data;
+ struct snd_pcm_substream *substream;
++
++ bool streaming;
+ };
+
+ /* General HDMI hardware state. */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch b/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch
new file mode 100644
index 0000000000..4827f96023
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch
@@ -0,0 +1,31 @@
+From 1cf3e20f13378430cd1fc929548bca9f5e517afe Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:03:42 +0000
+Subject: [PATCH] drm/vc4: Set the b-frame marker to the match ALSA's
+ default.
+
+ALSA's iec958 plugin by default sets the block start preamble
+to 8, whilst this driver was programming the hardware to expect
+0xF.
+Amend the hardware config to match ALSA.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -885,10 +885,11 @@ static int vc4_hdmi_audio_hw_params(stru
+
+ vc4_hdmi_audio_set_mai_clock(vc4_hdmi);
+
++ /* The B frame identifier should match the value used by alsa-lib (8) */
+ audio_packet_config =
+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT |
+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
+- VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
++ VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
+
+ channel_mask = GENMASK(vc4_hdmi->audio.channels - 1, 0);
+ audio_packet_config |= VC4_SET_FIELD(channel_mask,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch b/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch
new file mode 100644
index 0000000000..65e206c181
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch
@@ -0,0 +1,27 @@
+From 9f7718ae7edcf5feab81d3c8561e6c5112e0b462 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:07:19 +0000
+Subject: [PATCH] dts: Add reg-names for the HDMI registers on bcm2835
+
+Pi4 is requiring many more register configs in the HDMI
+block, and has switched to using reg-names instead of fixed index
+values.
+
+Switch bc2835-common to match.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2835-common.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2835-common.dtsi
++++ b/arch/arm/boot/dts/bcm2835-common.dtsi
+@@ -110,6 +110,8 @@
+ compatible = "brcm,bcm2835-hdmi";
+ reg = <0x7e902000 0x600>,
+ <0x7e808000 0x100>;
++ reg-names = "hdmi",
++ "hd";
+ interrupts = <2 8>, <2 9>;
+ ddc = <&i2c2>;
+ clocks = <&clocks BCM2835_PLLH_PIX>,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch b/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch
new file mode 100644
index 0000000000..9a9fd06eeb
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch
@@ -0,0 +1,32 @@
+From 4a3b5d7018f3b0d66f412b0b1500b76ab089a2c9 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:08:39 +0000
+Subject: [PATCH] dt: Add HDMI audio dma values to bcm2711.dtsi
+
+Adds the relevant DMA settings for HDMI audio to work.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -348,6 +348,8 @@
+ clock-names = "hdmi";
+ resets = <&dvp 0>;
+ ddc = <&ddc0>;
++ dmas = <&dma 10>;
++ dma-names = "audio-rx";
+ status = "disabled";
+ };
+
+@@ -383,6 +385,8 @@
+ clocks = <&firmware_clocks 13>;
+ clock-names = "hdmi";
+ resets = <&dvp 1>;
++ dmas = <&dma 17>;
++ dma-names = "audio-rx";
+ status = "disabled";
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch b/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch
new file mode 100644
index 0000000000..2504a07083
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch
@@ -0,0 +1,35 @@
+From 7a463d59a0539cdf79ee6f1fe6c52f0a487ee63e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:11:41 +0000
+Subject: [PATCH] drm/vc4: Use reg-names to configure HDMI audio.
+
+HDMI audio configuration was using fixed index numbers to
+load in DT register settings.
+Switch to using reg-names for flexibility and to match Pi4.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1097,6 +1097,7 @@ static int vc4_hdmi_audio_init(struct vc
+ struct snd_soc_card *card = &vc4_hdmi->audio.card;
+ struct device *dev = &vc4_hdmi->pdev->dev;
+ const __be32 *addr;
++ int index;
+ int ret;
+ int len;
+
+@@ -1122,7 +1123,9 @@ static int vc4_hdmi_audio_init(struct vc
+ * for DMA transfers.
+ * This VC/MMU should probably be exposed to avoid this kind of hacks.
+ */
+- addr = of_get_address(dev->of_node, 1, NULL, NULL);
++ index = of_property_match_string(dev->of_node, "reg-names", "hd");
++ addr = of_get_address(dev->of_node, index, NULL, NULL);
++
+ vc4_hdmi->audio.dma_data.addr = be32_to_cpup(addr) + mai_data->offset;
+ vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ vc4_hdmi->audio.dma_data.maxburst = 2;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch b/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch
new file mode 100644
index 0000000000..6ec6850866
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch
@@ -0,0 +1,127 @@
+From 727b5180ec09faab313d7e2517e225001c967bb0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:16:14 +0000
+Subject: [PATCH] drm/vc4: Add audio initialisation for Pi4.
+
+The audio configuration has changed for Pi4, so support the
+configuration functions via the variant tables.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 51 ++++++++++++++++++++++++++++------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 6 ++++
+ 2 files changed, 49 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -742,10 +742,44 @@ static const struct drm_encoder_helper_f
+ .enable = vc4_hdmi_encoder_enable,
+ };
+
++static u32 vc4_hdmi_get_hsm_clock(struct vc4_hdmi *vc4_hdmi)
++{
++ return clk_get_rate(vc4_hdmi->hsm_clock);
++}
++
++static u32 vc5_hdmi_get_hsm_clock(struct vc4_hdmi *vc4_hdmi)
++{
++ return 108000000;
++}
++
++static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
++{
++ int i;
++ u32 channel_map = 0;
++
++ for (i = 0; i < 8; i++) {
++ if (channel_mask & BIT(i))
++ channel_map |= i << (3 * i);
++ }
++ return channel_map;
++}
++
++static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
++{
++ int i;
++ u32 channel_map = 0;
++
++ for (i = 0; i < 8; i++) {
++ if (channel_mask & BIT(i))
++ channel_map |= i << (4 * i);
++ }
++ return channel_map;
++}
++
+ /* HDMI audio codec callbacks */
+ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi)
+ {
+- u32 hsm_clock = clk_get_rate(vc4_hdmi->hsm_clock);
++ u32 hsm_clock = vc4_hdmi->variant->get_hsm_clock(vc4_hdmi);
+ unsigned long n, m;
+
+ rational_best_approximation(hsm_clock, vc4_hdmi->audio.samplerate,
+@@ -864,7 +898,7 @@ static int vc4_hdmi_audio_hw_params(stru
+ struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai);
+ struct device *dev = &vc4_hdmi->pdev->dev;
+ u32 audio_packet_config, channel_mask;
+- u32 channel_map, i;
++ u32 channel_map;
+
+ if (substream != vc4_hdmi->audio.substream)
+ return -EINVAL;
+@@ -916,12 +950,7 @@ static int vc4_hdmi_audio_hw_params(stru
+ VC4_HDMI_MAI_CONFIG_BIT_REVERSE |
+ VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK));
+
+- channel_map = 0;
+- for (i = 0; i < 8; i++) {
+- if (channel_mask & BIT(i))
+- channel_map |= i << (3 * i);
+- }
+-
++ channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask);
+ HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map);
+ HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
+ vc4_hdmi_set_n_cts(vc4_hdmi);
+@@ -1715,6 +1744,8 @@ static const struct vc4_hdmi_variant bcm
+ .phy_disable = vc4_hdmi_phy_disable,
+ .phy_rng_enable = vc4_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc4_hdmi_phy_rng_disable,
++ .get_hsm_clock = vc4_hdmi_get_hsm_clock,
++ .channel_map = vc4_hdmi_channel_map,
+ };
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
+@@ -1736,6 +1767,8 @@ static const struct vc4_hdmi_variant bcm
+ .phy_init = vc5_hdmi_phy_init,
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
++ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
++ .channel_map = vc5_hdmi_channel_map,
+ };
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
+@@ -1757,6 +1790,8 @@ static const struct vc4_hdmi_variant bcm
+ .phy_init = vc5_hdmi_phy_init,
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
++ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
++ .channel_map = vc5_hdmi_channel_map,
+ };
+
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -88,6 +88,12 @@ struct vc4_hdmi_variant {
+
+ /* Callback to disable the RNG in the PHY */
+ void (*phy_rng_disable)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to get hsm clock */
++ u32 (*get_hsm_clock)(struct vc4_hdmi *vc4_hdmi);
++
++ /* Callback to get channel map */
++ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
+ };
+
+ /* HDMI audio information */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch b/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch
new file mode 100644
index 0000000000..78340ced70
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch
@@ -0,0 +1,31 @@
+From 446b19807781d73f214f959a8f4dab7662eed337 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:18:45 +0000
+Subject: [PATCH] drm/vc4: Enable audio on Pi4.
+
+This could be a revert of "drm/vc4: hdmi: Add an audio support flag"
+as it is no longer needed.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1750,6 +1750,7 @@ static const struct vc4_hdmi_variant bcm
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
+ .id = 0,
++ .audio_available = true,
+ .max_pixel_clock = 297000000,
+ .registers = vc5_hdmi_hdmi0_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
+@@ -1773,6 +1774,7 @@ static const struct vc4_hdmi_variant bcm
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
+ .id = 1,
++ .audio_available = true,
+ .max_pixel_clock = 297000000,
+ .registers = vc5_hdmi_hdmi1_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
diff --git a/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch b/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch
new file mode 100644
index 0000000000..f576b8624e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch
@@ -0,0 +1,46 @@
+From 37b204f22778f51cad7bdf678d7574ff6d7508a6 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 25 Mar 2020 18:22:40 +0000
+Subject: [PATCH] drm/vc4: Alter the HDMI state machine clock calc to
+ allow for 1920x1200
+
+Whilst the documentation for BCM2835 states that the HDMI state machine
+clock needs to be 108% of the pixel clock, other documentation says
+that it only has to be greater than the pixel clock. The firmware
+uses 101%, and that allows 1920x1200@60Hz to work within the
+constraint of the HSM clock being < 163.68MHz.
+
+Adopt 101%, and increase the maximum pixel clock for vc4 to 162MHz
+so that it too supports 1920x1200@60.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -581,10 +581,11 @@ static void vc4_hdmi_encoder_enable(stru
+ }
+
+ /*
+- * The HSM rate needs to be at 108% of the pixel clock, with a
+- * minimum of 108MHz.
++ * The HSM rate needs to be slightly greater than the pixel clock, with
++ * a minimum of 108MHz.
++ * Use 101% as this is what the firmware uses.
+ */
+- hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 108);
++ hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 101);
+ ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate);
+ if (ret) {
+ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+@@ -1730,7 +1731,7 @@ static int vc4_hdmi_dev_remove(struct pl
+ }
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
+- .max_pixel_clock = 148500000,
++ .max_pixel_clock = 162000000,
+ .audio_available = true,
+ .cec_available = true,
+ .registers = vc4_hdmi_fields,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch b/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch
new file mode 100644
index 0000000000..a05bf055ff
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch
@@ -0,0 +1,28 @@
+From 11d932df7f39124645cd017eb00853b4a70c28b4 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Mar 2020 11:51:55 +0000
+Subject: [PATCH] dtoverlays: Remove comment about vc4-kms-v3d locking
+ up X from README
+
+Using vc4-kms-v3d with X has worked for quite a while, and essentially
+required not using fbturbo and having an up to date MESA library.
+Remove the comment that says otherwise.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2672,9 +2672,7 @@ Params: <None>
+
+
+ Name: vc4-kms-v3d
+-Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver. Running startx or
+- booting to GUI while this overlay is in use will cause interesting
+- lockups.
++Info: Enable Eric Anholt's DRM VC4 HDMI/HVS/V3D driver.
+ Load: dtoverlay=vc4-kms-v3d,<param>
+ Params: cma-256 CMA is 256MB (needs 1GB)
+ cma-192 CMA is 192MB (needs 1GB)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch b/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch
new file mode 100644
index 0000000000..871d86a7a9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch
@@ -0,0 +1,97 @@
+From 289c29b96fcc6cbe5c966fb0cc9e1bb8efbdd9dc Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Mar 2020 15:32:19 +0000
+Subject: [PATCH] drm/vc4: Kick the core clock up during a mode change
+
+Experimental commit to kick the core clock up during mode
+switching. This makes mode switching far more reliable, and
+mimics what the firmware does.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 1 +
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
+ drivers/gpu/drm/vc4/vc4_hvs.c | 7 +++++++
+ drivers/gpu/drm/vc4/vc4_kms.c | 6 ++++++
+ 4 files changed, 16 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -306,6 +306,7 @@
+ };
+
+ hvs@7e400000 {
++ clocks = <&firmware_clocks 4>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -326,6 +326,8 @@ struct vc4_hvs {
+ void __iomem *regs;
+ u32 __iomem *dlist;
+
++ struct clk *core_clk;
++
+ /* Memory manager for CRTCs to allocate space in the display
+ * list. Units are dwords.
+ */
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -19,6 +19,7 @@
+ * each CRTC.
+ */
+
++#include <linux/clk.h>
+ #include <linux/component.h>
+ #include <linux/platform_device.h>
+
+@@ -239,6 +240,12 @@ static int vc4_hvs_bind(struct device *d
+ hvs->regset.regs = hvs_regs;
+ hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+
++ hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(hvs->core_clk)) {
++ dev_err(&pdev->dev, "Couldn't get core clock\n");
++ return PTR_ERR(hvs->regs);
++ }
++
+ hvs_version = readl(hvs->regs + SCALER_DISPLSTAT) >> 24;
+ if (hvs_version >= 0x40)
+ hvs->hvs5 = true;
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -13,6 +13,7 @@
+
+ #include <linux/bitfield.h>
+ #include <linux/bitops.h>
++#include <linux/clk.h>
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -222,6 +223,7 @@ vc4_atomic_complete_commit(struct drm_at
+ {
+ struct drm_device *dev = state->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hvs *hvs = vc4->hvs;
+ struct vc4_crtc *vc4_crtc;
+ int i;
+
+@@ -237,6 +239,8 @@ vc4_atomic_complete_commit(struct drm_at
+ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
+ }
+
++ clk_set_rate(hvs->core_clk, 500000000);
++
+ drm_atomic_helper_wait_for_fences(dev, state, false);
+
+ drm_atomic_helper_wait_for_dependencies(state);
+@@ -262,6 +266,8 @@ vc4_atomic_complete_commit(struct drm_at
+
+ drm_atomic_helper_commit_cleanup_done(state);
+
++ clk_set_rate(hvs->core_clk, 200000000);
++
+ drm_atomic_state_put(state);
+
+ up(&vc4->async_modeset);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch b/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch
new file mode 100644
index 0000000000..62080bffec
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch
@@ -0,0 +1,36 @@
+From 3c10a82ae5ce60ee8b4dbd1d1f8436efaa4593c3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 30 Mar 2020 12:52:26 +0100
+Subject: [PATCH] drm/vc4: Fixup for firmware KMS
+
+Fix up "drm/vc4: Kick the core clock up during a mode change" for
+firmware KMS mode where we don't have the HVS or core clock
+configured.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -239,7 +239,8 @@ vc4_atomic_complete_commit(struct drm_at
+ vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
+ }
+
+- clk_set_rate(hvs->core_clk, 500000000);
++ if (!vc4->firmware_kms)
++ clk_set_rate(hvs->core_clk, 500000000);
+
+ drm_atomic_helper_wait_for_fences(dev, state, false);
+
+@@ -266,7 +267,8 @@ vc4_atomic_complete_commit(struct drm_at
+
+ drm_atomic_helper_commit_cleanup_done(state);
+
+- clk_set_rate(hvs->core_clk, 200000000);
++ if (!vc4->firmware_kms)
++ clk_set_rate(hvs->core_clk, 200000000);
+
+ drm_atomic_state_put(state);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch
new file mode 100644
index 0000000000..8edba988f2
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch
@@ -0,0 +1,31 @@
+From d39fee7763d49f3b0bfd57e6cdc014467415d56a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 30 Mar 2020 18:25:10 +0100
+Subject: [PATCH] drm/vc4: Fixup plane init within firmware-kms
+
+"drm/vc4: plane: Move additional planes creation to driver" moved
+overlay and cursor plane creation to a global function thata was
+unconditionally run, when it is not wanted in firmware KMS mode.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -291,9 +291,11 @@ static int vc4_drm_bind(struct device *d
+ if (ret)
+ goto gem_destroy;
+
+- ret = vc4_plane_create_additional_planes(drm);
+- if (ret)
+- goto unbind_all;
++ if (!vc4->firmware_kms) {
++ ret = vc4_plane_create_additional_planes(drm);
++ if (ret)
++ goto unbind_all;
++ }
+
+ drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch b/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch
new file mode 100644
index 0000000000..a7b5b5fad3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch
@@ -0,0 +1,26 @@
+From 992513ac2ec9245cb8f0fdd9c0e2ce4add07beb2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Mar 2020 16:21:45 +0100
+Subject: [PATCH] drm/vc4-hdmi: Give the HDMI audio instances different
+ names
+
+The debugfs usage within asoc gets confused if multiple interfaces
+have the same card name, therefore use unique names when
+initialising them.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1198,7 +1198,7 @@ static int vc4_hdmi_audio_init(struct vc
+
+ card->dai_link = dai_link;
+ card->num_links = 1;
+- card->name = "vc4-hdmi";
++ card->name = vc4_hdmi->variant->id ? "vc4-hdmi1" : "vc4-hdmi";
+ card->dev = dev;
+
+ /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch b/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch
new file mode 100644
index 0000000000..89285ad25c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch
@@ -0,0 +1,49 @@
+From 8aa48c2a3fa470d348104e8f8aa558a661b724e5 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Mar 2020 16:23:11 +0100
+Subject: [PATCH] i2c: brcmstb: The interrupt line is optional, so use
+ platform_get_irq_optional
+
+If there is no interrupt defined then an error is logged due
+to the use of platform_get_irq. The driver handles not having
+the interrupt by falling back to polling, therefore make
+the appropriate call when claiming it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/i2c/busses/i2c-brcmstb.c | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-brcmstb.c
++++ b/drivers/i2c/busses/i2c-brcmstb.c
+@@ -647,20 +647,22 @@ static int brcmstb_i2c_probe(struct plat
+ int_name = NULL;
+
+ /* Get the interrupt number */
+- dev->irq = platform_get_irq(pdev, 0);
++ dev->irq = platform_get_irq_optional(pdev, 0);
+
+ /* disable the bsc interrupt line */
+ brcmstb_i2c_enable_disable_irq(dev, INT_DISABLE);
+
+ /* register the ISR handler */
+- rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
+- IRQF_SHARED,
+- int_name ? int_name : pdev->name,
+- dev);
++ if (dev->irq >= 0) {
++ rc = devm_request_irq(&pdev->dev, dev->irq, brcmstb_i2c_isr,
++ IRQF_SHARED,
++ int_name ? int_name : pdev->name,
++ dev);
+
+- if (rc) {
+- dev_dbg(dev->device, "falling back to polling mode");
+- dev->irq = -1;
++ if (rc) {
++ dev_dbg(dev->device, "falling back to polling mode");
++ dev->irq = -1;
++ }
+ }
+
+ if (of_property_read_u32(dev->device->of_node,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch b/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch
new file mode 100644
index 0000000000..cd18cd1309
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch
@@ -0,0 +1,35 @@
+From 460ddd2729f0dbb2723e48f3a22ffccbb78b42c7 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Mar 2020 17:54:08 +0100
+Subject: [PATCH] dt: Drop I2C for Pi4 HDMI interfaces to 97.5kHz.
+
+It was set to 390kHz, which is outside of the required spec for
+reading HDMI (max 100kHz). The i2c-brcmstb driver only supports
+a number of fixed bus speeds, of which 97.5kHz is the closest to
+100kHz without exceeding it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -358,7 +358,7 @@
+ compatible = "brcm,bcm2711-hdmi-i2c";
+ reg = <0x7ef04500 0x100>, <0x7ef00b00 0x300>;
+ reg-names = "bsc", "auto-i2c";
+- clock-frequency = <390000>;
++ clock-frequency = <97500>;
+ status = "disabled";
+ };
+
+@@ -395,7 +395,7 @@
+ compatible = "brcm,bcm2711-hdmi-i2c";
+ reg = <0x7ef09500 0x100>, <0x7ef05b00 0x300>;
+ reg-names = "bsc", "auto-i2c";
+- clock-frequency = <390000>;
++ clock-frequency = <97500>;
+ status = "disabled";
+ };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch b/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch
new file mode 100644
index 0000000000..716678f078
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch
@@ -0,0 +1,39 @@
+From bb766b0401a49f4a824dd116b9befe8542fe3cd6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 27 Mar 2020 13:49:25 +0000
+Subject: [PATCH] overlays: Add missing rpi-poe parameters
+
+The rpi-poe fan overlay has gained two more fan speeds and adjusted
+the thresholds and hystereses.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2037,12 +2037,20 @@ Name: rpi-poe
+ Info: Raspberry Pi PoE HAT fan
+ Load: dtoverlay=rpi-poe,<param>[=<val>]
+ Params: poe_fan_temp0 Temperature (in millicelcius) at which the fan
+- turns on (default 50000)
++ turns on (default 40000)
+ poe_fan_temp0_hyst Temperature delta (in millicelcius) at which
+- the fan turns off (default 5000)
++ the fan turns off (default 2000)
+ poe_fan_temp1 Temperature (in millicelcius) at which the fan
+- speeds up (default 55000)
++ speeds up (default 45000)
+ poe_fan_temp1_hyst Temperature delta (in millicelcius) at which
++ the fan slows down (default 2000)
++ poe_fan_temp2 Temperature (in millicelcius) at which the fan
++ speeds up (default 50000)
++ poe_fan_temp2_hyst Temperature delta (in millicelcius) at which
++ the fan slows down (default 2000)
++ poe_fan_temp3 Temperature (in millicelcius) at which the fan
++ speeds up (default 55000)
++ poe_fan_temp3_hyst Temperature delta (in millicelcius) at which
+ the fan slows down (default 5000)
+
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch b/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch
new file mode 100644
index 0000000000..83d8d25e29
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch
@@ -0,0 +1,30 @@
+From 9da2d153eb010d3e92083c322e87de9ae066d93e Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Thu, 2 Apr 2020 16:46:31 +0100
+Subject: [PATCH] vc4_hdmi_phy: Fix offset calculation
+
+The original firmware code worked with float and did
+ offset = ((vco_freq / fref * 2) * (1 << 22));
+ offset >>= 2;
+
+In this code it's all integer so doing the integer divide before the shift loses lots of precision
+
+This fixes the issue of 1080p59.94 mode having 59.64 fps
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -192,8 +192,8 @@ static u32 phy_get_rm_offset(unsigned lo
+
+ /* RM offset is stored as 9.22 format */
+ offset = vco_freq * 2;
+- do_div(offset, fref);
+ offset = offset << 22;
++ do_div(offset, fref);
+ offset >>= 2;
+
+ return offset;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch
new file mode 100644
index 0000000000..20f548328c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch
@@ -0,0 +1,101 @@
+From b9b7a84463d95fd912406e377a58af8b1fb81d21 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 1 Apr 2020 15:09:42 +0100
+Subject: [PATCH] overlays: Add overlay_map
+
+The overlay map permits platform-specific overlays, with deprecation
+and renaming.
+
+See: https://github.com/raspberrypi/linux/issues/3520
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 2 +
+ arch/arm/boot/dts/overlays/overlay_map.dts | 71 ++++++++++++++++++++++
+ 2 files changed, 73 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/overlay_map.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -1,5 +1,7 @@
+ # Overlays for the Raspberry Pi platform
+
++dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
++
+ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ act-led.dtbo \
+ adau1977-adc.dtbo \
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -0,0 +1,71 @@
++/dts-v1/;
++
++/ {
++ i2c3 {
++ bcm2711;
++ };
++
++ i2c4 {
++ bcm2711;
++ };
++
++ i2c5 {
++ bcm2711;
++ };
++
++ i2c6 {
++ bcm2711;
++ };
++
++ rpivid-v4l2 {
++ bcm2711;
++ };
++
++ spi3-1cs {
++ bcm2711;
++ };
++
++ spi3-2cs {
++ bcm2711;
++ };
++
++ spi4-1cs {
++ bcm2711;
++ };
++
++ spi4-2cs {
++ bcm2711;
++ };
++
++ spi5-1cs {
++ bcm2711;
++ };
++
++ spi5-2cs {
++ bcm2711;
++ };
++
++ spi6-1cs {
++ bcm2711;
++ };
++
++ spi6-2cs {
++ bcm2711;
++ };
++
++ uart2 {
++ bcm2711;
++ };
++
++ uart3 {
++ bcm2711;
++ };
++
++ uart4 {
++ bcm2711;
++ };
++
++ uart5 {
++ bcm2711;
++ };
++};
diff --git a/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch b/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch
new file mode 100644
index 0000000000..edcb3792af
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch
@@ -0,0 +1,226 @@
+From c9d7d2eb73f2c6024e3f94765fc830bce0203f2b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 1 Apr 2020 17:24:15 +0100
+Subject: [PATCH] overlays: Formally rename/deprecate old overlays
+
+Take advantage of the overlay_map to rename or deprecate some obsolete
+overlays.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 7 ----
+ arch/arm/boot/dts/overlays/README | 12 +-----
+ .../overlays/bmp085_i2c-sensor-overlay.dts | 23 -----------
+ .../dts/overlays/i2c0-bcm2708-overlay.dts | 14 -------
+ .../dts/overlays/i2c1-bcm2708-overlay.dts | 9 -----
+ arch/arm/boot/dts/overlays/overlay_map.dts | 40 +++++++++++++++++++
+ .../boot/dts/overlays/pi3-act-led-overlay.dts | 1 -
+ .../dts/overlays/pi3-disable-bt-overlay.dts | 1 -
+ .../dts/overlays/pi3-disable-wifi-overlay.dts | 1 -
+ .../dts/overlays/pi3-miniuart-bt-overlay.dts | 1 -
+ 10 files changed, 42 insertions(+), 67 deletions(-)
+ delete mode 100644 arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/pi3-disable-wifi-overlay.dts
+ delete mode 100644 arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -27,7 +27,6 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ audiosense-pi.dtbo \
+ audremap.dtbo \
+ balena-fin.dtbo \
+- bmp085_i2c-sensor.dtbo \
+ dht11.dtbo \
+ dionaudio-loco.dtbo \
+ dionaudio-loco-v2.dtbo \
+@@ -75,9 +74,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ i2c-rtc-gpio.dtbo \
+ i2c-sensor.dtbo \
+ i2c0.dtbo \
+- i2c0-bcm2708.dtbo \
+ i2c1.dtbo \
+- i2c1-bcm2708.dtbo \
+ i2c3.dtbo \
+ i2c4.dtbo \
+ i2c5.dtbo \
+@@ -114,10 +111,6 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ mz61581.dtbo \
+ ov5647.dtbo \
+ papirus.dtbo \
+- pi3-act-led.dtbo \
+- pi3-disable-bt.dtbo \
+- pi3-disable-wifi.dtbo \
+- pi3-miniuart-bt.dtbo \
+ pibell.dtbo \
+ piglow.dtbo \
+ piscreen.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1288,11 +1288,8 @@ Params: pins_0_1 Use pins
+
+
+ Name: i2c0-bcm2708
+-Info: Deprecated, legacy version of i2c0, from which it inherits its
+- parameters, just adding the explicit individual pin specifiers.
++Info: Deprecated, legacy version of i2c0.
+ Load: <Deprecated>
+-Params: sda0_pin GPIO pin for SDA0 (deprecated - use pins_*)
+- scl0_pin GPIO pin for SCL0 (deprecated - use pins_*)
+
+
+ Name: i2c1
+@@ -1307,13 +1304,8 @@ Params: pins_2_3 Use pins
+
+
+ Name: i2c1-bcm2708
+-Info: Deprecated, legacy version of i2c1, from which it inherits its
+- parameters, just adding the explicit individual pin specifiers.
++Info: Deprecated, legacy version of i2c1.
+ Load: <Deprecated>
+-Params: sda1_pin GPIO pin for SDA1 (2 or 44 - default 2)
+- scl1_pin GPIO pin for SCL1 (3 or 45 - default 3)
+- pin_func Alternative pin function (4 (alt0), 6 (alt2) -
+- default 4)
+
+
+ Name: i2c3
+--- a/arch/arm/boot/dts/overlays/bmp085_i2c-sensor-overlay.dts
++++ /dev/null
+@@ -1,23 +0,0 @@
+-// Definitions for BMP085/BMP180 digital barometric pressure and temperature sensors from Bosch Sensortec
+-/dts-v1/;
+-/plugin/;
+-
+-/ {
+- compatible = "brcm,bcm2835";
+-
+- fragment@0 {
+- target = <&i2c_arm>;
+- __overlay__ {
+- #address-cells = <1>;
+- #size-cells = <0>;
+- status = "okay";
+-
+- bmp085@77 {
+- compatible = "bosch,bmp085";
+- reg = <0x77>;
+- default-oversampling = <3>;
+- status = "okay";
+- };
+- };
+- };
+-};
+--- a/arch/arm/boot/dts/overlays/i2c0-bcm2708-overlay.dts
++++ /dev/null
+@@ -1,14 +0,0 @@
+-#include "i2c0-overlay.dts"
+-
+-/{
+- __overrides__ {
+- sda0_pin = <&pins1>,"brcm,pins:0",
+- <&pins2>,"brcm,pins:0",
+- <&pins3>,"brcm,pins:0",
+- <&pins4>,"brcm,pins:0";
+- scl0_pin = <&pins1>,"brcm,pins:4",
+- <&pins2>,"brcm,pins:4",
+- <&pins3>,"brcm,pins:4",
+- <&pins4>,"brcm,pins:4";
+- };
+-};
+--- a/arch/arm/boot/dts/overlays/i2c1-bcm2708-overlay.dts
++++ /dev/null
+@@ -1,9 +0,0 @@
+-#include "i2c1-overlay.dts"
+-
+-/{
+- __overrides__ {
+- sda1_pin = <&pins1>,"brcm,pins:0", <&pins2>,"brcm,pins:0";
+- scl1_pin = <&pins1>,"brcm,pins:4", <&pins1>,"brcm,pins:4";
+- pin_func = <&pins1>,"brcm,function:0", <&pins2>,"brcm,function:0";
+- };
+-};
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -1,6 +1,18 @@
+ /dts-v1/;
+
+ / {
++ bmp085_i2c-sensor {
++ deprecated = "use i2c-sensor,bmp085";
++ };
++
++ i2c0-bcm2708 {
++ deprecated = "use i2c0";
++ };
++
++ i2c1-bcm2708 {
++ deprecated = "use i2c1";
++ };
++
+ i2c3 {
+ bcm2711;
+ };
+@@ -17,10 +29,34 @@
+ bcm2711;
+ };
+
++ lirc-rpi {
++ deprecated = "use gpio-ir";
++ };
++
++ pi3-act-led {
++ renamed = "act-led";
++ };
++
++ pi3-disable-bt {
++ renamed = "disable-bt";
++ };
++
++ pi3-disable-wifi {
++ renamed = "disable-wifi";
++ };
++
++ pi3-miniuart-bt {
++ renamed = "miniuart-bt";
++ };
++
+ rpivid-v4l2 {
+ bcm2711;
+ };
+
++ sdio-1bit {
++ deprecated = "use sdio,bus_width=1,gpios_22_25";
++ };
++
+ spi3-1cs {
+ bcm2711;
+ };
+@@ -68,4 +104,8 @@
+ uart5 {
+ bcm2711;
+ };
++
++ upstream-aux-interrupt {
++ deprecated = "no longer necessary";
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/pi3-act-led-overlay.dts
++++ /dev/null
+@@ -1 +0,0 @@
+-#include "act-led-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/pi3-disable-bt-overlay.dts
++++ /dev/null
+@@ -1 +0,0 @@
+-#include "disable-bt-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/pi3-disable-wifi-overlay.dts
++++ /dev/null
+@@ -1 +0,0 @@
+-#include "disable-wifi-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/pi3-miniuart-bt-overlay.dts
++++ /dev/null
+@@ -1 +0,0 @@
+-#include "miniuart-bt-overlay.dts"
diff --git a/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch
new file mode 100644
index 0000000000..4fd5879ec8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch
@@ -0,0 +1,26 @@
+From ed5f6f5d1077e849c0762595069e79a7749951bf Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 1 Apr 2020 15:51:56 +0100
+Subject: [PATCH] overlays: Add vc4-kms-v3d-pi4 to overlay_map
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/overlay_map.dts | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -108,4 +108,13 @@
+ upstream-aux-interrupt {
+ deprecated = "no longer necessary";
+ };
++
++ vc4-kms-v3d {
++ bcm2835;
++ bcm2711 = "vc4-kms-v3d-pi4";
++ };
++
++ vc4-kms-v3d-pi4 {
++ bcm2711;
++ };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch b/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch
new file mode 100644
index 0000000000..e76367b9da
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch
@@ -0,0 +1,229 @@
+From 0a65f76d99bce7685e57ae506eedc499c551ac83 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 6 Apr 2020 09:47:42 +0100
+Subject: [PATCH] Add upstream and upstream-pi4 to overlay_map
+
+Because the upstream overlay applies vc4-kms-v3d, of which Pi 4 has its
+own version, there also needs to be a Pi 4 version - vc4-kms-v3d-pi4.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 7 +
+ arch/arm/boot/dts/overlays/overlay_map.dts | 9 +
+ .../dts/overlays/upstream-pi4-overlay.dts | 161 ++++++++++++++++++
+ 4 files changed, 178 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -183,6 +183,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ uart5.dtbo \
+ udrc.dtbo \
+ upstream.dtbo \
++ upstream-pi4.dtbo \
+ vc4-fkms-v3d.dtbo \
+ vc4-kms-kippah-7inch.dtbo \
+ vc4-kms-v3d.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2653,6 +2653,13 @@ Info: This overlay has been deprecated
+ Load: <Deprecated>
+
+
++Name: upstream-pi4
++Info: Allow usage of downstream .dtb with upstream kernel on Pi 4. Comprises
++ the vc4-kms-v3d-pi4 and dwc2 overlays.
++Load: dtoverlay=upstream-pi4
++Params: <None>
++
++
+ Name: vc4-fkms-v3d
+ Info: Enable Eric Anholt's DRM VC4 V3D driver on top of the dispmanx
+ display stack.
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -105,10 +105,19 @@
+ bcm2711;
+ };
+
++ upstream {
++ bcm2835;
++ bcm2711 = "upstream-pi4";
++ };
++
+ upstream-aux-interrupt {
+ deprecated = "no longer necessary";
+ };
+
++ upstream-pi4 {
++ bcm2711;
++ };
++
+ vc4-kms-v3d {
+ bcm2835;
+ bcm2711 = "vc4-kms-v3d-pi4";
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts
+@@ -0,0 +1,161 @@
++// redo: ovmerge -c vc4-kms-v3d-pi4-overlay.dts,cma-96 dwc2-overlay.dts,dr_mode=otg
++
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/bcm2835.h>
++
++/ {
++ compatible = "brcm,bcm2835";
++ fragment@0 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=256M";
++ };
++ };
++ fragment@1 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=192M";
++ };
++ };
++ fragment@2 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=128M";
++ };
++ };
++ fragment@3 {
++ target-path = "/chosen";
++ __overlay__ {
++ bootargs = "cma=96M";
++ };
++ };
++ fragment@4 {
++ target-path = "/chosen";
++ __dormant__ {
++ bootargs = "cma=64M";
++ };
++ };
++ fragment@5 {
++ target = <&ddc0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@6 {
++ target = <&ddc1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@7 {
++ target = <&hdmi0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@8 {
++ target = <&hdmi1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@9 {
++ target = <&hvs>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@10 {
++ target = <&pixelvalve0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@11 {
++ target = <&pixelvalve1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@12 {
++ target = <&pixelvalve2>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@13 {
++ target = <&pixelvalve3>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@14 {
++ target = <&pixelvalve4>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@15 {
++ target = <&v3d>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@16 {
++ target = <&vc4>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@17 {
++ target = <&txp>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@18 {
++ target = <&fb>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++ fragment@19 {
++ target = <&firmwarekms>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++ fragment@20 {
++ target = <&vec>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++ fragment@21 {
++ target = <&hdmi0>;
++ __dormant__ {
++ dmas;
++ };
++ };
++ fragment@22 {
++ target = <&hdmi1>;
++ __dormant__ {
++ dmas;
++ };
++ };
++ fragment@23 {
++ target = <&usb>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ __overlay__ {
++ compatible = "brcm,bcm2835-usb";
++ dr_mode = "otg";
++ g-np-tx-fifo-size = <32>;
++ g-rx-fifo-size = <558>;
++ g-tx-fifo-size = <512 512 512 512 512 256 256>;
++ status = "okay";
++ };
++ };
++};
diff --git a/target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch b/target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch
index 31978c761a..c0422f4ed3 100644
--- a/target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch
+++ b/target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch
@@ -1,4 +1,4 @@
-From 3e2eb77ba8d0c6913138382512309e7892907a1c Mon Sep 17 00:00:00 2001
+From a2e39f36678626f5d7883c5a1dc8c476134c5e0b Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Mon, 9 Sep 2019 15:49:56 +0100
Subject: [PATCH] clk-raspberrypi: Allow cpufreq driver to also adjust
@@ -14,9 +14,9 @@ Signed-off-by: popcornmix <popcornmix@gmail.com>
--- a/drivers/clk/bcm/clk-raspberrypi.c
+++ b/drivers/clk/bcm/clk-raspberrypi.c
-@@ -70,7 +70,7 @@ static int raspberrypi_clock_property(st
+@@ -76,7 +76,7 @@ static int raspberrypi_clock_property(st
struct raspberrypi_firmware_prop msg = {
- .id = cpu_to_le32(clk),
+ .id = cpu_to_le32(data->id),
.val = cpu_to_le32(*val),
- .disable_turbo = cpu_to_le32(1),
+ .disable_turbo = cpu_to_le32(0),
diff --git a/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch b/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch
new file mode 100644
index 0000000000..e35806962e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch
@@ -0,0 +1,323 @@
+From 5b37b08ff1c29e7386eb8a29b168e94e33cf82c3 Mon Sep 17 00:00:00 2001
+From: Matt Flax <flatmax@flatmax.org>
+Date: Wed, 8 Apr 2020 20:00:30 +1000
+Subject: [PATCH] Add support for the AudioInjector.net Isolated sound
+ card
+
+This patch adds support for the Audio Injector Isolated sound card.
+
+Signed-off-by: Matt Flax <flatmax@flatmax.org>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 +
+ ...dioinjector-isolated-soundcard-overlay.dts | 55 ++++++
+ sound/soc/bcm/Kconfig | 7 +
+ sound/soc/bcm/Makefile | 2 +
+ .../bcm/audioinjector-isolated-soundcard.c | 183 ++++++++++++++++++
+ 11 files changed, 259 insertions(+), 5 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
+ create mode 100644 sound/soc/bcm/audioinjector-isolated-soundcard.c
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -22,6 +22,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ applepi-dac.dtbo \
+ at86rf233.dtbo \
+ audioinjector-addons.dtbo \
++ audioinjector-isolated-soundcard.dtbo \
+ audioinjector-ultra.dtbo \
+ audioinjector-wm8731-audio.dtbo \
+ audiosense-pi.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -505,6 +505,12 @@ Params: non-stop-clocks Keeps th
+ is paused or stopped (default off)
+
+
++Name: audioinjector-isolated-soundcard
++Info: Configures the audioinjector.net isolated soundcard
++Load: dtoverlay=audioinjector-isolated-soundcard
++Params: <None>
++
++
+ Name: audioinjector-ultra
+ Info: Configures the audioinjector.net ultra soundcard
+ Load: dtoverlay=audioinjector-ultra
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
+@@ -0,0 +1,55 @@
++// Definitions for audioinjector.net audio isolated soundcard
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2s>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target-path = "/";
++ __overlay__ {
++ cs4272_mclk: codec-mclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24576000>;
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&i2c1>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ cs4272: cs4271@10 {
++ #sound-dai-cells = <0>;
++ compatible = "cirrus,cs4271";
++ reg = <0x10>;
++ reset-gpio = <&gpio 5 0>;
++ clocks = <&cs4272_mclk>;
++ clock-names = "mclk";
++ status = "okay";
++ };
++ };
++ };
++
++ fragment@3 {
++ target = <&sound>;
++ snd: __overlay__ {
++ compatible = "ai,audioinjector-isolated-soundcard";
++ mute-gpios = <&gpio 17 0>;
++ i2s-controller = <&i2s>;
++ codec = <&cs4272>;
++ status = "okay";
++ };
++ };
++};
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -192,6 +192,13 @@ config SND_AUDIOINJECTOR_OCTO_SOUNDCARD
+ help
+ Say Y or M if you want to add support for audioinjector.net octo add on
+
++config SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD
++ tristate "Support for audioinjector.net isolated DAC and ADC soundcard"
++ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
++ select SND_SOC_CS4271_I2C
++ help
++ Say Y or M if you want to add support for audioinjector.net isolated soundcard
++
+ config SND_AUDIOSENSE_PI
+ tristate "Support for AudioSense Add-On Soundcard"
+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+--- a/sound/soc/bcm/Makefile
++++ b/sound/soc/bcm/Makefile
+@@ -27,6 +27,7 @@ snd-soc-iqaudio-dac-objs := iqaudio-dac.
+ snd-soc-i-sabre-q2m-objs := i-sabre-q2m.o
+ snd-soc-audioinjector-pi-soundcard-objs := audioinjector-pi-soundcard.o
+ snd-soc-audioinjector-octo-soundcard-objs := audioinjector-octo-soundcard.o
++snd-soc-audioinjector-isolated-soundcard-objs := audioinjector-isolated-soundcard.o
+ snd-soc-audiosense-pi-objs := audiosense-pi.o
+ snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o
+ snd-soc-dionaudio-loco-objs := dionaudio_loco.o
+@@ -55,6 +56,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC
+ obj-$(CONFIG_SND_BCM2708_SOC_I_SABRE_Q2M) += snd-soc-i-sabre-q2m.o
+ obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD) += snd-soc-audioinjector-pi-soundcard.o
+ obj-$(CONFIG_SND_AUDIOINJECTOR_OCTO_SOUNDCARD) += snd-soc-audioinjector-octo-soundcard.o
++obj-$(CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD) += snd-soc-audioinjector-isolated-soundcard.o
+ obj-$(CONFIG_SND_AUDIOSENSE_PI) += snd-soc-audiosense-pi.o
+ obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o
+ obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o
+--- /dev/null
++++ b/sound/soc/bcm/audioinjector-isolated-soundcard.c
+@@ -0,0 +1,183 @@
++/*
++ * ASoC Driver for AudioInjector.net isolated soundcard
++ *
++ * Created on: 20-February-2020
++ * Author: flatmax@flatmax.org
++ * based on audioinjector-octo-soundcard.c
++ *
++ * Copyright (C) 2020 Flatmax Pty. Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/gpio/consumer.h>
++
++#include <sound/core.h>
++#include <sound/soc.h>
++#include <sound/pcm_params.h>
++#include <sound/control.h>
++
++static struct gpio_desc *mute_gpio;
++
++static const unsigned int audioinjector_isolated_rates[] = {
++ 192000, 96000, 48000, 32000, 24000, 16000, 8000
++};
++
++static struct snd_pcm_hw_constraint_list audioinjector_isolated_constraints = {
++ .list = audioinjector_isolated_rates,
++ .count = ARRAY_SIZE(audioinjector_isolated_rates),
++};
++
++static int audioinjector_isolated_dai_init(struct snd_soc_pcm_runtime *rtd)
++{
++ int ret=snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 24576000, 0);
++ if (ret)
++ return ret;
++
++ return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 64);
++}
++
++static int audioinjector_isolated_startup(struct snd_pcm_substream *substream)
++{
++ snd_pcm_hw_constraint_list(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_RATE, &audioinjector_isolated_constraints);
++
++ return 0;
++}
++
++static int audioinjector_isolated_trigger(struct snd_pcm_substream *substream,
++ int cmd){
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ gpiod_set_value(mute_gpio, 0);
++ break;
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ gpiod_set_value(mute_gpio, 1);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct snd_soc_ops audioinjector_isolated_ops = {
++ .startup = audioinjector_isolated_startup,
++ .trigger = audioinjector_isolated_trigger,
++};
++
++SND_SOC_DAILINK_DEFS(audioinjector_isolated,
++ DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
++ DAILINK_COMP_ARRAY(COMP_CODEC("cs4271.1-0010", "cs4271-hifi")),
++ DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
++
++static struct snd_soc_dai_link audioinjector_isolated_dai[] = {
++ {
++ .name = "AudioInjector ISO",
++ .stream_name = "AI-HIFI",
++ .ops = &audioinjector_isolated_ops,
++ .init = audioinjector_isolated_dai_init,
++ .symmetric_rates = 1,
++ .symmetric_channels = 1,
++ .dai_fmt = SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF,
++ SND_SOC_DAILINK_REG(audioinjector_isolated),
++ }
++};
++
++static const struct snd_soc_dapm_widget audioinjector_isolated_widgets[] = {
++ SND_SOC_DAPM_OUTPUT("OUTPUTS"),
++ SND_SOC_DAPM_INPUT("INPUTS"),
++};
++
++static const struct snd_soc_dapm_route audioinjector_isolated_route[] = {
++ /* Balanced outputs */
++ {"OUTPUTS", NULL, "AOUTA+"},
++ {"OUTPUTS", NULL, "AOUTA-"},
++ {"OUTPUTS", NULL, "AOUTB+"},
++ {"OUTPUTS", NULL, "AOUTB-"},
++
++ /* Balanced inputs */
++ {"AINA", NULL, "INPUTS"},
++ {"AINB", NULL, "INPUTS"},
++};
++
++static struct snd_soc_card snd_soc_audioinjector_isolated = {
++ .name = "audioinjector-isolated-soundcard",
++ .dai_link = audioinjector_isolated_dai,
++ .num_links = ARRAY_SIZE(audioinjector_isolated_dai),
++
++ .dapm_widgets = audioinjector_isolated_widgets,
++ .num_dapm_widgets = ARRAY_SIZE(audioinjector_isolated_widgets),
++ .dapm_routes = audioinjector_isolated_route,
++ .num_dapm_routes = ARRAY_SIZE(audioinjector_isolated_route),
++};
++
++static int audioinjector_isolated_probe(struct platform_device *pdev)
++{
++ struct snd_soc_card *card = &snd_soc_audioinjector_isolated;
++ int ret;
++
++ card->dev = &pdev->dev;
++
++ if (pdev->dev.of_node) {
++ struct snd_soc_dai_link *dai = &audioinjector_isolated_dai[0];
++ struct device_node *i2s_node =
++ of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0);
++
++ if (i2s_node) {
++ dai->cpus->dai_name = NULL;
++ dai->cpus->of_node = i2s_node;
++ dai->platforms->name = NULL;
++ dai->platforms->of_node = i2s_node;
++ } else {
++ dev_err(&pdev->dev,
++ "i2s-controller missing or invalid in DT\n");
++ return -EINVAL;
++ }
++
++ mute_gpio = devm_gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_LOW);
++ if (IS_ERR(mute_gpio)){
++ dev_err(&pdev->dev, "mute gpio not found in dt overlay\n");
++ return PTR_ERR(mute_gpio);
++ }
++ }
++
++ ret = devm_snd_soc_register_card(&pdev->dev, card);
++ if (ret && ret != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
++ return ret;
++}
++
++static const struct of_device_id audioinjector_isolated_of_match[] = {
++ { .compatible = "ai,audioinjector-isolated-soundcard", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, audioinjector_isolated_of_match);
++
++static struct platform_driver audioinjector_isolated_driver = {
++ .driver = {
++ .name = "audioinjector-isolated",
++ .owner = THIS_MODULE,
++ .of_match_table = audioinjector_isolated_of_match,
++ },
++ .probe = audioinjector_isolated_probe,
++};
++
++module_platform_driver(audioinjector_isolated_driver);
++MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
++MODULE_DESCRIPTION("AudioInjector.net isolated Soundcard");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:audioinjector-isolated-soundcard");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch b/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch
new file mode 100644
index 0000000000..2da57b81de
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch
@@ -0,0 +1,24 @@
+From 1231481bdb45114abe7b0348c78a943642fde717 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Apr 2020 11:59:39 +0100
+Subject: [PATCH] overlays: Fix dtc warnings in i2c-gpio
+
+Better late than never.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
+@@ -9,6 +9,9 @@
+ target-path = "/";
+
+ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
+ i2c_gpio: i2c@0 {
+ reg = <0xffffffff>;
+ compatible = "i2c-gpio";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch b/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch
new file mode 100644
index 0000000000..66091ca591
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch
@@ -0,0 +1,28 @@
+From 31b68a380e7649f0cbc7209c465bf747c072a7ce Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Apr 2020 15:23:56 +0100
+Subject: [PATCH] kbuild: Disable gcc plugins
+
+The GCC plugin feature leads to different kernel configurations on what
+ought to be equivalent build systems because they depend on the build
+hosts native compilers rather than the cross compilers needed for the
+target. This causes problems with module symbol version mismatches.
+
+Disable GCC plugins for all build hosts.
+
+Advanced build script hackery borrowed from a patch by milhouse.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ scripts/gcc-plugin.sh | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/scripts/gcc-plugin.sh
++++ b/scripts/gcc-plugin.sh
+@@ -1,5 +1,6 @@
+ #!/bin/sh
+ # SPDX-License-Identifier: GPL-2.0
++exit 0 # Disable plugins
+ srctree=$(dirname "$0")
+
+ SHOW_ERROR=
diff --git a/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch b/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch
new file mode 100644
index 0000000000..a5aee43aef
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch
@@ -0,0 +1,42 @@
+From 9554903fc8c15828d8f6cc9bd8c5444433c56cae Mon Sep 17 00:00:00 2001
+From: AMuszkat <ariel.muszkat@gmail.com>
+Date: Wed, 8 Apr 2020 10:04:49 +0200
+Subject: [PATCH] ASoC: ma120x0p: Add 96KHz rate support
+
+Add 96KHz rate support to MA120X0P codec and make enable and mute gpio
+pins optional.
+
+Signed-off-by: AMuszkat <ariel.muszkat@gmail.com>
+---
+ sound/soc/codecs/ma120x0p.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/codecs/ma120x0p.c
++++ b/sound/soc/codecs/ma120x0p.c
+@@ -1002,7 +1002,7 @@ static struct snd_soc_dai_driver ma120x0
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 44100,
+- .rate_max = 48000,
++ .rate_max = 96000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
+ },
+ .ops = &ma120x0p_dai_ops,
+@@ -1235,7 +1235,7 @@ static int ma120x0p_i2c_probe(struct i2c
+ //Startup sequence
+
+ //Make sure the device is muted
+- priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp",
++ priv_data->mute_gpio = devm_gpiod_get_optional(&i2c->dev, "mute_gp",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv_data->mute_gpio)) {
+ ret = PTR_ERR(priv_data->mute_gpio);
+@@ -1262,7 +1262,7 @@ static int ma120x0p_i2c_probe(struct i2c
+ msleep(200);
+
+ //Enable ma120x0pp
+- priv_data->enable_gpio = devm_gpiod_get(&i2c->dev,
++ priv_data->enable_gpio = devm_gpiod_get_optional(&i2c->dev,
+ "enable_gp", GPIOD_OUT_LOW);
+ if (IS_ERR(priv_data->enable_gpio)) {
+ ret = PTR_ERR(priv_data->enable_gpio);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch
new file mode 100644
index 0000000000..137a2fa4a0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch
@@ -0,0 +1,44 @@
+From d4cf092a0e923361f521e1bc7d1fbfb1907958b3 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 7 Nov 2019 10:56:11 +0100
+Subject: [PATCH] arm64: mm: reserve CMA and crashkernel in ZONE_DMA32
+
+commit bff3b04460a80f425442fe8e5c6ee8c3ebef611f upstream.
+
+With the introduction of ZONE_DMA in arm64 we moved the default CMA and
+crashkernel reservation into that area. This caused a regression on big
+machines that need big CMA and crashkernel reservations. Note that
+ZONE_DMA is only 1GB big.
+
+Restore the previous behavior as the wide majority of devices are OK
+with reserving these in ZONE_DMA32. The ones that need them in ZONE_DMA
+will configure it explicitly.
+
+Fixes: 1a8e1cef7603 ("arm64: use both ZONE_DMA and ZONE_DMA32")
+Reported-by: Qian Cai <cai@lca.pw>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -91,7 +91,7 @@ static void __init reserve_crashkernel(v
+
+ if (crash_base == 0) {
+ /* Current arm64 boot protocol requires 2MB alignment */
+- crash_base = memblock_find_in_range(0, ARCH_LOW_ADDRESS_LIMIT,
++ crash_base = memblock_find_in_range(0, arm64_dma32_phys_limit,
+ crash_size, SZ_2M);
+ if (crash_base == 0) {
+ pr_warn("cannot allocate crashkernel (size:0x%llx)\n",
+@@ -459,7 +459,7 @@ void __init arm64_memblock_init(void)
+
+ high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
+
+- dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
++ dma_contiguous_reserve(arm64_dma32_phys_limit);
+ }
+
+ void __init bootmem_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch b/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch
new file mode 100644
index 0000000000..654864d52d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch
@@ -0,0 +1,105 @@
+From cd2d09e995bc72711b48b5c271b72e3ea3e99cdf Mon Sep 17 00:00:00 2001
+From: Will Deacon <will@kernel.org>
+Date: Tue, 3 Dec 2019 12:10:13 +0000
+Subject: [PATCH] arm64: mm: Fix initialisation of DMA zones on
+ non-NUMA systems
+
+commit 93b90414c33f59b7960bc8d607da0ce83377e021 upstream.
+
+John reports that the recently merged commit 1a8e1cef7603 ("arm64: use
+both ZONE_DMA and ZONE_DMA32") breaks the boot on his DB845C board:
+
+ | Booting Linux on physical CPU 0x0000000000 [0x517f803c]
+ | Linux version 5.4.0-mainline-10675-g957a03b9e38f
+ | Machine model: Thundercomm Dragonboard 845c
+ | [...]
+ | Built 1 zonelists, mobility grouping on. Total pages: -188245
+ | Kernel command line: earlycon
+ | firmware_class.path=/vendor/firmware/ androidboot.hardware=db845c
+ | init=/init androidboot.boot_devices=soc/1d84000.ufshc
+ | printk.devkmsg=on buildvariant=userdebug root=/dev/sda2
+ | androidboot.bootdevice=1d84000.ufshc androidboot.serialno=c4e1189c
+ | androidboot.baseband=sda
+ | msm_drm.dsi_display0=dsi_lt9611_1080_video_display:
+ | androidboot.slot_suffix=_a skip_initramfs rootwait ro init=/init
+ |
+ | <hangs indefinitely here>
+
+This is because, when CONFIG_NUMA=n, zone_sizes_init() fails to handle
+memblocks that fall entirely within the ZONE_DMA region and erroneously ends up
+trying to add a negatively-sized region into the following ZONE_DMA32, which is
+later interpreted as a large unsigned region by the core MM code.
+
+Rework the non-NUMA implementation of zone_sizes_init() so that the start
+address of the memblock being processed is adjusted according to the end of the
+previous zone, which is then range-checked before updating the hole information
+of subsequent zones.
+
+Cc: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
+Link: https://lore.kernel.org/lkml/CALAqxLVVcsmFrDKLRGRq7GewcW405yTOxG=KR3csVzQ6bXutkA@mail.gmail.com
+Fixes: 1a8e1cef7603 ("arm64: use both ZONE_DMA and ZONE_DMA32")
+Reported-by: John Stultz <john.stultz@linaro.org>
+Tested-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 25 +++++++++++--------------
+ 1 file changed, 11 insertions(+), 14 deletions(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -214,15 +214,14 @@ static void __init zone_sizes_init(unsig
+ {
+ struct memblock_region *reg;
+ unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+- unsigned long max_dma32 = min;
+- unsigned long max_dma = min;
++ unsigned long __maybe_unused max_dma, max_dma32;
+
+ memset(zone_size, 0, sizeof(zone_size));
+
++ max_dma = max_dma32 = min;
+ #ifdef CONFIG_ZONE_DMA
+- max_dma = PFN_DOWN(arm64_dma_phys_limit);
++ max_dma = max_dma32 = PFN_DOWN(arm64_dma_phys_limit);
+ zone_size[ZONE_DMA] = max_dma - min;
+- max_dma32 = max_dma;
+ #endif
+ #ifdef CONFIG_ZONE_DMA32
+ max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
+@@ -236,25 +235,23 @@ static void __init zone_sizes_init(unsig
+ unsigned long start = memblock_region_memory_base_pfn(reg);
+ unsigned long end = memblock_region_memory_end_pfn(reg);
+
+- if (start >= max)
+- continue;
+ #ifdef CONFIG_ZONE_DMA
+- if (start < max_dma) {
+- unsigned long dma_end = min_not_zero(end, max_dma);
++ if (start >= min && start < max_dma) {
++ unsigned long dma_end = min(end, max_dma);
+ zhole_size[ZONE_DMA] -= dma_end - start;
++ start = dma_end;
+ }
+ #endif
+ #ifdef CONFIG_ZONE_DMA32
+- if (start < max_dma32) {
++ if (start >= max_dma && start < max_dma32) {
+ unsigned long dma32_end = min(end, max_dma32);
+- unsigned long dma32_start = max(start, max_dma);
+- zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
++ zhole_size[ZONE_DMA32] -= dma32_end - start;
++ start = dma32_end;
+ }
+ #endif
+- if (end > max_dma32) {
++ if (start >= max_dma32 && start < max) {
+ unsigned long normal_end = min(end, max);
+- unsigned long normal_start = max(start, max_dma32);
+- zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
++ zhole_size[ZONE_NORMAL] -= normal_end - start;
+ }
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch
new file mode 100644
index 0000000000..a0dfcb3325
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch
@@ -0,0 +1,95 @@
+From a2e6d1c03908eccf76b9305c4a493230a36035c0 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Fri, 10 Jan 2020 18:29:35 +0100
+Subject: [PATCH] ARM: dts: bcm283x: Unify CMA configuration
+
+commit c5a1e5375d19bd4001c59dc5d482ac5b1ba51cbf upstream.
+
+With the introduction of the Raspberry Pi 4 we were forced to explicitly
+configure CMA's location, since arm64 defaults it into the ZONE_DMA32
+memory area, which is not good enough to perform DMA operations on that
+device. To bypass this limitation a dedicated CMA DT node was created,
+explicitly indicating the acceptable memory range and size.
+
+That said, compatibility between boards is a must on the Raspberry Pi
+ecosystem so this creates a common CMA DT node so as for DT overlays to
+be able to update CMA's properties regardless of the board being used.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Phil Elwell <phil@raspberrypi.org>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 32 +++++++++++++-------------------
+ arch/arm/boot/dts/bcm283x.dtsi | 13 +++++++++++++
+ 2 files changed, 26 insertions(+), 19 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -12,25 +12,6 @@
+
+ interrupt-parent = <&gicv2>;
+
+- reserved-memory {
+- #address-cells = <2>;
+- #size-cells = <1>;
+- ranges;
+-
+- /*
+- * arm64 reserves the CMA by default somewhere in ZONE_DMA32,
+- * that's not good enough for the BCM2711 as some devices can
+- * only address the lower 1G of memory (ZONE_DMA).
+- */
+- linux,cma {
+- compatible = "shared-dma-pool";
+- size = <0x2000000>; /* 32MB */
+- alloc-ranges = <0x0 0x00000000 0x40000000>;
+- reusable;
+- linux,cma-default;
+- };
+- };
+-
+ vc4: gpu {
+ compatible = "brcm,bcm2711-vc5";
+ status = "disabled";
+@@ -992,6 +973,19 @@
+ };
+ };
+
++&rmem {
++ #address-cells = <2>;
++};
++
++&cma {
++ /*
++ * arm64 reserves the CMA by default somewhere in ZONE_DMA32,
++ * that's not good enough for the BCM2711 as some devices can
++ * only address the lower 1G of memory (ZONE_DMA).
++ */
++ alloc-ranges = <0x0 0x00000000 0x40000000>;
++};
++
+ &i2c0 {
+ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
+ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
+--- a/arch/arm/boot/dts/bcm283x.dtsi
++++ b/arch/arm/boot/dts/bcm283x.dtsi
+@@ -30,6 +30,19 @@
+ stdout-path = "serial0:115200n8";
+ };
+
++ rmem: reserved-memory {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ cma: linux,cma {
++ compatible = "shared-dma-pool";
++ size = <0x4000000>; /* 64MB */
++ reusable;
++ linux,cma-default;
++ };
++ };
++
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ polling-delay-passive = <0>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch b/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch
new file mode 100644
index 0000000000..d3354bb9e1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch
@@ -0,0 +1,49 @@
+From cf40e83d2b6fb6857b13df4c8d69cc4c45395ea2 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Fri, 10 Jan 2020 18:19:33 +0100
+Subject: [PATCH] dma-contiguous: CMA: give precedence to cmdline
+
+commit 8c8c5a4994a306c217fd061cbfc5903399fd4c1c upstream.
+
+Although the device tree might contain a reserved-memory DT node
+dedicated as the default CMA pool, users might want to change CMA's
+parameters using the kernel command line for debugging purposes and
+whatnot. Honor this by bypassing the reserved memory CMA setup, which
+will ultimately end up freeing the memblock and allow the command line
+CMA configuration routine to run.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Phil Elwell <phil@raspberrypi.org>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+---
+ kernel/dma/contiguous.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/kernel/dma/contiguous.c
++++ b/kernel/dma/contiguous.c
+@@ -301,9 +301,16 @@ static int __init rmem_cma_setup(struct
+ phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
+ phys_addr_t mask = align - 1;
+ unsigned long node = rmem->fdt_node;
++ bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
+ struct cma *cma;
+ int err;
+
++ if (size_cmdline != -1 && default_cma) {
++ pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n",
++ rmem->name);
++ return -EBUSY;
++ }
++
+ if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
+ of_get_flat_dt_prop(node, "no-map", NULL))
+ return -EINVAL;
+@@ -321,7 +328,7 @@ static int __init rmem_cma_setup(struct
+ /* Architecture specific contiguous memory fixup. */
+ dma_contiguous_early_fixup(rmem->base, rmem->size);
+
+- if (of_get_flat_dt_prop(node, "linux,cma-default", NULL))
++ if (default_cma)
+ dma_contiguous_set_default(cma);
+
+ rmem->ops = &rmem_cma_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch b/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch
new file mode 100644
index 0000000000..fb4fe893c0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch
@@ -0,0 +1,36 @@
+From 4d0f0dfc57f6a8652c624575ea34f04bddea629b Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 2 Apr 2020 19:22:46 +0200
+Subject: [PATCH] ARM: dts: Use upstream CMA configuration
+
+Now that the kernel command line has precedence over the device tree,
+we can use the upstream CMA setup without breaking backward
+compatibility.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -195,7 +195,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 cma=64M";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1";
+ };
+
+ aliases {
+@@ -215,10 +215,6 @@
+ };
+
+ /delete-node/ wifi-pwrseq;
+-
+- reserved-memory {
+- /delete-node/ linux,cma;
+- };
+ };
+
+ &mmcnr {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch b/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch
new file mode 100644
index 0000000000..3c0f1e4388
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch
@@ -0,0 +1,871 @@
+From 3cd31a44e61e2219d730d6b1a4a13c8e15d6e395 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 2 Apr 2020 19:54:33 +0200
+Subject: [PATCH] ARM: dts: overlays: Unify overlay CMA handling
+
+Now that we don't have to abuse the kernel command line to change CMA's
+size we can clean-up and centralize CMA usage in overlays.
+
+A new file, cma-overlay.dts is created to be used as a standalone
+overlay or included on other overlays. All CMA users are converted to
+this scheme. Ultimately upstream-overlay.dts is also updated to use the
+default CMA size provided by upstream.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 19 +++++
+ arch/arm/boot/dts/overlays/cma-overlay.dts | 32 ++++++++
+ .../boot/dts/overlays/upstream-overlay.dts | 56 ++++---------
+ .../dts/overlays/upstream-pi4-overlay.dts | 66 +++++----------
+ .../dts/overlays/vc4-fkms-v3d-overlay.dts | 51 ++----------
+ .../boot/dts/overlays/vc4-kms-v3d-overlay.dts | 66 ++++-----------
+ .../dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 80 +++++--------------
+ 8 files changed, 129 insertions(+), 242 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/cma-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -28,6 +28,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ audiosense-pi.dtbo \
+ audremap.dtbo \
+ balena-fin.dtbo \
++ cma.dtbo \
+ dht11.dtbo \
+ dionaudio-loco.dtbo \
+ dionaudio-loco-v2.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -554,6 +554,19 @@ Info: This overlay is now deprecated -
+ Load: <Deprecated>
+
+
++Name: cma
++Info: Set custom CMA sizes, only use if you know what you are doing, might
++ clash with other overlays like vc4-fkms-v3d and vc4-kms-v3d.
++Load: dtoverlay=cma,<param>=<val>
++Params: cma-256 CMA is 256MB (needs 1GB)
++ cma-192 CMA is 192MB (needs 1GB)
++ cma-128 CMA is 128MB
++ cma-96 CMA is 96MB
++ cma-64 CMA is 64MB
++ cma-size CMA size in bytes, 4MB aligned
++ cma-default Use upstream's default value
++
++
+ Name: dht11
+ Info: Overlay for the DHT11/DHT21/DHT22 humidity/temperature sensors
+ Also sometimes found with the part number(s) AM230x.
+@@ -2675,6 +2688,8 @@ Params: cma-256 CMA is 2
+ cma-128 CMA is 128MB
+ cma-96 CMA is 96MB
+ cma-64 CMA is 64MB
++ cma-size CMA size in bytes, 4MB aligned
++ cma-default Use upstream's default value
+
+
+ Name: vc4-kms-kippah-7inch
+@@ -2692,6 +2707,8 @@ Params: cma-256 CMA is 2
+ cma-128 CMA is 128MB
+ cma-96 CMA is 96MB
+ cma-64 CMA is 64MB
++ cma-size CMA size in bytes, 4MB aligned
++ cma-default Use upstream's default value
+ audio Enable or disable audio over HDMI (default "on")
+
+
+@@ -2703,6 +2720,8 @@ Params: cma-256 CMA is 2
+ cma-128 CMA is 128MB
+ cma-96 CMA is 96MB
+ cma-64 CMA is 64MB
++ cma-size CMA size in bytes, 4MB aligned
++ cma-default Use upstream's default value
+ audio Enable or disable audio over HDMI0 (default
+ "on")
+ audio1 Enable or disable audio over HDMI1 (default
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/cma-overlay.dts
+@@ -0,0 +1,32 @@
++/*
++ * cma.dts
++ */
++
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&cma>;
++ frag0: __overlay__ {
++ /*
++ * The default size when using this overlay is 256 MB
++ * and should be kept as is for backwards
++ * compatibility.
++ */
++ size = <0x10000000>;
++ };
++ };
++
++ __overrides__ {
++ cma-256 = <&frag0>,"size:0=",<0x10000000>;
++ cma-192 = <&frag0>,"size:0=",<0xC000000>;
++ cma-128 = <&frag0>,"size:0=",<0x8000000>;
++ cma-96 = <&frag0>,"size:0=",<0x6000000>;
++ cma-64 = <&frag0>,"size:0=",<0x4000000>;
++ cma-size = <&frag0>,"size:0"; /* in bytes, 4MB aligned */
++ cma-default = <0>,"-0";
++ };
++};
+--- a/arch/arm/boot/dts/overlays/upstream-overlay.dts
++++ b/arch/arm/boot/dts/overlays/upstream-overlay.dts
+@@ -1,4 +1,4 @@
+-// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-96 dwc2-overlay.dts,dr_mode=otg
++// redo: ovmerge -c vc4-kms-v3d-overlay.dts,cma-default dwc2-overlay.dts,dr_mode=otg
+
+ /dts-v1/;
+ /plugin/;
+@@ -8,114 +8,90 @@
+ / {
+ compatible = "brcm,bcm2835";
+ fragment@0 {
+- target-path = "/chosen";
++ target = <&cma>;
+ __dormant__ {
+- bootargs = "cma=256M";
++ size = <0x10000000>;
+ };
+ };
+ fragment@1 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=192M";
+- };
+- };
+- fragment@2 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=128M";
+- };
+- };
+- fragment@3 {
+- target-path = "/chosen";
+- __overlay__ {
+- bootargs = "cma=96M";
+- };
+- };
+- fragment@4 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=64M";
+- };
+- };
+- fragment@5 {
+ target = <&i2c2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@6 {
++ fragment@2 {
+ target = <&fb>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+- fragment@7 {
++ fragment@3 {
+ target = <&pixelvalve0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@8 {
++ fragment@4 {
+ target = <&pixelvalve1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@9 {
++ fragment@5 {
+ target = <&pixelvalve2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@10 {
++ fragment@6 {
+ target = <&hvs>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@11 {
++ fragment@7 {
+ target = <&hdmi>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@12 {
++ fragment@8 {
+ target = <&v3d>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@13 {
++ fragment@9 {
+ target = <&vc4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@14 {
++ fragment@10 {
+ target = <&clocks>;
+ __overlay__ {
+ claim-clocks = <BCM2835_PLLD_DSI0 BCM2835_PLLD_DSI1 BCM2835_PLLH_AUX BCM2835_PLLH_PIX>;
+ };
+ };
+- fragment@15 {
++ fragment@11 {
+ target = <&vec>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@16 {
++ fragment@12 {
+ target = <&txp>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@17 {
++ fragment@13 {
+ target = <&hdmi>;
+ __dormant__ {
+ dmas;
+ };
+ };
+- fragment@18 {
++ fragment@14 {
+ target = <&usb>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+--- a/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/upstream-pi4-overlay.dts
+@@ -8,144 +8,120 @@
+ / {
+ compatible = "brcm,bcm2835";
+ fragment@0 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=256M";
+- };
+- };
+- fragment@1 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=192M";
+- };
+- };
+- fragment@2 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=128M";
+- };
+- };
+- fragment@3 {
+- target-path = "/chosen";
++ target = <&cma>;
+ __overlay__ {
+- bootargs = "cma=96M";
++ size = <100663296>;
+ };
+ };
+- fragment@4 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=64M";
+- };
+- };
+- fragment@5 {
++ fragment@1 {
+ target = <&ddc0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@6 {
++ fragment@2 {
+ target = <&ddc1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@7 {
++ fragment@3 {
+ target = <&hdmi0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@8 {
++ fragment@4 {
+ target = <&hdmi1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@9 {
++ fragment@5 {
+ target = <&hvs>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@10 {
++ fragment@6 {
+ target = <&pixelvalve0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@11 {
++ fragment@7 {
+ target = <&pixelvalve1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@12 {
++ fragment@8 {
+ target = <&pixelvalve2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@13 {
++ fragment@9 {
+ target = <&pixelvalve3>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@14 {
++ fragment@10 {
+ target = <&pixelvalve4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@15 {
++ fragment@11 {
+ target = <&v3d>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@16 {
++ fragment@12 {
+ target = <&vc4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@17 {
++ fragment@13 {
+ target = <&txp>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+- fragment@18 {
++ fragment@14 {
+ target = <&fb>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+- fragment@19 {
++ fragment@15 {
+ target = <&firmwarekms>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+- fragment@20 {
++ fragment@16 {
+ target = <&vec>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+- fragment@21 {
++ fragment@17 {
+ target = <&hdmi0>;
+ __dormant__ {
+ dmas;
+ };
+ };
+- fragment@22 {
++ fragment@18 {
+ target = <&hdmi1>;
+ __dormant__ {
+ dmas;
+ };
+ };
+- fragment@23 {
++ fragment@19 {
+ target = <&usb>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
+@@ -5,77 +5,36 @@
+ /dts-v1/;
+ /plugin/;
+
++#include "cma-overlay.dts"
++
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
+- target-path = "/chosen";
+- __overlay__ {
+- bootargs = "cma=256M";
+- };
+- };
+-
+ fragment@1 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=192M";
+- };
+- };
+-
+- fragment@2 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=128M";
+- };
+- };
+-
+- fragment@3 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=96M";
+- };
+- };
+-
+- fragment@4 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=64M";
+- };
+- };
+-
+- fragment@5 {
+ target = <&fb>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+- fragment@6 {
++ fragment@2 {
+ target = <&firmwarekms>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@7 {
++ fragment@3 {
+ target = <&v3d>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@8 {
++ fragment@4 {
+ target = <&vc4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+-
+- __overrides__ {
+- cma-256 = <0>,"+0-1-2-3-4";
+- cma-192 = <0>,"-0+1-2-3-4";
+- cma-128 = <0>,"-0-1+2-3-4";
+- cma-96 = <0>,"-0-1-2+3-4";
+- cma-64 = <0>,"-0-1-2-3+4";
+- };
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
+@@ -7,108 +7,75 @@
+
+ #include <dt-bindings/clock/bcm2835.h>
+
++#include "cma-overlay.dts"
++
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
+- target-path = "/chosen";
+- __overlay__ {
+- bootargs = "cma=256M";
+- };
+- };
+-
+ fragment@1 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=192M";
+- };
+- };
+-
+- fragment@2 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=128M";
+- };
+- };
+-
+- fragment@3 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=96M";
+- };
+- };
+-
+- fragment@4 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=64M";
+- };
+- };
+-
+- fragment@5 {
+ target = <&i2c2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@6 {
++ fragment@2 {
+ target = <&fb>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+- fragment@7 {
++ fragment@3 {
+ target = <&pixelvalve0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@8 {
++ fragment@4 {
+ target = <&pixelvalve1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@9 {
++ fragment@5 {
+ target = <&pixelvalve2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@10 {
++ fragment@6 {
+ target = <&hvs>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@11 {
++ fragment@7 {
+ target = <&hdmi>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@12 {
++ fragment@8 {
+ target = <&v3d>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@13 {
++ fragment@9 {
+ target = <&vc4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@14 {
++ fragment@10 {
+ target = <&clocks>;
+ __overlay__ {
+ claim-clocks = <
+@@ -120,21 +87,21 @@
+ };
+ };
+
+- fragment@15 {
++ fragment@11 {
+ target = <&vec>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@16 {
++ fragment@12 {
+ target = <&txp>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@17 {
++ fragment@13 {
+ target = <&hdmi>;
+ __dormant__ {
+ dmas;
+@@ -142,11 +109,6 @@
+ };
+
+ __overrides__ {
+- cma-256 = <0>,"+0-1-2-3-4";
+- cma-192 = <0>,"-0+1-2-3-4";
+- cma-128 = <0>,"-0-1+2-3-4";
+- cma-96 = <0>,"-0-1-2+3-4";
+- cma-64 = <0>,"-0-1-2-3+4";
+ audio = <0>,"!17";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
+@@ -7,164 +7,131 @@
+
+ #include <dt-bindings/clock/bcm2835.h>
+
++#include "cma-overlay.dts"
++
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
+- target-path = "/chosen";
+- __overlay__ {
+- bootargs = "cma=256M";
+- };
+- };
+-
+ fragment@1 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=192M";
+- };
+- };
+-
+- fragment@2 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=128M";
+- };
+- };
+-
+- fragment@3 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=96M";
+- };
+- };
+-
+- fragment@4 {
+- target-path = "/chosen";
+- __dormant__ {
+- bootargs = "cma=64M";
+- };
+- };
+-
+- fragment@5 {
+ target = <&ddc0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@6 {
++ fragment@2 {
+ target = <&ddc1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@7 {
++ fragment@3 {
+ target = <&hdmi0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@8 {
++ fragment@4 {
+ target = <&hdmi1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@9 {
++ fragment@5 {
+ target = <&hvs>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@10 {
++ fragment@6 {
+ target = <&pixelvalve0>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@11 {
++ fragment@7 {
+ target = <&pixelvalve1>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@12 {
++ fragment@8 {
+ target = <&pixelvalve2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@13 {
++ fragment@9 {
+ target = <&pixelvalve3>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@14 {
++ fragment@10 {
+ target = <&pixelvalve4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@15 {
++ fragment@11 {
+ target = <&v3d>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@16 {
++ fragment@12 {
+ target = <&vc4>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@17 {
++ fragment@13 {
+ target = <&txp>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@18 {
++ fragment@14 {
+ target = <&fb>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+- fragment@19 {
++ fragment@15 {
+ target = <&firmwarekms>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+- fragment@20 {
++ fragment@16 {
+ target = <&vec>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+
+- fragment@21 {
++ fragment@17 {
+ target = <&hdmi0>;
+ __dormant__ {
+ dmas;
+ };
+ };
+
+- fragment@22 {
++ fragment@18 {
+ target = <&hdmi1>;
+ __dormant__ {
+ dmas;
+@@ -172,12 +139,7 @@
+ };
+
+ __overrides__ {
+- cma-256 = <0>,"+0-1-2-3-4";
+- cma-192 = <0>,"-0+1-2-3-4";
+- cma-128 = <0>,"-0-1+2-3-4";
+- cma-96 = <0>,"-0-1-2+3-4";
+- cma-64 = <0>,"-0-1-2-3+4";
+- audio = <0>,"!21";
+- audio1 = <0>,"!22";
++ audio = <0>,"!17";
++ audio1 = <0>,"!18";
+ };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch b/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch
new file mode 100644
index 0000000000..31840e5437
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch
@@ -0,0 +1,28 @@
+From 142ad0b8433d6beb87070bd39a6b2d23ca9cae30 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 19 Mar 2020 20:00:13 +0100
+Subject: [PATCH] ARM: dts: bcm283x: Fix vc4's firmware bus DMA
+ limitations
+
+The bus is virtual and devices have to inherit their DMA constraints
+from the underlying interconnect. So add an empty dma-ranges property to
+the bus node, implying the firmware bus' DMA constraints are identical to
+its parent's.
+
+Fixes: 7dbe8c62ceeb ("ARM: dts: Add minimal Raspberry Pi 4 support")
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ arch/arm/boot/dts/bcm2835-rpi.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
+@@ -15,6 +15,7 @@
+ firmware: firmware {
+ compatible = "raspberrypi,bcm2835-firmware", "simple-bus";
+ mboxes = <&mailbox>;
++ dma-ranges;
+ };
+
+ power: power {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch b/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch
new file mode 100644
index 0000000000..9a1e29e7d1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch
@@ -0,0 +1,33 @@
+From 7b307016ed13cbb65e08b6a704912e5c9e5b81ac Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 14 Apr 2020 15:25:02 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Restrict CMA to first 768MB
+
+The downstream 32-bit 2711 kernel configuration enables HIGHMEM for
+access to more physical RAM. The HIGHMEM zone starts at 0x30000000
+(768MB), and allowing the CMA zone to overlap that area causes a
+failure during CMA activation.
+
+Avoid the overlap by limiting CMA to the first 768MB. This is overly
+restrictive on a 64-bit kernel, but shouldn't cause any practical
+problems.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -45,6 +45,11 @@
+ };
+ };
+
++&cma {
++ /* Limit cma to the lower 768MB to allow room for HIGHMEM on 32-bit */
++ alloc-ranges = <0x0 0x00000000 0x30000000>;
++};
++
+ &soc {
+ thermal: thermal@7d5d2200 {
+ compatible = "brcm,avs-tmon-bcm2711";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch b/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch
new file mode 100644
index 0000000000..8eb2b16b10
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch
@@ -0,0 +1,23 @@
+From 9773c8c521f45f34631bcb147d638ba0252bbdd4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 4 Feb 2020 12:51:56 +0000
+Subject: [PATCH] ARM: dts: Extend SCB bus address range
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -69,7 +69,9 @@
+ <0x0 0x40000000 0x0 0xff800000 0x00800000>,
+ <0x6 0x00000000 0x6 0x00000000 0x40000000>,
+ <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
+- dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>,
++ <0x1 0x00000000 0x1 0x00000000 0x80000000>,
++ <0x1 0x80000000 0x1 0x80000000 0x80000000>;
+
+ dma40: dma@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch b/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch
new file mode 100644
index 0000000000..52cf03a382
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch
@@ -0,0 +1,52 @@
+From f97ba33547711d727fbbcb10eb046f8ac605a966 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.org>
+Date: Thu, 5 Dec 2019 18:02:08 +0000
+Subject: [PATCH] dts: bcm2711: Move emmc2 to its own "bus"
+
+Moving the EMMC2 controller under a dedicated bus allows the firmware
+to patch the dma-ranges property for different memory sizes without
+affecting anything else.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.org>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -193,6 +193,8 @@
+ #include "bcm2711-rpi.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
+
++/delete-node/ &emmc2;
++
+ / {
+ chosen {
+ bootargs = "coherent_pool=1M 8250.nr_uarts=1";
+@@ -212,6 +214,26 @@
+ /delete-property/ ethernet;
+ /delete-property/ intc;
+ pcie0 = &pcie0;
++ emmc2bus = &emmc2bus;
++ };
++
++ emmc2bus: emmc2bus {
++ compatible = "simple-bus";
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ ranges = <0x0 0x7e000000 0x0 0xfe000000 0x01800000>;
++ dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x3c000000>;
++
++ emmc2: emmc2@7e340000 {
++ compatible = "brcm,bcm2711-emmc2";
++ status = "okay";
++ interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clocks BCM2711_CLOCK_EMMC2>;
++ reg = <0x0 0x7e340000 0x100>;
++ vqmmc-supply = <&sd_io_1v8_reg>;
++ broken-cd;
++ };
+ };
+
+ /delete-node/ wifi-pwrseq;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch b/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch
new file mode 100644
index 0000000000..1b1cb143ae
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch
@@ -0,0 +1,29 @@
+From ba875ce27cd407bc61502517671623df07bb6c1a Mon Sep 17 00:00:00 2001
+From: James Hilliard <james.hilliard1@gmail.com>
+Date: Fri, 10 Apr 2020 19:24:40 -0600
+Subject: [PATCH] drm/vc4: hdmi: Silence pixel clock error on
+ -EPROBE_DEFER
+
+If the vc4 hdmi driver loads before the pixel clock is available we
+see a spurious "*ERROR* Failed to get pixel clock" error.
+
+Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1493,8 +1493,10 @@ static int vc4_hdmi_init_resources(struc
+
+ vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel");
+ if (IS_ERR(vc4_hdmi->pixel_clock)) {
+- DRM_ERROR("Failed to get pixel clock\n");
+- return PTR_ERR(vc4_hdmi->pixel_clock);
++ ret = PTR_ERR(vc4_hdmi->pixel_clock);
++ if (ret != -EPROBE_DEFER)
++ DRM_ERROR("Failed to get pixel clock\n");
++ return ret;
+ }
+
+ vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch b/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch
new file mode 100644
index 0000000000..f7b1217527
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch
@@ -0,0 +1,41 @@
+From 8695764265eb6e749c9fdc901e98c2e7b4d2adfc Mon Sep 17 00:00:00 2001
+From: James Hilliard <james.hilliard1@gmail.com>
+Date: Fri, 10 Apr 2020 20:23:13 -0600
+Subject: [PATCH] component: Silence bind error on -EPROBE_DEFER
+
+If a component fails to bind due to -EPROBE_DEFER we should not log an
+error as this is not a real failure.
+
+Fixes:
+vc4-drm soc:gpu: failed to bind 3f902000.hdmi (ops vc4_hdmi_ops): -517
+vc4-drm soc:gpu: master bind failed: -517
+
+Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
+---
+ drivers/base/component.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/base/component.c
++++ b/drivers/base/component.c
+@@ -257,7 +257,8 @@ static int try_to_bring_up_master(struct
+ ret = master->ops->bind(master->dev);
+ if (ret < 0) {
+ devres_release_group(master->dev, NULL);
+- dev_info(master->dev, "master bind failed: %d\n", ret);
++ if (ret != -EPROBE_DEFER)
++ dev_info(master->dev, "master bind failed: %d\n", ret);
+ return ret;
+ }
+
+@@ -611,8 +612,9 @@ static int component_bind(struct compone
+ devres_release_group(component->dev, NULL);
+ devres_release_group(master->dev, NULL);
+
+- dev_err(master->dev, "failed to bind %s (ops %ps): %d\n",
+- dev_name(component->dev), component->ops, ret);
++ if (ret != -EPROBE_DEFER)
++ dev_err(master->dev, "failed to bind %s (ops %ps): %d\n",
++ dev_name(component->dev), component->ops, ret);
+ }
+
+ return ret;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch b/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch
new file mode 100644
index 0000000000..f1427ae71f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch
@@ -0,0 +1,42 @@
+From f303194bc24925d3efd965ccfae40974ea437240 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?J=C3=B6rg=20Schambacher?=
+ <j-schambacher@users.noreply.github.com>
+Date: Wed, 15 Apr 2020 11:48:29 +0200
+Subject: [PATCH] Fixes a problem with clock settings of HiFiBerry
+ DAC+ADC PRO (#3545)
+
+This patch fixes a problem of the re-calculation of
+i2s-clock and -parameter settings when only the ADC is activated.
+
+Signed-off-by: Joerg Schambacher <joerg@i2audio.com>
+---
+ sound/soc/bcm/hifiberry_dacplusadcpro.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/sound/soc/bcm/hifiberry_dacplusadcpro.c
++++ b/sound/soc/bcm/hifiberry_dacplusadcpro.c
+@@ -390,9 +390,11 @@ static int snd_rpi_hifiberry_dacplusadcp
+ int channels = params_channels(params);
+ int width = 32;
+ struct snd_soc_component *dac = rtd->codec_dais[0]->component;
++ struct snd_soc_dai *dai = rtd->codec_dais[0];
++ struct snd_soc_dai_driver *drv = dai->driver;
++ const struct snd_soc_dai_ops *ops = drv->ops;
+
+ if (snd_rpi_hifiberry_is_dacpro) {
+-
+ width = snd_pcm_format_physical_width(params_format(params));
+
+ snd_rpi_hifiberry_dacplusadcpro_set_sclk(dac,
+@@ -414,6 +416,11 @@ static int snd_rpi_hifiberry_dacplusadcp
+ return ret;
+ ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x03, 0x03,
+ channels, width);
++ if (ret)
++ return ret;
++
++ if (snd_rpi_hifiberry_is_dacpro && ops->hw_params)
++ ret = ops->hw_params(substream, params, dai);
+ return ret;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch b/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch
new file mode 100644
index 0000000000..f843a83a7d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch
@@ -0,0 +1,34 @@
+From afd6952c4dfb0d4945b496ef08b2f478d6f40097 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo@jmondi.org>
+Date: Tue, 7 Apr 2020 17:21:55 +0200
+Subject: [PATCH] Documentation: media: Update sub-device API intro
+
+Update the V4L2 sub-device userspace API introduction to provide more
+details on why complex devices might want to register devnodes for the
+connected subdevices.
+
+Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
+---
+ Documentation/media/kapi/v4l2-subdev.rst | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+--- a/Documentation/media/kapi/v4l2-subdev.rst
++++ b/Documentation/media/kapi/v4l2-subdev.rst
+@@ -275,8 +275,13 @@ system the .unbind() method is called. A
+ V4L2 sub-device userspace API
+ -----------------------------
+
+-Beside exposing a kernel API through the :c:type:`v4l2_subdev_ops` structure,
+-V4L2 sub-devices can also be controlled directly by userspace applications.
++Bridge drivers traditionally expose one or multiple video nodes to userspace,
++and control subdevices through the :c:type:`v4l2_subdev_ops` operations in
++response to video node operations. This hides the complexity of the underlying
++hardware from applications. For complex devices, finer-grained control of the
++device than what the video nodes offer may be required. In those cases, bridge
++drivers that implement :ref:`the media controller API <media_controller>` may
++opt for making the subdevice operations directly accessible from userpace.
+
+ Device nodes named ``v4l-subdev``\ *X* can be created in ``/dev`` to access
+ sub-devices directly. If a sub-device supports direct userspace configuration
diff --git a/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch b/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch
new file mode 100644
index 0000000000..1983334ae1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch
@@ -0,0 +1,217 @@
+From c1a630e792140b4791bad84974e31b4a1cf09b1b Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo@jmondi.org>
+Date: Tue, 7 Apr 2020 17:21:56 +0200
+Subject: [PATCH] Documentation: media: Document read-only subdevice
+
+Document a new kAPI function to register subdev device nodes in read only
+mode and for each affected ioctl report how access is restricted.
+
+Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
+---
+ Documentation/media/kapi/v4l2-subdev.rst | 44 +++++++++++++++++++
+ Documentation/media/uapi/v4l/dev-subdev.rst | 5 +++
+ .../media/uapi/v4l/vidioc-g-dv-timings.rst | 6 +++
+ Documentation/media/uapi/v4l/vidioc-g-std.rst | 6 +++
+ .../media/uapi/v4l/vidioc-subdev-g-crop.rst | 9 ++++
+ .../media/uapi/v4l/vidioc-subdev-g-fmt.rst | 8 ++++
+ .../v4l/vidioc-subdev-g-frame-interval.rst | 8 ++++
+ .../uapi/v4l/vidioc-subdev-g-selection.rst | 8 ++++
+ 8 files changed, 94 insertions(+)
+
+--- a/Documentation/media/kapi/v4l2-subdev.rst
++++ b/Documentation/media/kapi/v4l2-subdev.rst
+@@ -332,6 +332,50 @@ Private ioctls
+ All ioctls not in the above list are passed directly to the sub-device
+ driver through the core::ioctl operation.
+
++Read-only sub-device userspace API
++----------------------------------
++
++Bridge drivers that control their connected subdevices through direct calls to
++the kernel API realized by :c:type:`v4l2_subdev_ops` structure do not usually
++want userspace to be able to change the same parameters through the subdevice
++device node and thus do not usually register any.
++
++It is sometimes useful to report to userspace the current subdevice
++configuration through a read-only API, that does not permit applications to
++change to the device parameters but allows interfacing to the subdevice device
++node to inspect them.
++
++For instance, to implement cameras based on computational photography, userspace
++needs to know the detailed camera sensor configuration (in terms of skipping,
++binning, cropping and scaling) for each supported output resolution. To support
++such use cases, bridge drivers may expose the subdevice operations to userspace
++through a read-only API.
++
++To create a read-only device node for all the subdevices registered with the
++``V4L2_SUBDEV_FL_HAS_DEVNODE`` set, the :c:type:`v4l2_device` driver should call
++:c:func:`v4l2_device_register_ro_subdev_nodes`.
++
++Access to the following ioctls for userspace applications is restricted on
++sub-device device nodes registered with
++:c:func:`v4l2_device_register_ro_subdev_nodes`.
++
++``VIDIOC_SUBDEV_S_FMT``,
++``VIDIOC_SUBDEV_S_CROP``,
++``VIDIOC_SUBDEV_S_SELECTION``:
++
++ These ioctls are only allowed on a read-only subdevice device node
++ for the :ref:`V4L2_SUBDEV_FORMAT_TRY <v4l2-subdev-format-whence>`
++ formats and selection rectangles.
++
++``VIDIOC_SUBDEV_S_FRAME_INTERVAL``,
++``VIDIOC_SUBDEV_S_DV_TIMINGS``,
++``VIDIOC_SUBDEV_S_STD``:
++
++ These ioctls are not allowed on a read-only subdevice node.
++
++In case the ioctl is not allowed, or the format to modify is set to
++``V4L2_SUBDEV_FORMAT_ACTIVE``, the core returns a negative error code and
++the errno variable is set to ``-EPERM``.
+
+ I2C sub-device drivers
+ ----------------------
+--- a/Documentation/media/uapi/v4l/dev-subdev.rst
++++ b/Documentation/media/uapi/v4l/dev-subdev.rst
+@@ -39,6 +39,11 @@ will feature a character device node on
+ Sub-device character device nodes, conventionally named
+ ``/dev/v4l-subdev*``, use major number 81.
+
++Drivers may opt to limit the sub-device character devices to only expose
++operations that do not modify the device state. In such a case the sub-devices
++are referred to as ``read-only`` in the rest of this documentation, and the
++related restrictions are documented in individual ioctls.
++
+
+ Controls
+ ========
+--- a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst
++++ b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst
+@@ -57,6 +57,10 @@ pointer to the struct :c:type:`v4l2_dv_t
+ structure as argument. If the ioctl is not supported or the timing
+ values are not correct, the driver returns ``EINVAL`` error code.
+
++Calling ``VIDIOC_SUBDEV_S_DV_TIMINGS`` on a subdev device node that has been
++registered in read-only mode is not allowed. An error is returned and the errno
++variable is set to ``-EPERM``.
++
+ The ``linux/v4l2-dv-timings.h`` header can be used to get the timings of
+ the formats in the :ref:`cea861` and :ref:`vesadmt` standards. If
+ the current input or output does not support DV timings (e.g. if
+@@ -81,6 +85,8 @@ ENODATA
+ EBUSY
+ The device is busy and therefore can not change the timings.
+
++EPERM
++ ``VIDIOC_SUBDEV_S_DV_TIMINGS`` has been called on a read-only subdevice.
+
+ .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
+
+--- a/Documentation/media/uapi/v4l/vidioc-g-std.rst
++++ b/Documentation/media/uapi/v4l/vidioc-g-std.rst
+@@ -66,6 +66,9 @@ video timings (e.g. if :ref:`VIDIOC_ENUM
+ does not set the ``V4L2_IN_CAP_STD`` flag), then ``ENODATA`` error code is
+ returned.
+
++Calling ``VIDIOC_SUBDEV_S_STD`` on a subdev device node that has been registered
++in read-only mode is not allowed. An error is returned and the errno variable is
++set to ``-EPERM``.
+
+ Return Value
+ ============
+@@ -79,3 +82,6 @@ EINVAL
+
+ ENODATA
+ Standard video timings are not supported for this input or output.
++
++EPERM
++ ``VIDIOC_SUBDEV_S_STD`` has been called on a read-only subdevice.
+--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-crop.rst
++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-crop.rst
+@@ -73,6 +73,11 @@ crop rectangles and stored in the sub-de
+ applications querying the same sub-device would thus not interact with
+ each other.
+
++If the subdev device node has been registered in read-only mode, calls to
++``VIDIOC_SUBDEV_S_CROP`` are only valid if the ``which`` field is set to
++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno
++variable is set to ``-EPERM``.
++
+ Drivers must not return an error solely because the requested crop
+ rectangle doesn't match the device capabilities. They must instead
+ modify the rectangle to match what the hardware can provide. The
+@@ -123,3 +128,7 @@ EINVAL
+ references a non-existing pad, the ``which`` field references a
+ non-existing format, or cropping is not supported on the given
+ subdev pad.
++
++EPERM
++ The ``VIDIOC_SUBDEV_S_CROP`` ioctl has been called on a read-only subdevice
++ and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``.
+--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-fmt.rst
++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-fmt.rst
+@@ -78,6 +78,11 @@ current links configuration or sub-devic
+ a low-pass noise filter might crop pixels at the frame boundaries,
+ modifying its output frame size.
+
++If the subdev device node has been registered in read-only mode, calls to
++``VIDIOC_SUBDEV_S_FMT`` are only valid if the ``which`` field is set to
++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno
++variable is set to ``-EPERM``.
++
+ Drivers must not return an error solely because the requested format
+ doesn't match the device capabilities. They must instead modify the
+ format to match what the hardware can provide. The modified format
+@@ -146,6 +151,9 @@ EINVAL
+ ``pad`` references a non-existing pad, or the ``which`` field
+ references a non-existing format.
+
++EPERM
++ The ``VIDIOC_SUBDEV_S_FMT`` ioctl has been called on a read-only subdevice
++ and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``.
+
+ ============
+
+--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-frame-interval.rst
++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-frame-interval.rst
+@@ -65,6 +65,10 @@ struct
+ contains the current frame interval as would be returned by a
+ ``VIDIOC_SUBDEV_G_FRAME_INTERVAL`` call.
+
++Calling ``VIDIOC_SUBDEV_S_FRAME_INTERVAL`` on a subdev device node that has been
++registered in read-only mode is not allowed. An error is returned and the errno
++variable is set to ``-EPERM``.
++
+ Drivers must not return an error solely because the requested interval
+ doesn't match the device capabilities. They must instead modify the
+ interval to match what the hardware can provide. The modified interval
+@@ -118,3 +122,7 @@ EINVAL
+ :c:type:`v4l2_subdev_frame_interval`
+ ``pad`` references a non-existing pad, or the pad doesn't support
+ frame intervals.
++
++EPERM
++ The ``VIDIOC_SUBDEV_S_FRAME_INTERVAL`` ioctl has been called on a read-only
++ subdevice.
+--- a/Documentation/media/uapi/v4l/vidioc-subdev-g-selection.rst
++++ b/Documentation/media/uapi/v4l/vidioc-subdev-g-selection.rst
+@@ -53,6 +53,10 @@ function of the crop API, and more, are
+ See :ref:`subdev` for more information on how each selection target
+ affects the image processing pipeline inside the subdevice.
+
++If the subdev device node has been registered in read-only mode, calls to
++``VIDIOC_SUBDEV_S_SELECTION`` are only valid if the ``which`` field is set to
++``V4L2_SUBDEV_FORMAT_TRY``, otherwise an error is returned and the errno
++variable is set to ``-EPERM``.
+
+ Types of selection targets
+ --------------------------
+@@ -123,3 +127,7 @@ EINVAL
+ ``pad`` references a non-existing pad, the ``which`` field
+ references a non-existing format, or the selection target is not
+ supported on the given subdev pad.
++
++EPERM
++ The ``VIDIOC_SUBDEV_S_SELECTION`` ioctl has been called on a read-only
++ subdevice and the ``which`` field is set to ``V4L2_SUBDEV_FORMAT_ACTIVE``.
diff --git a/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch b/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch
new file mode 100644
index 0000000000..47f2551ec7
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch
@@ -0,0 +1,206 @@
+From 90712c1a495c2aa4b10dd8127fdd7f1a0cd9ef00 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo@jmondi.org>
+Date: Tue, 7 Apr 2020 17:21:57 +0200
+Subject: [PATCH] media: v4l2-dev: Add
+ v4l2_device_register_ro_subdev_node()
+
+Add to the V4L2 core a function to register device nodes for video
+subdevices in read-only mode.
+
+Registering a device node in read-only mode is useful to expose to
+userspace the current sub-device configuration, without allowing
+application to change it by using the V4L2 subdevice ioctls.
+
+Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
+---
+ drivers/media/v4l2-core/v4l2-device.c | 7 ++--
+ drivers/media/v4l2-core/v4l2-subdev.c | 19 ++++++++++
+ include/media/v4l2-dev.h | 7 ++++
+ include/media/v4l2-device.h | 50 ++++++++++++++++++++++++---
+ 4 files changed, 77 insertions(+), 6 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-device.c
++++ b/drivers/media/v4l2-core/v4l2-device.c
+@@ -189,7 +189,8 @@ static void v4l2_device_release_subdev_n
+ kfree(vdev);
+ }
+
+-int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
++int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev,
++ bool read_only)
+ {
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+@@ -218,6 +219,8 @@ int v4l2_device_register_subdev_nodes(st
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = v4l2_device_release_subdev_node;
+ vdev->ctrl_handler = sd->ctrl_handler;
++ if (read_only)
++ set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
+ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (err < 0) {
+@@ -255,7 +258,7 @@ clean_up:
+
+ return err;
+ }
+-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
++EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes);
+
+ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+ {
+--- a/drivers/media/v4l2-core/v4l2-subdev.c
++++ b/drivers/media/v4l2-core/v4l2-subdev.c
+@@ -331,6 +331,7 @@ static long subdev_do_ioctl(struct file
+ struct v4l2_fh *vfh = file->private_data;
+ #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
++ bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
+ int rval;
+ #endif
+
+@@ -453,6 +454,9 @@ static long subdev_do_ioctl(struct file
+ case VIDIOC_SUBDEV_S_FMT: {
+ struct v4l2_subdev_format *format = arg;
+
++ if (format->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
++ return -EPERM;
++
+ memset(format->reserved, 0, sizeof(format->reserved));
+ memset(format->format.reserved, 0, sizeof(format->format.reserved));
+ return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format);
+@@ -480,6 +484,9 @@ static long subdev_do_ioctl(struct file
+ struct v4l2_subdev_crop *crop = arg;
+ struct v4l2_subdev_selection sel;
+
++ if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
++ return -EPERM;
++
+ memset(crop->reserved, 0, sizeof(crop->reserved));
+ memset(&sel, 0, sizeof(sel));
+ sel.which = crop->which;
+@@ -521,6 +528,9 @@ static long subdev_do_ioctl(struct file
+ case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
+ struct v4l2_subdev_frame_interval *fi = arg;
+
++ if (ro_subdev)
++ return -EPERM;
++
+ memset(fi->reserved, 0, sizeof(fi->reserved));
+ return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+ }
+@@ -544,6 +554,9 @@ static long subdev_do_ioctl(struct file
+ case VIDIOC_SUBDEV_S_SELECTION: {
+ struct v4l2_subdev_selection *sel = arg;
+
++ if (sel->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
++ return -EPERM;
++
+ memset(sel->reserved, 0, sizeof(sel->reserved));
+ return v4l2_subdev_call(
+ sd, pad, set_selection, subdev_fh->pad, sel);
+@@ -580,6 +593,9 @@ static long subdev_do_ioctl(struct file
+ return v4l2_subdev_call(sd, video, g_dv_timings, arg);
+
+ case VIDIOC_SUBDEV_S_DV_TIMINGS:
++ if (ro_subdev)
++ return -EPERM;
++
+ return v4l2_subdev_call(sd, video, s_dv_timings, arg);
+
+ case VIDIOC_SUBDEV_G_STD:
+@@ -588,6 +604,9 @@ static long subdev_do_ioctl(struct file
+ case VIDIOC_SUBDEV_S_STD: {
+ v4l2_std_id *std = arg;
+
++ if (ro_subdev)
++ return -EPERM;
++
+ return v4l2_subdev_call(sd, video, s_std, *std);
+ }
+
+--- a/include/media/v4l2-dev.h
++++ b/include/media/v4l2-dev.h
+@@ -82,11 +82,18 @@ struct v4l2_ctrl_handler;
+ * but the old crop API will still work as expected in order to preserve
+ * backwards compatibility.
+ * Never set this flag for new drivers.
++ * @V4L2_FL_SUBDEV_RO_DEVNODE:
++ * indicates that the video device node is registered in read-only mode.
++ * The flag only applies to device nodes registered for sub-devices, it is
++ * set by the core when the sub-devices device nodes are registered with
++ * v4l2_device_register_ro_subdev_nodes() and used by the sub-device ioctl
++ * handler to restrict access to some ioctl calls.
+ */
+ enum v4l2_video_device_flags {
+ V4L2_FL_REGISTERED = 0,
+ V4L2_FL_USES_V4L2_FH = 1,
+ V4L2_FL_QUIRK_INVERTED_CROP = 2,
++ V4L2_FL_SUBDEV_RO_DEVNODE = 3,
+ };
+
+ /* Priority helper functions */
+--- a/include/media/v4l2-device.h
++++ b/include/media/v4l2-device.h
+@@ -174,14 +174,56 @@ int __must_check v4l2_device_register_su
+ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
+
+ /**
+- * v4l2_device_register_subdev_nodes - Registers device nodes for all subdevs
+- * of the v4l2 device that are marked with
+- * the %V4L2_SUBDEV_FL_HAS_DEVNODE flag.
++ * __v4l2_device_register_ro_subdev_nodes - Registers device nodes for
++ * all subdevs of the v4l2 device that are marked with the
++ * %V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ *
+ * @v4l2_dev: pointer to struct v4l2_device
++ * @read_only: subdevices read-only flag. True to register the subdevices
++ * device nodes in read-only mode, false to allow full access to the
++ * subdevice userspace API.
+ */
+ int __must_check
+-v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev);
++__v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev,
++ bool read_only);
++
++/**
++ * v4l2_device_register_subdev_nodes - Registers subdevices device nodes with
++ * unrestricted access to the subdevice userspace operations
++ *
++ * Internally calls __v4l2_device_register_subdev_nodes(). See its documentation
++ * for more details.
++ *
++ * @v4l2_dev: pointer to struct v4l2_device
++ */
++static inline int __must_check
++v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
++{
++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
++ return __v4l2_device_register_subdev_nodes(v4l2_dev, false);
++#else
++ return 0;
++#endif
++}
++
++/**
++ * v4l2_device_register_ro_subdev_nodes - Registers subdevices device nodes
++ * in read-only mode
++ *
++ * Internally calls __v4l2_device_register_subdev_nodes(). See its documentation
++ * for more details.
++ *
++ * @v4l2_dev: pointer to struct v4l2_device
++ */
++static inline int __must_check
++v4l2_device_register_ro_subdev_nodes(struct v4l2_device *v4l2_dev)
++{
++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
++ return __v4l2_device_register_subdev_nodes(v4l2_dev, true);
++#else
++ return 0;
++#endif
++}
+
+ /**
+ * v4l2_subdev_notify - Sends a notification to v4l2_device.
diff --git a/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch b/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch
new file mode 100644
index 0000000000..e142f598ad
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch
@@ -0,0 +1,2709 @@
+From 33a897162230dfc35b854aae2bec1ce8c2996642 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 1 Apr 2020 08:39:49 +0100
+Subject: [PATCH] media: bcm2835-unicam: Driver for CCP2/CSI2 camera
+ interface
+
+Add driver for the Unicam camera receiver block on
+BCM283x processors.
+
+This commit is made up of a series of changes cherry-picked from the
+rpi-4.19.y branch.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ MAINTAINERS | 2 +-
+ drivers/media/platform/Kconfig | 1 +
+ drivers/media/platform/Makefile | 2 +
+ drivers/media/platform/bcm2835/Kconfig | 14 +
+ drivers/media/platform/bcm2835/Makefile | 3 +
+ .../media/platform/bcm2835/bcm2835-unicam.c | 2369 +++++++++++++++++
+ .../media/platform/bcm2835/vc4-regs-unicam.h | 253 ++
+ 7 files changed, 2643 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/media/platform/bcm2835/Kconfig
+ create mode 100644 drivers/media/platform/bcm2835/Makefile
+ create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c
+ create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3206,7 +3206,7 @@ F: Documentation/devicetree/bindings/med
+ F: drivers/staging/media/rpivid
+
+ BROADCOM BCM2835 CAMERA DRIVER
+-M: Dave Stevenson <dave.stevenson@raspberrypi.org>
++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
+ L: linux-media@vger.kernel.org
+ S: Maintained
+ F: drivers/media/platform/bcm2835/
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -146,6 +146,7 @@ source "drivers/media/platform/am437x/Kc
+ source "drivers/media/platform/xilinx/Kconfig"
+ source "drivers/media/platform/rcar-vin/Kconfig"
+ source "drivers/media/platform/atmel/Kconfig"
++source "drivers/media/platform/bcm2835/Kconfig"
+ source "drivers/media/platform/sunxi/Kconfig"
+
+ config VIDEO_TI_CAL
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -100,4 +100,6 @@ obj-y += meson/
+
+ obj-y += cros-ec-cec/
+
++obj-y += bcm2835/
++
+ obj-y += sunxi/
+--- /dev/null
++++ b/drivers/media/platform/bcm2835/Kconfig
+@@ -0,0 +1,14 @@
++# Broadcom VideoCore4 V4L2 camera support
++
++config VIDEO_BCM2835_UNICAM
++ tristate "Broadcom BCM2835 Unicam video capture driver"
++ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
++ depends on ARCH_BCM2835 || COMPILE_TEST
++ select VIDEOBUF2_DMA_CONTIG
++ select V4L2_FWNODE
++ help
++ Say Y here to enable V4L2 subdevice for CSI2 receiver.
++ This is a V4L2 subdevice that interfaces directly to the VC4 peripheral.
++
++ To compile this driver as a module, choose M here. The module
++ will be called bcm2835-unicam.
+--- /dev/null
++++ b/drivers/media/platform/bcm2835/Makefile
+@@ -0,0 +1,3 @@
++# Makefile for BCM2835 Unicam driver
++
++obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
+--- /dev/null
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -0,0 +1,2369 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * BCM2835 Unicam Capture Driver
++ *
++ * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd.
++ *
++ * Dave Stevenson <dave.stevenson@raspberrypi.com>
++ *
++ * Based on TI am437x driver by
++ * Benoit Parrot <bparrot@ti.com>
++ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
++ *
++ * and TI CAL camera interface driver by
++ * Benoit Parrot <bparrot@ti.com>
++ *
++ *
++ * There are two camera drivers in the kernel for BCM283x - this one
++ * and bcm2835-camera (currently in staging).
++ *
++ * This driver directly controls the Unicam peripheral - there is no
++ * involvement with the VideoCore firmware. Unicam receives CSI-2 or
++ * CCP2 data and writes it into SDRAM.
++ * The only potential processing options are to repack Bayer data into an
++ * alternate format, and applying windowing.
++ * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P
++ * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12,
++ * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both
++ * formats where the relevant formats are defined, and will automatically
++ * configure the repacking as required.
++ * Support for windowing may be added later.
++ *
++ * It should be possible to connect this driver to any sensor with a
++ * suitable output interface and V4L2 subdevice driver.
++ *
++ * bcm2835-camera uses the VideoCore firmware to control the sensor,
++ * Unicam, ISP, and all tuner control loops. Fully processed frames are
++ * delivered to the driver by the firmware. It only has sensor drivers
++ * for Omnivision OV5647, and Sony IMX219 sensors.
++ *
++ * The two drivers are mutually exclusive for the same Unicam instance.
++ * The VideoCore firmware checks the device tree configuration during boot.
++ * If it finds device tree nodes called csi0 or csi1 it will block the
++ * firmware from accessing the peripheral, and bcm2835-camera will
++ * not be able to stream data.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of_graph.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-dv-timings.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-fwnode.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "vc4-regs-unicam.h"
++
++#define UNICAM_MODULE_NAME "unicam"
++#define UNICAM_VERSION "0.1.0"
++
++static int debug;
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "Debug level 0-3");
++
++#define unicam_dbg(level, dev, fmt, arg...) \
++ v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
++#define unicam_info(dev, fmt, arg...) \
++ v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
++#define unicam_err(dev, fmt, arg...) \
++ v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
++
++/* To protect against a dodgy sensor driver never returning an error from
++ * enum_mbus_code, set a maximum index value to be used.
++ */
++#define MAX_ENUM_MBUS_CODE 128
++
++/*
++ * Stride is a 16 bit register, but also has to be a multiple of 32.
++ */
++#define BPL_ALIGNMENT 32
++#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT)
++/*
++ * Max width is therefore determined by the max stride divided by
++ * the number of bits per pixel. Take 32bpp as a
++ * worst case.
++ * No imposed limit on the height, so adopt a square image for want
++ * of anything better.
++ */
++#define MAX_WIDTH (MAX_BYTESPERLINE / 4)
++#define MAX_HEIGHT MAX_WIDTH
++/* Define a nominal minimum image size */
++#define MIN_WIDTH 16
++#define MIN_HEIGHT 16
++
++/*
++ * struct unicam_fmt - Unicam media bus format information
++ * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a.
++ * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded
++ * out to 16bpp. 0 if n/a.
++ * @code: V4L2 media bus format code.
++ * @depth: Bits per pixel as delivered from the source.
++ * @csi_dt: CSI data type.
++ * @check_variants: Flag to denote that there are multiple mediabus formats
++ * still in the list that could match this V4L2 format.
++ */
++struct unicam_fmt {
++ u32 fourcc;
++ u32 repacked_fourcc;
++ u32 code;
++ u8 depth;
++ u8 csi_dt;
++ u8 check_variants;
++};
++
++static const struct unicam_fmt formats[] = {
++ /* YUV Formats */
++ {
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .code = MEDIA_BUS_FMT_YUYV8_2X8,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ .check_variants = 1,
++ }, {
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .code = MEDIA_BUS_FMT_UYVY8_2X8,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ .check_variants = 1,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YVYU,
++ .code = MEDIA_BUS_FMT_YVYU8_2X8,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ .check_variants = 1,
++ }, {
++ .fourcc = V4L2_PIX_FMT_VYUY,
++ .code = MEDIA_BUS_FMT_VYUY8_2X8,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ .check_variants = 1,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .code = MEDIA_BUS_FMT_YUYV8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ }, {
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .code = MEDIA_BUS_FMT_UYVY8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YVYU,
++ .code = MEDIA_BUS_FMT_YVYU8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ }, {
++ .fourcc = V4L2_PIX_FMT_VYUY,
++ .code = MEDIA_BUS_FMT_VYUY8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ }, {
++ /* RGB Formats */
++ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .depth = 16,
++ .csi_dt = 0x22,
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
++ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
++ .depth = 16,
++ .csi_dt = 0x22
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
++ .depth = 16,
++ .csi_dt = 0x21,
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
++ .depth = 16,
++ .csi_dt = 0x21,
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
++ .code = MEDIA_BUS_FMT_RGB888_1X24,
++ .depth = 24,
++ .csi_dt = 0x24,
++ }, {
++ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
++ .code = MEDIA_BUS_FMT_BGR888_1X24,
++ .depth = 24,
++ .csi_dt = 0x24,
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
++ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
++ .depth = 32,
++ .csi_dt = 0x0,
++ }, {
++ /* Bayer Formats */
++ .fourcc = V4L2_PIX_FMT_SBGGR8,
++ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG8,
++ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG8,
++ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SRGGB8,
++ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR10P,
++ .repacked_fourcc = V4L2_PIX_FMT_SBGGR10,
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG10P,
++ .repacked_fourcc = V4L2_PIX_FMT_SGBRG10,
++ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG10P,
++ .repacked_fourcc = V4L2_PIX_FMT_SGRBG10,
++ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SRGGB10P,
++ .repacked_fourcc = V4L2_PIX_FMT_SRGGB10,
++ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR12P,
++ .repacked_fourcc = V4L2_PIX_FMT_SBGGR12,
++ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG12P,
++ .repacked_fourcc = V4L2_PIX_FMT_SGBRG12,
++ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG12P,
++ .repacked_fourcc = V4L2_PIX_FMT_SGRBG12,
++ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SRGGB12P,
++ .repacked_fourcc = V4L2_PIX_FMT_SRGGB12,
++ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR14P,
++ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG14P,
++ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG14P,
++ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SRGGB14P,
++ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ }, {
++ /*
++ * 16 bit Bayer formats could be supported, but there is no CSI2
++ * data_type defined for raw 16, and no sensors that produce it at
++ * present.
++ */
++
++ /* Greyscale formats */
++ .fourcc = V4L2_PIX_FMT_GREY,
++ .code = MEDIA_BUS_FMT_Y8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ }, {
++ .fourcc = V4L2_PIX_FMT_Y10P,
++ .repacked_fourcc = V4L2_PIX_FMT_Y10,
++ .code = MEDIA_BUS_FMT_Y10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ }, {
++ /* NB There is no packed V4L2 fourcc for this format. */
++ .repacked_fourcc = V4L2_PIX_FMT_Y12,
++ .code = MEDIA_BUS_FMT_Y12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ },
++};
++
++struct unicam_dmaqueue {
++ struct list_head active;
++};
++
++struct unicam_buffer {
++ struct vb2_v4l2_buffer vb;
++ struct list_head list;
++};
++
++struct unicam_cfg {
++ /* peripheral base address */
++ void __iomem *base;
++ /* clock gating base address */
++ void __iomem *clk_gate_base;
++};
++
++#define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats))
++
++struct unicam_device {
++ /* V4l2 specific parameters */
++ /* Identifies video device for this channel */
++ struct video_device video_dev;
++ struct v4l2_ctrl_handler ctrl_handler;
++
++ struct v4l2_fwnode_endpoint endpoint;
++
++ struct v4l2_async_subdev asd;
++
++ /* unicam cfg */
++ struct unicam_cfg cfg;
++ /* clock handle */
++ struct clk *clock;
++ /* V4l2 device */
++ struct v4l2_device v4l2_dev;
++ struct media_device mdev;
++ struct media_pad pad;
++
++ /* parent device */
++ struct platform_device *pdev;
++ /* subdevice async Notifier */
++ struct v4l2_async_notifier notifier;
++ unsigned int sequence;
++
++ /* ptr to sub device */
++ struct v4l2_subdev *sensor;
++ /* Pad config for the sensor */
++ struct v4l2_subdev_pad_config *sensor_config;
++ /* current input at the sub device */
++ int current_input;
++
++ /* Pointer pointing to current v4l2_buffer */
++ struct unicam_buffer *cur_frm;
++ /* Pointer pointing to next v4l2_buffer */
++ struct unicam_buffer *next_frm;
++
++ /* video capture */
++ const struct unicam_fmt *fmt;
++ /* Used to store current pixel format */
++ struct v4l2_format v_fmt;
++ /* Used to store current mbus frame format */
++ struct v4l2_mbus_framefmt m_fmt;
++
++ unsigned int virtual_channel;
++ enum v4l2_mbus_type bus_type;
++ /*
++ * Stores bus.mipi_csi2.flags for CSI2 sensors, or
++ * bus.mipi_csi1.strobe for CCP2.
++ */
++ unsigned int bus_flags;
++ unsigned int max_data_lanes;
++ unsigned int active_data_lanes;
++
++ struct v4l2_rect crop;
++
++ /* Currently selected input on subdev */
++ int input;
++
++ /* Buffer queue used in video-buf */
++ struct vb2_queue buffer_queue;
++ /* Queue of filled frames */
++ struct unicam_dmaqueue dma_queue;
++ /* IRQ lock for DMA queue */
++ spinlock_t dma_queue_lock;
++ /* lock used to access this structure */
++ struct mutex lock;
++ /* Flag to denote that we are processing buffers */
++ int streaming;
++};
++
++/* Hardware access */
++#define clk_write(dev, val) writel((val) | 0x5a000000, (dev)->clk_gate_base)
++#define clk_read(dev) readl((dev)->clk_gate_base)
++
++#define reg_read(dev, offset) readl((dev)->base + (offset))
++#define reg_write(dev, offset, val) writel(val, (dev)->base + (offset))
++
++#define reg_read_field(dev, offset, mask) get_field(reg_read((dev), (offset), \
++ mask))
++
++static inline int get_field(u32 value, u32 mask)
++{
++ return (value & mask) >> __ffs(mask);
++}
++
++static inline void set_field(u32 *valp, u32 field, u32 mask)
++{
++ u32 val = *valp;
++
++ val &= ~mask;
++ val |= (field << __ffs(mask)) & mask;
++ *valp = val;
++}
++
++static inline void reg_write_field(struct unicam_cfg *dev, u32 offset,
++ u32 field, u32 mask)
++{
++ u32 val = reg_read((dev), (offset));
++
++ set_field(&val, field, mask);
++ reg_write((dev), (offset), val);
++}
++
++/* Power management functions */
++static inline int unicam_runtime_get(struct unicam_device *dev)
++{
++ return pm_runtime_get_sync(&dev->pdev->dev);
++}
++
++static inline void unicam_runtime_put(struct unicam_device *dev)
++{
++ pm_runtime_put_sync(&dev->pdev->dev);
++}
++
++/* Format setup functions */
++static const struct unicam_fmt *find_format_by_code(u32 code)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(formats); i++) {
++ if (formats[i].code == code)
++ return &formats[i];
++ }
++
++ return NULL;
++}
++
++static int check_mbus_format(struct unicam_device *dev,
++ const struct unicam_fmt *format)
++{
++ struct v4l2_subdev_mbus_code_enum mbus_code;
++ int ret = 0;
++ int i;
++
++ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
++ memset(&mbus_code, 0, sizeof(mbus_code));
++ mbus_code.index = i;
++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
++ NULL, &mbus_code);
++
++ if (!ret && mbus_code.code == format->code)
++ return 1;
++ }
++
++ return 0;
++}
++
++static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
++ u32 pixelformat)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(formats); i++) {
++ if (formats[i].fourcc == pixelformat ||
++ formats[i].repacked_fourcc == pixelformat) {
++ if (formats[i].check_variants &&
++ !check_mbus_format(dev, &formats[i]))
++ continue;
++ return &formats[i];
++ }
++ }
++
++ return NULL;
++}
++
++static inline unsigned int bytes_per_line(u32 width,
++ const struct unicam_fmt *fmt,
++ u32 v4l2_fourcc)
++{
++ if (v4l2_fourcc == fmt->repacked_fourcc)
++ /* Repacking always goes to 16bpp */
++ return ALIGN(width << 1, BPL_ALIGNMENT);
++ else
++ return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT);
++}
++
++static int __subdev_get_format(struct unicam_device *dev,
++ struct v4l2_mbus_framefmt *fmt)
++{
++ struct v4l2_subdev_format sd_fmt = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ };
++ int ret;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config,
++ &sd_fmt);
++ if (ret < 0)
++ return ret;
++
++ *fmt = sd_fmt.format;
++
++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__,
++ fmt->width, fmt->height, fmt->code);
++
++ return 0;
++}
++
++static int __subdev_set_format(struct unicam_device *dev,
++ struct v4l2_mbus_framefmt *fmt)
++{
++ struct v4l2_subdev_format sd_fmt = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ };
++ int ret;
++
++ sd_fmt.format = *fmt;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
++ &sd_fmt);
++ if (ret < 0)
++ return ret;
++
++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__,
++ fmt->width, fmt->height, fmt->code);
++
++ return 0;
++}
++
++static int unicam_calc_format_size_bpl(struct unicam_device *dev,
++ const struct unicam_fmt *fmt,
++ struct v4l2_format *f)
++{
++ unsigned int min_bytesperline;
++
++ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
++ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0,
++ 0);
++
++ min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt,
++ f->fmt.pix.pixelformat);
++
++ if (f->fmt.pix.bytesperline > min_bytesperline &&
++ f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
++ f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
++ BPL_ALIGNMENT);
++ else
++ f->fmt.pix.bytesperline = min_bytesperline;
++
++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
++
++ unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n",
++ __func__,
++ f->fmt.pix.pixelformat,
++ f->fmt.pix.width, f->fmt.pix.height,
++ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
++
++ return 0;
++}
++
++static int unicam_reset_format(struct unicam_device *dev)
++{
++ struct v4l2_mbus_framefmt mbus_fmt;
++ int ret;
++
++ ret = __subdev_get_format(dev, &mbus_fmt);
++ if (ret) {
++ unicam_err(dev, "Failed to get_format - ret %d\n", ret);
++ return ret;
++ }
++
++ if (mbus_fmt.code != dev->fmt->code) {
++ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n",
++ dev->fmt->code, mbus_fmt.code);
++ return ret;
++ }
++
++ v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
++ dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++
++ unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt);
++
++ dev->m_fmt = mbus_fmt;
++
++ return 0;
++}
++
++static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr)
++{
++ /*
++ * dmaaddr should be a 32-bit address with the top two bits set to 0x3
++ * to signify uncached access through the Videocore memory controller.
++ */
++ BUG_ON((dmaaddr >> 30) != 0x3);
++
++ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
++ reg_write(&dev->cfg, UNICAM_IBEA0,
++ dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
++}
++
++static inline unsigned int unicam_get_lines_done(struct unicam_device *dev)
++{
++ dma_addr_t start_addr, cur_addr;
++ unsigned int stride = dev->v_fmt.fmt.pix.bytesperline;
++ struct unicam_buffer *frm = dev->cur_frm;
++
++ if (!frm)
++ return 0;
++
++ start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
++ cur_addr = reg_read(&dev->cfg, UNICAM_IBWP);
++ return (unsigned int)(cur_addr - start_addr) / stride;
++}
++
++static inline void unicam_schedule_next_buffer(struct unicam_device *dev)
++{
++ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_buffer *buf;
++ dma_addr_t addr;
++
++ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
++ dev->next_frm = buf;
++ list_del(&buf->list);
++
++ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ unicam_wr_dma_addr(dev, addr);
++}
++
++static inline void unicam_process_buffer_complete(struct unicam_device *dev)
++{
++ dev->cur_frm->vb.field = dev->m_fmt.field;
++ dev->cur_frm->vb.sequence = dev->sequence++;
++
++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++ dev->cur_frm = dev->next_frm;
++}
++
++/*
++ * unicam_isr : ISR handler for unicam capture
++ * @irq: irq number
++ * @dev_id: dev_id ptr
++ *
++ * It changes status of the captured buffer, takes next buffer from the queue
++ * and sets its address in unicam registers
++ */
++static irqreturn_t unicam_isr(int irq, void *dev)
++{
++ struct unicam_device *unicam = (struct unicam_device *)dev;
++ struct unicam_cfg *cfg = &unicam->cfg;
++ struct unicam_dmaqueue *dma_q = &unicam->dma_queue;
++ unsigned int lines_done = unicam_get_lines_done(dev);
++ unsigned int sequence = unicam->sequence;
++ int ista, sta;
++
++ /*
++ * Don't service interrupts if not streaming.
++ * Avoids issues if the VPU should enable the
++ * peripheral without the kernel knowing (that
++ * shouldn't happen, but causes issues if it does).
++ */
++ if (!unicam->streaming)
++ return IRQ_HANDLED;
++
++ sta = reg_read(cfg, UNICAM_STA);
++ /* Write value back to clear the interrupts */
++ reg_write(cfg, UNICAM_STA, sta);
++
++ ista = reg_read(cfg, UNICAM_ISTA);
++ /* Write value back to clear the interrupts */
++ reg_write(cfg, UNICAM_ISTA, ista);
++
++ unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d",
++ ista, sta, sequence, lines_done);
++
++ if (!(sta && (UNICAM_IS | UNICAM_PI0)))
++ return IRQ_HANDLED;
++
++ if (ista & UNICAM_FSI) {
++ /*
++ * Timestamp is to be when the first data byte was captured,
++ * aka frame start.
++ */
++ if (unicam->cur_frm)
++ unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
++ }
++ if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
++ /*
++ * Ensure we have swapped buffers already as we can't
++ * stop the peripheral. Overwrite the frame we've just
++ * captured instead.
++ */
++ if (unicam->cur_frm && unicam->cur_frm != unicam->next_frm)
++ unicam_process_buffer_complete(unicam);
++ }
++
++ /* Cannot swap buffer at frame end, there may be a race condition
++ * where the HW does not actually swap it if the new frame has
++ * already started.
++ */
++ if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) {
++ spin_lock(&unicam->dma_queue_lock);
++ if (!list_empty(&dma_q->active) &&
++ unicam->cur_frm == unicam->next_frm)
++ unicam_schedule_next_buffer(unicam);
++ spin_unlock(&unicam->dma_queue_lock);
++ }
++
++ if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
++ /* Switch out of trigger mode if selected */
++ reg_write_field(&unicam->cfg, UNICAM_ICTL, 1, UNICAM_TFC);
++ reg_write_field(&unicam->cfg, UNICAM_ICTL, 0, UNICAM_FCM);
++ }
++ return IRQ_HANDLED;
++}
++
++static int unicam_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
++ strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
++
++ snprintf(cap->bus_info, sizeof(cap->bus_info),
++ "platform:%s", dev->v4l2_dev.name);
++
++ return 0;
++}
++
++static int unicam_enum_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct v4l2_subdev_mbus_code_enum mbus_code;
++ const struct unicam_fmt *fmt = NULL;
++ int index = 0;
++ int ret = 0;
++ int i;
++
++ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
++ memset(&mbus_code, 0, sizeof(mbus_code));
++ mbus_code.index = i;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
++ NULL, &mbus_code);
++ if (ret < 0) {
++ unicam_dbg(2, dev,
++ "subdev->enum_mbus_code idx %d returned %d - index invalid\n",
++ i, ret);
++ return -EINVAL;
++ }
++
++ fmt = find_format_by_code(mbus_code.code);
++ if (fmt) {
++ if (fmt->fourcc) {
++ if (index == f->index) {
++ f->pixelformat = fmt->fourcc;
++ break;
++ }
++ index++;
++ }
++ if (fmt->repacked_fourcc) {
++ if (index == f->index) {
++ f->pixelformat = fmt->repacked_fourcc;
++ break;
++ }
++ index++;
++ }
++ }
++ }
++
++ return 0;
++}
++
++static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ *f = dev->v_fmt;
++
++ return 0;
++}
++
++static
++const struct unicam_fmt *get_first_supported_format(struct unicam_device *dev)
++{
++ struct v4l2_subdev_mbus_code_enum mbus_code;
++ const struct unicam_fmt *fmt = NULL;
++ int ret;
++ int j;
++
++ for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
++ memset(&mbus_code, 0, sizeof(mbus_code));
++ mbus_code.index = j;
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
++ &mbus_code);
++ if (ret < 0) {
++ unicam_dbg(2, dev,
++ "subdev->enum_mbus_code idx %d returned %d - continue\n",
++ j, ret);
++ continue;
++ }
++
++ unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %d\n",
++ dev->sensor->name, mbus_code.code, j);
++
++ fmt = find_format_by_code(mbus_code.code);
++ unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n",
++ mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
++ fmt ? fmt->csi_dt : 0);
++ if (fmt)
++ return fmt;
++ }
++
++ return NULL;
++}
++
++static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct v4l2_subdev_format sd_fmt = {
++ .which = V4L2_SUBDEV_FORMAT_TRY,
++ };
++ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
++ const struct unicam_fmt *fmt;
++ int ret;
++
++ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
++ if (!fmt) {
++ /* Pixel format not supported by unicam. Choose the first
++ * supported format, and let the sensor choose something else.
++ */
++ unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n",
++ f->fmt.pix.pixelformat);
++
++ fmt = &formats[0];
++ f->fmt.pix.pixelformat = fmt->fourcc;
++ }
++
++ v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code);
++ /*
++ * No support for receiving interlaced video, so never
++ * request it from the sensor subdev.
++ */
++ mbus_fmt->field = V4L2_FIELD_NONE;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config,
++ &sd_fmt);
++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
++ return ret;
++
++ if (mbus_fmt->field != V4L2_FIELD_NONE)
++ unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
++
++ v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
++ if (mbus_fmt->code != fmt->code) {
++ /* Sensor has returned an alternate format */
++ fmt = find_format_by_code(mbus_fmt->code);
++ if (!fmt) {
++ /* The alternate format is one unicam can't support.
++ * Find the first format that is supported by both, and
++ * then set that.
++ */
++ fmt = get_first_supported_format(dev);
++ mbus_fmt->code = fmt->code;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
++ dev->sensor_config, &sd_fmt);
++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
++ return ret;
++
++ if (mbus_fmt->field != V4L2_FIELD_NONE)
++ unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
++
++ v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
++
++ if (mbus_fmt->code != fmt->code) {
++ /* We've set a format that the sensor reports
++ * as being supported, but it refuses to set it.
++ * Not much else we can do.
++ * Assume that the sensor driver may accept the
++ * format when it is set (rather than tried).
++ */
++ unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n");
++ }
++ }
++
++ if (fmt->fourcc)
++ f->fmt.pix.pixelformat = fmt->fourcc;
++ else
++ f->fmt.pix.pixelformat = fmt->repacked_fourcc;
++ }
++
++ return unicam_calc_format_size_bpl(dev, fmt, f);
++}
++
++static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct vb2_queue *q = &dev->buffer_queue;
++ struct v4l2_mbus_framefmt mbus_fmt = {0};
++ const struct unicam_fmt *fmt;
++ int ret;
++
++ if (vb2_is_busy(q))
++ return -EBUSY;
++
++ ret = unicam_try_fmt_vid_cap(file, priv, f);
++ if (ret < 0)
++ return ret;
++
++ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
++ if (!fmt) {
++ /* Unknown pixel format - adopt a default.
++ * This shouldn't happen as try_fmt should have resolved any
++ * issues first.
++ */
++ fmt = get_first_supported_format(dev);
++ if (!fmt)
++ /* It shouldn't be possible to get here with no
++ * supported formats
++ */
++ return -EINVAL;
++ f->fmt.pix.pixelformat = fmt->fourcc;
++ return -EINVAL;
++ }
++
++ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
++
++ ret = __subdev_set_format(dev, &mbus_fmt);
++ if (ret) {
++ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n",
++ __func__, ret);
++ return ret;
++ }
++
++ /* Just double check nothing has gone wrong */
++ if (mbus_fmt.code != fmt->code) {
++ unicam_dbg(3, dev,
++ "%s subdev changed format on us, this should not happen\n",
++ __func__);
++ return -EINVAL;
++ }
++
++ dev->fmt = fmt;
++ dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
++ dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
++ unicam_reset_format(dev);
++
++ unicam_dbg(3, dev, "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n",
++ __func__, dev->v_fmt.fmt.pix.width,
++ dev->v_fmt.fmt.pix.height, mbus_fmt.code,
++ dev->v_fmt.fmt.pix.pixelformat);
++
++ *f = dev->v_fmt;
++
++ return 0;
++}
++
++static int unicam_queue_setup(struct vb2_queue *vq,
++ unsigned int *nbuffers,
++ unsigned int *nplanes,
++ unsigned int sizes[],
++ struct device *alloc_devs[])
++{
++ struct unicam_device *dev = vb2_get_drv_priv(vq);
++ unsigned int size = dev->v_fmt.fmt.pix.sizeimage;
++
++ if (vq->num_buffers + *nbuffers < 3)
++ *nbuffers = 3 - vq->num_buffers;
++
++ if (*nplanes) {
++ if (sizes[0] < size) {
++ unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0],
++ size);
++ return -EINVAL;
++ }
++ size = sizes[0];
++ }
++
++ *nplanes = 1;
++ sizes[0] = size;
++
++ return 0;
++}
++
++static int unicam_buffer_prepare(struct vb2_buffer *vb)
++{
++ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
++ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
++ vb.vb2_buf);
++ unsigned long size;
++
++ if (WARN_ON(!dev->fmt))
++ return -EINVAL;
++
++ size = dev->v_fmt.fmt.pix.sizeimage;
++ if (vb2_plane_size(vb, 0) < size) {
++ unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
++ vb2_plane_size(vb, 0), size);
++ return -EINVAL;
++ }
++
++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
++ return 0;
++}
++
++static void unicam_buffer_queue(struct vb2_buffer *vb)
++{
++ struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
++ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
++ vb.vb2_buf);
++ struct unicam_dmaqueue *dma_queue = &dev->dma_queue;
++ unsigned long flags = 0;
++
++ spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ list_add_tail(&buf->list, &dma_queue->active);
++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++}
++
++static void unicam_set_packing_config(struct unicam_device *dev)
++{
++ int pack, unpack;
++ u32 val;
++
++ if (dev->v_fmt.fmt.pix.pixelformat == dev->fmt->fourcc) {
++ unpack = UNICAM_PUM_NONE;
++ pack = UNICAM_PPM_NONE;
++ } else {
++ switch (dev->fmt->depth) {
++ case 8:
++ unpack = UNICAM_PUM_UNPACK8;
++ break;
++ case 10:
++ unpack = UNICAM_PUM_UNPACK10;
++ break;
++ case 12:
++ unpack = UNICAM_PUM_UNPACK12;
++ break;
++ case 14:
++ unpack = UNICAM_PUM_UNPACK14;
++ break;
++ case 16:
++ unpack = UNICAM_PUM_UNPACK16;
++ break;
++ default:
++ unpack = UNICAM_PUM_NONE;
++ break;
++ }
++
++ /* Repacking is always to 16bpp */
++ pack = UNICAM_PPM_PACK16;
++ }
++
++ val = 0;
++ set_field(&val, unpack, UNICAM_PUM_MASK);
++ set_field(&val, pack, UNICAM_PPM_MASK);
++ reg_write(&dev->cfg, UNICAM_IPIPE, val);
++}
++
++static void unicam_cfg_image_id(struct unicam_device *dev)
++{
++ struct unicam_cfg *cfg = &dev->cfg;
++
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
++ /* CSI2 mode */
++ reg_write(cfg, UNICAM_IDI0,
++ (dev->virtual_channel << 6) | dev->fmt->csi_dt);
++ } else {
++ /* CCP2 mode */
++ reg_write(cfg, UNICAM_IDI0, (0x80 | dev->fmt->csi_dt));
++ }
++}
++
++static void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
++{
++ struct unicam_cfg *cfg = &dev->cfg;
++ int line_int_freq = dev->v_fmt.fmt.pix.height >> 2;
++ unsigned int i;
++ u32 val;
++
++ if (line_int_freq < 128)
++ line_int_freq = 128;
++
++ /* Enable lane clocks */
++ val = 1;
++ for (i = 0; i < dev->active_data_lanes; i++)
++ val = val << 2 | 1;
++ clk_write(cfg, val);
++
++ /* Basic init */
++ reg_write(cfg, UNICAM_CTRL, UNICAM_MEM);
++
++ /* Enable analogue control, and leave in reset. */
++ val = UNICAM_AR;
++ set_field(&val, 7, UNICAM_CTATADJ_MASK);
++ set_field(&val, 7, UNICAM_PTATADJ_MASK);
++ reg_write(cfg, UNICAM_ANA, val);
++ usleep_range(1000, 2000);
++
++ /* Come out of reset */
++ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_AR);
++
++ /* Peripheral reset */
++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
++
++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
++
++ /* Enable Rx control. */
++ val = reg_read(cfg, UNICAM_CTRL);
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
++ set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
++ set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
++ } else {
++ set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
++ set_field(&val, dev->bus_flags, UNICAM_DCM_MASK);
++ }
++ /* Packet framer timeout */
++ set_field(&val, 0xf, UNICAM_PFT_MASK);
++ set_field(&val, 128, UNICAM_OET_MASK);
++ reg_write(cfg, UNICAM_CTRL, val);
++
++ reg_write(cfg, UNICAM_IHWIN, 0);
++ reg_write(cfg, UNICAM_IVWIN, 0);
++
++ /* AXI bus access QoS setup */
++ val = reg_read(&dev->cfg, UNICAM_PRI);
++ set_field(&val, 0, UNICAM_BL_MASK);
++ set_field(&val, 0, UNICAM_BS_MASK);
++ set_field(&val, 0xe, UNICAM_PP_MASK);
++ set_field(&val, 8, UNICAM_NP_MASK);
++ set_field(&val, 2, UNICAM_PT_MASK);
++ set_field(&val, 1, UNICAM_PE);
++ reg_write(cfg, UNICAM_PRI, val);
++
++ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL);
++
++ /* Always start in trigger frame capture mode (UNICAM_FCM set) */
++ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM;
++ set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
++ reg_write(cfg, UNICAM_ICTL, val);
++ reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL);
++ reg_write(cfg, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
++
++ /* tclk_term_en */
++ reg_write_field(cfg, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
++ /* tclk_settle */
++ reg_write_field(cfg, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
++ /* td_term_en */
++ reg_write_field(cfg, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
++ /* ths_settle */
++ reg_write_field(cfg, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
++ /* trx_enable */
++ reg_write_field(cfg, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
++
++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_SOE);
++
++ /* Packet compare setup - required to avoid missing frame ends */
++ val = 0;
++ set_field(&val, 1, UNICAM_PCE);
++ set_field(&val, 1, UNICAM_GI);
++ set_field(&val, 1, UNICAM_CPH);
++ set_field(&val, 0, UNICAM_PCVC_MASK);
++ set_field(&val, 1, UNICAM_PCDT_MASK);
++ reg_write(cfg, UNICAM_CMP0, val);
++
++ /* Enable clock lane and set up terminations */
++ val = 0;
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
++ /* CSI2 */
++ set_field(&val, 1, UNICAM_CLE);
++ set_field(&val, 1, UNICAM_CLLPE);
++ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
++ set_field(&val, 1, UNICAM_CLTRE);
++ set_field(&val, 1, UNICAM_CLHSE);
++ }
++ } else {
++ /* CCP2 */
++ set_field(&val, 1, UNICAM_CLE);
++ set_field(&val, 1, UNICAM_CLHSE);
++ set_field(&val, 1, UNICAM_CLTRE);
++ }
++ reg_write(cfg, UNICAM_CLK, val);
++
++ /*
++ * Enable required data lanes with appropriate terminations.
++ * The same value needs to be written to UNICAM_DATn registers for
++ * the active lanes, and 0 for inactive ones.
++ */
++ val = 0;
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
++ /* CSI2 */
++ set_field(&val, 1, UNICAM_DLE);
++ set_field(&val, 1, UNICAM_DLLPE);
++ if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) {
++ set_field(&val, 1, UNICAM_DLTRE);
++ set_field(&val, 1, UNICAM_DLHSE);
++ }
++ } else {
++ /* CCP2 */
++ set_field(&val, 1, UNICAM_DLE);
++ set_field(&val, 1, UNICAM_DLHSE);
++ set_field(&val, 1, UNICAM_DLTRE);
++ }
++ reg_write(cfg, UNICAM_DAT0, val);
++
++ if (dev->active_data_lanes == 1)
++ val = 0;
++ reg_write(cfg, UNICAM_DAT1, val);
++
++ if (dev->max_data_lanes > 2) {
++ /*
++ * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the
++ * instance supports more than 2 data lanes.
++ */
++ if (dev->active_data_lanes == 2)
++ val = 0;
++ reg_write(cfg, UNICAM_DAT2, val);
++
++ if (dev->active_data_lanes == 3)
++ val = 0;
++ reg_write(cfg, UNICAM_DAT3, val);
++ }
++
++ reg_write(&dev->cfg, UNICAM_IBLS, dev->v_fmt.fmt.pix.bytesperline);
++ unicam_wr_dma_addr(dev, addr);
++ unicam_set_packing_config(dev);
++ unicam_cfg_image_id(dev);
++
++ /* Disabled embedded data */
++ val = 0;
++ set_field(&val, 0, UNICAM_EDL_MASK);
++ reg_write(cfg, UNICAM_DCS, val);
++
++ val = reg_read(cfg, UNICAM_MISC);
++ set_field(&val, 1, UNICAM_FL0);
++ set_field(&val, 1, UNICAM_FL1);
++ reg_write(cfg, UNICAM_MISC, val);
++
++ /* Enable peripheral */
++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE);
++
++ /* Load image pointers */
++ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
++
++ /*
++ * Enable trigger only for the first frame to
++ * sync correctly to the FS from the source.
++ */
++ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_TFC);
++}
++
++static void unicam_disable(struct unicam_device *dev)
++{
++ struct unicam_cfg *cfg = &dev->cfg;
++
++ /* Analogue lane control disable */
++ reg_write_field(cfg, UNICAM_ANA, 1, UNICAM_DDL);
++
++ /* Stop the output engine */
++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_SOE);
++
++ /* Disable the data lanes. */
++ reg_write(cfg, UNICAM_DAT0, 0);
++ reg_write(cfg, UNICAM_DAT1, 0);
++
++ if (dev->max_data_lanes > 2) {
++ reg_write(cfg, UNICAM_DAT2, 0);
++ reg_write(cfg, UNICAM_DAT3, 0);
++ }
++
++ /* Peripheral reset */
++ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPR);
++ usleep_range(50, 100);
++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPR);
++
++ /* Disable peripheral */
++ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
++
++ /* Disable all lane clocks */
++ clk_write(cfg, 0);
++}
++
++static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
++{
++ struct unicam_device *dev = vb2_get_drv_priv(vq);
++ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_buffer *buf, *tmp;
++ unsigned long addr = 0;
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
++ dev->cur_frm = buf;
++ dev->next_frm = buf;
++ list_del(&buf->list);
++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++
++ addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0);
++ dev->sequence = 0;
++
++ ret = unicam_runtime_get(dev);
++ if (ret < 0) {
++ unicam_dbg(3, dev, "unicam_runtime_get failed\n");
++ goto err_release_buffers;
++ }
++
++ dev->active_data_lanes = dev->max_data_lanes;
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY &&
++ v4l2_subdev_has_op(dev->sensor, video, g_mbus_config)) {
++ struct v4l2_mbus_config mbus_config;
++
++ ret = v4l2_subdev_call(dev->sensor, video, g_mbus_config,
++ &mbus_config);
++ if (ret < 0) {
++ unicam_dbg(3, dev, "g_mbus_config failed\n");
++ goto err_pm_put;
++ }
++
++ dev->active_data_lanes =
++ (mbus_config.flags & V4L2_MBUS_CSI2_LANE_MASK) >>
++ __ffs(V4L2_MBUS_CSI2_LANE_MASK);
++ if (!dev->active_data_lanes)
++ dev->active_data_lanes = dev->max_data_lanes;
++ }
++ if (dev->active_data_lanes > dev->max_data_lanes) {
++ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
++ dev->active_data_lanes, dev->max_data_lanes);
++ ret = -EINVAL;
++ goto err_pm_put;
++ }
++
++ unicam_dbg(1, dev, "Running with %u data lanes\n",
++ dev->active_data_lanes);
++
++ ret = clk_set_rate(dev->clock, 100 * 1000 * 1000);
++ if (ret) {
++ unicam_err(dev, "failed to set up clock\n");
++ goto err_pm_put;
++ }
++
++ ret = clk_prepare_enable(dev->clock);
++ if (ret) {
++ unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
++ goto err_pm_put;
++ }
++ dev->streaming = 1;
++
++ unicam_start_rx(dev, addr);
++
++ ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
++ if (ret < 0) {
++ unicam_err(dev, "stream on failed in subdev\n");
++ goto err_disable_unicam;
++ }
++
++ return 0;
++
++err_disable_unicam:
++ unicam_disable(dev);
++ clk_disable_unprepare(dev->clock);
++err_pm_put:
++ unicam_runtime_put(dev);
++err_release_buffers:
++ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
++ list_del(&buf->list);
++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
++ }
++ if (dev->cur_frm != dev->next_frm)
++ vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
++ VB2_BUF_STATE_QUEUED);
++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
++ dev->next_frm = NULL;
++ dev->cur_frm = NULL;
++
++ return ret;
++}
++
++static void unicam_stop_streaming(struct vb2_queue *vq)
++{
++ struct unicam_device *dev = vb2_get_drv_priv(vq);
++ struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_buffer *buf, *tmp;
++ unsigned long flags;
++
++ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
++ unicam_err(dev, "stream off failed in subdev\n");
++
++ unicam_disable(dev);
++
++ /* Release all active buffers */
++ spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
++ list_del(&buf->list);
++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++ }
++
++ if (dev->cur_frm == dev->next_frm) {
++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++ } else {
++ vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++ vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
++ VB2_BUF_STATE_ERROR);
++ }
++ dev->cur_frm = NULL;
++ dev->next_frm = NULL;
++ spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++
++ clk_disable_unprepare(dev->clock);
++ unicam_runtime_put(dev);
++}
++
++static int unicam_enum_input(struct file *file, void *priv,
++ struct v4l2_input *inp)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ if (inp->index != 0)
++ return -EINVAL;
++
++ inp->type = V4L2_INPUT_TYPE_CAMERA;
++ if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) {
++ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
++ inp->std = 0;
++ } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) {
++ inp->capabilities = V4L2_IN_CAP_STD;
++ if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std)
++ < 0)
++ inp->std = V4L2_STD_ALL;
++ } else {
++ inp->capabilities = 0;
++ inp->std = 0;
++ }
++ sprintf(inp->name, "Camera 0");
++ return 0;
++}
++
++static int unicam_g_input(struct file *file, void *priv, unsigned int *i)
++{
++ *i = 0;
++
++ return 0;
++}
++
++static int unicam_s_input(struct file *file, void *priv, unsigned int i)
++{
++ /*
++ * FIXME: Ideally we would like to be able to query the source
++ * subdevice for information over the input connectors it supports,
++ * and map that through in to a call to video_ops->s_routing.
++ * There is no infrastructure support for defining that within
++ * devicetree at present. Until that is implemented we can't
++ * map a user physical connector number to s_routing input number.
++ */
++ if (i > 0)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int unicam_querystd(struct file *file, void *priv,
++ v4l2_std_id *std)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, video, querystd, std);
++}
++
++static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, video, g_std, std);
++}
++
++static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ int ret;
++ v4l2_std_id current_std;
++
++ ret = v4l2_subdev_call(dev->sensor, video, g_std, &current_std);
++ if (ret)
++ return ret;
++
++ if (std == current_std)
++ return 0;
++
++ if (vb2_is_busy(&dev->buffer_queue))
++ return -EBUSY;
++
++ ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
++
++ /* Force recomputation of bytesperline */
++ dev->v_fmt.fmt.pix.bytesperline = 0;
++
++ unicam_reset_format(dev);
++
++ return ret;
++}
++
++static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
++}
++
++static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
++}
++
++static int unicam_enum_framesizes(struct file *file, void *priv,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ const struct unicam_fmt *fmt;
++ struct v4l2_subdev_frame_size_enum fse;
++ int ret;
++
++ /* check for valid format */
++ fmt = find_format_by_pix(dev, fsize->pixel_format);
++ if (!fmt) {
++ unicam_dbg(3, dev, "Invalid pixel code: %x\n",
++ fsize->pixel_format);
++ return -EINVAL;
++ }
++
++ fse.index = fsize->index;
++ fse.pad = 0;
++ fse.code = fmt->code;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse);
++ if (ret)
++ return ret;
++
++ unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n",
++ __func__, fse.index, fse.code, fse.min_width, fse.max_width,
++ fse.min_height, fse.max_height);
++
++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++ fsize->discrete.width = fse.max_width;
++ fsize->discrete.height = fse.max_height;
++
++ return 0;
++}
++
++static int unicam_enum_frameintervals(struct file *file, void *priv,
++ struct v4l2_frmivalenum *fival)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ const struct unicam_fmt *fmt;
++ struct v4l2_subdev_frame_interval_enum fie = {
++ .index = fival->index,
++ .width = fival->width,
++ .height = fival->height,
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ };
++ int ret;
++
++ fmt = find_format_by_pix(dev, fival->pixel_format);
++ if (!fmt)
++ return -EINVAL;
++
++ fie.code = fmt->code;
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval,
++ NULL, &fie);
++ if (ret)
++ return ret;
++
++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
++ fival->discrete = fie.interval;
++
++ return 0;
++}
++
++static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a);
++}
++
++static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a);
++}
++
++static int unicam_g_dv_timings(struct file *file, void *priv,
++ struct v4l2_dv_timings *timings)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
++}
++
++static int unicam_s_dv_timings(struct file *file, void *priv,
++ struct v4l2_dv_timings *timings)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct v4l2_dv_timings current_timings;
++ int ret;
++
++ ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings,
++ &current_timings);
++
++ if (v4l2_match_dv_timings(timings, &current_timings, 0, false))
++ return 0;
++
++ if (vb2_is_busy(&dev->buffer_queue))
++ return -EBUSY;
++
++ ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
++
++ /* Force recomputation of bytesperline */
++ dev->v_fmt.fmt.pix.bytesperline = 0;
++
++ unicam_reset_format(dev);
++
++ return ret;
++}
++
++static int unicam_query_dv_timings(struct file *file, void *priv,
++ struct v4l2_dv_timings *timings)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
++}
++
++static int unicam_enum_dv_timings(struct file *file, void *priv,
++ struct v4l2_enum_dv_timings *timings)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
++}
++
++static int unicam_dv_timings_cap(struct file *file, void *priv,
++ struct v4l2_dv_timings_cap *cap)
++{
++ struct unicam_device *dev = video_drvdata(file);
++
++ return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
++}
++
++static int unicam_subscribe_event(struct v4l2_fh *fh,
++ const struct v4l2_event_subscription *sub)
++{
++ switch (sub->type) {
++ case V4L2_EVENT_SOURCE_CHANGE:
++ return v4l2_event_subscribe(fh, sub, 4, NULL);
++ }
++
++ return v4l2_ctrl_subscribe_event(fh, sub);
++}
++
++static int unicam_log_status(struct file *file, void *fh)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct unicam_cfg *cfg = &dev->cfg;
++ u32 reg;
++
++ /* status for sub devices */
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status);
++
++ unicam_info(dev, "-----Receiver status-----\n");
++ unicam_info(dev, "V4L2 width/height: %ux%u\n",
++ dev->v_fmt.fmt.pix.width, dev->v_fmt.fmt.pix.height);
++ unicam_info(dev, "Mediabus format: %08x\n", dev->fmt->code);
++ unicam_info(dev, "V4L2 format: %08x\n",
++ dev->v_fmt.fmt.pix.pixelformat);
++ reg = reg_read(&dev->cfg, UNICAM_IPIPE);
++ unicam_info(dev, "Unpacking/packing: %u / %u\n",
++ get_field(reg, UNICAM_PUM_MASK),
++ get_field(reg, UNICAM_PPM_MASK));
++ unicam_info(dev, "----Live data----\n");
++ unicam_info(dev, "Programmed stride: %4u\n",
++ reg_read(cfg, UNICAM_IBLS));
++ unicam_info(dev, "Detected resolution: %ux%u\n",
++ reg_read(cfg, UNICAM_IHSTA),
++ reg_read(cfg, UNICAM_IVSTA));
++ unicam_info(dev, "Write pointer: %08x\n",
++ reg_read(cfg, UNICAM_IBWP));
++
++ return 0;
++}
++
++static void unicam_notify(struct v4l2_subdev *sd,
++ unsigned int notification, void *arg)
++{
++ struct unicam_device *dev =
++ container_of(sd->v4l2_dev, struct unicam_device, v4l2_dev);
++
++ switch (notification) {
++ case V4L2_DEVICE_NOTIFY_EVENT:
++ v4l2_event_queue(&dev->video_dev, arg);
++ break;
++ default:
++ break;
++ }
++}
++
++static const struct vb2_ops unicam_video_qops = {
++ .wait_prepare = vb2_ops_wait_prepare,
++ .wait_finish = vb2_ops_wait_finish,
++ .queue_setup = unicam_queue_setup,
++ .buf_prepare = unicam_buffer_prepare,
++ .buf_queue = unicam_buffer_queue,
++ .start_streaming = unicam_start_streaming,
++ .stop_streaming = unicam_stop_streaming,
++};
++
++/*
++ * unicam_open : This function is based on the v4l2_fh_open helper function.
++ * It has been augmented to handle sensor subdevice power management,
++ */
++static int unicam_open(struct file *file)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ int ret;
++
++ mutex_lock(&dev->lock);
++
++ ret = v4l2_fh_open(file);
++ if (ret) {
++ unicam_err(dev, "v4l2_fh_open failed\n");
++ goto unlock;
++ }
++
++ if (!v4l2_fh_is_singular_file(file))
++ goto unlock;
++
++ ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
++ if (ret < 0 && ret != -ENOIOCTLCMD) {
++ v4l2_fh_release(file);
++ goto unlock;
++ }
++
++ ret = 0;
++
++unlock:
++ mutex_unlock(&dev->lock);
++ return ret;
++}
++
++static int unicam_release(struct file *file)
++{
++ struct unicam_device *dev = video_drvdata(file);
++ struct v4l2_subdev *sd = dev->sensor;
++ bool fh_singular;
++ int ret;
++
++ mutex_lock(&dev->lock);
++
++ fh_singular = v4l2_fh_is_singular_file(file);
++
++ ret = _vb2_fop_release(file, NULL);
++
++ if (fh_singular)
++ v4l2_subdev_call(sd, core, s_power, 0);
++
++ mutex_unlock(&dev->lock);
++
++ return ret;
++}
++
++/* unicam capture driver file operations */
++static const struct v4l2_file_operations unicam_fops = {
++ .owner = THIS_MODULE,
++ .open = unicam_open,
++ .release = unicam_release,
++ .read = vb2_fop_read,
++ .poll = vb2_fop_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .mmap = vb2_fop_mmap,
++};
++
++/* unicam capture ioctl operations */
++static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
++ .vidioc_querycap = unicam_querycap,
++ .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap,
++
++ .vidioc_enum_input = unicam_enum_input,
++ .vidioc_g_input = unicam_g_input,
++ .vidioc_s_input = unicam_s_input,
++
++ .vidioc_querystd = unicam_querystd,
++ .vidioc_s_std = unicam_s_std,
++ .vidioc_g_std = unicam_g_std,
++
++ .vidioc_g_edid = unicam_g_edid,
++ .vidioc_s_edid = unicam_s_edid,
++
++ .vidioc_enum_framesizes = unicam_enum_framesizes,
++ .vidioc_enum_frameintervals = unicam_enum_frameintervals,
++
++ .vidioc_g_parm = unicam_g_parm,
++ .vidioc_s_parm = unicam_s_parm,
++
++ .vidioc_s_dv_timings = unicam_s_dv_timings,
++ .vidioc_g_dv_timings = unicam_g_dv_timings,
++ .vidioc_query_dv_timings = unicam_query_dv_timings,
++ .vidioc_enum_dv_timings = unicam_enum_dv_timings,
++ .vidioc_dv_timings_cap = unicam_dv_timings_cap,
++
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++
++ .vidioc_log_status = unicam_log_status,
++ .vidioc_subscribe_event = unicam_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++};
++
++static int
++unicam_async_bound(struct v4l2_async_notifier *notifier,
++ struct v4l2_subdev *subdev,
++ struct v4l2_async_subdev *asd)
++{
++ struct unicam_device *unicam = container_of(notifier->v4l2_dev,
++ struct unicam_device, v4l2_dev);
++
++ if (unicam->sensor) {
++ unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
++ subdev->name);
++ return 0;
++ }
++
++ unicam->sensor = subdev;
++ unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
++
++ return 0;
++}
++
++static int unicam_probe_complete(struct unicam_device *unicam)
++{
++ struct video_device *vdev;
++ struct vb2_queue *q;
++ struct v4l2_mbus_framefmt mbus_fmt = {0};
++ const struct unicam_fmt *fmt;
++ int ret;
++
++ v4l2_set_subdev_hostdata(unicam->sensor, unicam);
++
++ unicam->v4l2_dev.notify = unicam_notify;
++
++ unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
++ if (!unicam->sensor_config)
++ return -ENOMEM;
++
++ ret = __subdev_get_format(unicam, &mbus_fmt);
++ if (ret) {
++ unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
++ return ret;
++ }
++
++ fmt = find_format_by_code(mbus_fmt.code);
++ if (!fmt) {
++ /* Find the first format that the sensor and unicam both
++ * support
++ */
++ fmt = get_first_supported_format(unicam);
++
++ if (!fmt)
++ /* No compatible formats */
++ return -EINVAL;
++
++ mbus_fmt.code = fmt->code;
++ ret = __subdev_set_format(unicam, &mbus_fmt);
++ if (ret)
++ return -EINVAL;
++ }
++ if (mbus_fmt.field != V4L2_FIELD_NONE) {
++ /* Interlaced not supported - disable it now. */
++ mbus_fmt.field = V4L2_FIELD_NONE;
++ ret = __subdev_set_format(unicam, &mbus_fmt);
++ if (ret)
++ return -EINVAL;
++ }
++
++ unicam->fmt = fmt;
++ if (fmt->fourcc)
++ unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
++ else
++ unicam->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
++
++ /* Read current subdev format */
++ unicam_reset_format(unicam);
++
++ if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
++ v4l2_std_id tvnorms;
++
++ if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video,
++ g_tvnorms)))
++ /*
++ * Subdevice should not advertise s_std but not
++ * g_tvnorms
++ */
++ return -EINVAL;
++
++ ret = v4l2_subdev_call(unicam->sensor, video,
++ g_tvnorms, &tvnorms);
++ if (WARN_ON(ret))
++ return -EINVAL;
++ unicam->video_dev.tvnorms |= tvnorms;
++ }
++
++ spin_lock_init(&unicam->dma_queue_lock);
++ mutex_init(&unicam->lock);
++
++ /* Add controls from the subdevice */
++ ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
++ unicam->sensor->ctrl_handler, NULL, true);
++ if (ret < 0)
++ return ret;
++
++ q = &unicam->buffer_queue;
++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
++ q->drv_priv = unicam;
++ q->ops = &unicam_video_qops;
++ q->mem_ops = &vb2_dma_contig_memops;
++ q->buf_struct_size = sizeof(struct unicam_buffer);
++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++ q->lock = &unicam->lock;
++ q->min_buffers_needed = 2;
++ q->dev = &unicam->pdev->dev;
++
++ ret = vb2_queue_init(q);
++ if (ret) {
++ unicam_err(unicam, "vb2_queue_init() failed\n");
++ return ret;
++ }
++
++ INIT_LIST_HEAD(&unicam->dma_queue.active);
++
++ vdev = &unicam->video_dev;
++ strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
++ vdev->release = video_device_release_empty;
++ vdev->fops = &unicam_fops;
++ vdev->ioctl_ops = &unicam_ioctl_ops;
++ vdev->v4l2_dev = &unicam->v4l2_dev;
++ vdev->vfl_dir = VFL_DIR_RX;
++ vdev->queue = q;
++ vdev->lock = &unicam->lock;
++ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
++ V4L2_CAP_READWRITE;
++
++ /* If the source has no controls then remove our ctrl handler. */
++ if (list_empty(&unicam->ctrl_handler.ctrls))
++ unicam->v4l2_dev.ctrl_handler = NULL;
++
++ video_set_drvdata(vdev, unicam);
++ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
++
++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD);
++ }
++ if (!v4l2_subdev_has_op(unicam->sensor, video, querystd))
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD);
++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS);
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS);
++ }
++ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval))
++ v4l2_disable_ioctl(&unicam->video_dev,
++ VIDIOC_ENUM_FRAMEINTERVALS);
++ if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval))
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_PARM);
++ if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval))
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_PARM);
++
++ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
++ v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_FRAMESIZES);
++
++ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
++ if (ret) {
++ unicam_err(unicam, "Unable to register video device.\n");
++ return ret;
++ }
++
++ ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
++ if (ret) {
++ unicam_err(unicam,
++ "Unable to register subdev nodes.\n");
++ video_unregister_device(&unicam->video_dev);
++ return ret;
++ }
++
++ ret = media_create_pad_link(&unicam->sensor->entity, 0,
++ &unicam->video_dev.entity, 0,
++ MEDIA_LNK_FL_ENABLED |
++ MEDIA_LNK_FL_IMMUTABLE);
++ if (ret) {
++ unicam_err(unicam, "Unable to create pad links.\n");
++ video_unregister_device(&unicam->video_dev);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int unicam_async_complete(struct v4l2_async_notifier *notifier)
++{
++ struct unicam_device *unicam = container_of(notifier->v4l2_dev,
++ struct unicam_device, v4l2_dev);
++
++ return unicam_probe_complete(unicam);
++}
++
++static const struct v4l2_async_notifier_operations unicam_async_ops = {
++ .bound = unicam_async_bound,
++ .complete = unicam_async_complete,
++};
++
++static int of_unicam_connect_subdevs(struct unicam_device *dev)
++{
++ struct platform_device *pdev = dev->pdev;
++ struct device_node *parent, *ep_node = NULL, *remote_ep = NULL,
++ *sensor_node = NULL;
++ struct v4l2_fwnode_endpoint *ep;
++ struct v4l2_async_subdev *asd;
++ unsigned int peripheral_data_lanes;
++ int ret = -EINVAL;
++ unsigned int lane;
++
++ parent = pdev->dev.of_node;
++
++ asd = &dev->asd;
++ ep = &dev->endpoint;
++
++ ep_node = of_graph_get_next_endpoint(parent, NULL);
++ if (!ep_node) {
++ unicam_dbg(3, dev, "can't get next endpoint\n");
++ goto cleanup_exit;
++ }
++
++ unicam_dbg(3, dev, "ep_node is %s\n", ep_node->name);
++
++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), ep);
++
++ for (lane = 0; lane < ep->bus.mipi_csi2.num_data_lanes; lane++) {
++ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
++ unicam_err(dev, "Local endpoint - data lane reordering not supported\n");
++ goto cleanup_exit;
++ }
++ }
++
++ peripheral_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
++
++ sensor_node = of_graph_get_remote_port_parent(ep_node);
++ if (!sensor_node) {
++ unicam_dbg(3, dev, "can't get remote parent\n");
++ goto cleanup_exit;
++ }
++ unicam_dbg(3, dev, "sensor_node is %s\n", sensor_node->name);
++ asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
++ asd->match.fwnode = of_fwnode_handle(sensor_node);
++
++ remote_ep = of_graph_get_remote_endpoint(ep_node);
++ if (!remote_ep) {
++ unicam_dbg(3, dev, "can't get remote-endpoint\n");
++ goto cleanup_exit;
++ }
++ unicam_dbg(3, dev, "remote_ep is %s\n", remote_ep->name);
++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), ep);
++ unicam_dbg(3, dev, "parsed remote_ep to endpoint. nr_of_link_frequencies %u, bus_type %u\n",
++ ep->nr_of_link_frequencies, ep->bus_type);
++
++ switch (ep->bus_type) {
++ case V4L2_MBUS_CSI2_DPHY:
++ if (ep->bus.mipi_csi2.num_data_lanes >
++ peripheral_data_lanes) {
++ unicam_err(dev, "Subdevice %s wants too many data lanes (%u > %u)\n",
++ sensor_node->name,
++ ep->bus.mipi_csi2.num_data_lanes,
++ peripheral_data_lanes);
++ goto cleanup_exit;
++ }
++ for (lane = 0;
++ lane < ep->bus.mipi_csi2.num_data_lanes;
++ lane++) {
++ if (ep->bus.mipi_csi2.data_lanes[lane] != lane + 1) {
++ unicam_err(dev, "Subdevice %s - incompatible data lane config\n",
++ sensor_node->name);
++ goto cleanup_exit;
++ }
++ }
++ dev->max_data_lanes = ep->bus.mipi_csi2.num_data_lanes;
++ dev->bus_flags = ep->bus.mipi_csi2.flags;
++ break;
++ case V4L2_MBUS_CCP2:
++ if (ep->bus.mipi_csi1.clock_lane != 0 ||
++ ep->bus.mipi_csi1.data_lane != 1) {
++ unicam_err(dev, "Subdevice %s incompatible lane config\n",
++ sensor_node->name);
++ goto cleanup_exit;
++ }
++ dev->max_data_lanes = 1;
++ dev->bus_flags = ep->bus.mipi_csi1.strobe;
++ break;
++ default:
++ /* Unsupported bus type */
++ unicam_err(dev, "sub-device %s is not a CSI2 or CCP2 device %d\n",
++ sensor_node->name, ep->bus_type);
++ goto cleanup_exit;
++ }
++
++ /* Store bus type - CSI2 or CCP2 */
++ dev->bus_type = ep->bus_type;
++ unicam_dbg(3, dev, "bus_type is %d\n", dev->bus_type);
++
++ /* Store Virtual Channel number */
++ dev->virtual_channel = ep->base.id;
++
++ unicam_dbg(3, dev, "v4l2-endpoint: %s\n",
++ dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI2" : "CCP2");
++ unicam_dbg(3, dev, "Virtual Channel=%d\n", dev->virtual_channel);
++ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY)
++ unicam_dbg(3, dev, "flags=0x%08x\n", ep->bus.mipi_csi2.flags);
++ unicam_dbg(3, dev, "num_data_lanes=%d\n", dev->max_data_lanes);
++
++ unicam_dbg(1, dev, "found sub-device %s\n", sensor_node->name);
++
++ v4l2_async_notifier_init(&dev->notifier);
++
++ ret = v4l2_async_notifier_add_subdev(&dev->notifier, asd);
++ if (ret) {
++ unicam_err(dev, "Error adding subdevice - ret %d\n", ret);
++ goto cleanup_exit;
++ }
++
++ dev->notifier.ops = &unicam_async_ops;
++ ret = v4l2_async_notifier_register(&dev->v4l2_dev,
++ &dev->notifier);
++ if (ret) {
++ unicam_err(dev, "Error registering async notifier - ret %d\n",
++ ret);
++ ret = -EINVAL;
++ }
++
++cleanup_exit:
++ if (remote_ep)
++ of_node_put(remote_ep);
++ if (sensor_node)
++ of_node_put(sensor_node);
++ if (ep_node)
++ of_node_put(ep_node);
++
++ return ret;
++}
++
++static int unicam_probe(struct platform_device *pdev)
++{
++ struct unicam_cfg *unicam_cfg;
++ struct unicam_device *unicam;
++ struct v4l2_ctrl_handler *hdl;
++ struct resource *res;
++ int ret;
++
++ unicam = devm_kzalloc(&pdev->dev, sizeof(*unicam), GFP_KERNEL);
++ if (!unicam)
++ return -ENOMEM;
++
++ unicam->pdev = pdev;
++ unicam_cfg = &unicam->cfg;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ unicam_cfg->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(unicam_cfg->base)) {
++ unicam_err(unicam, "Failed to get main io block\n");
++ return PTR_ERR(unicam_cfg->base);
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ unicam_cfg->clk_gate_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(unicam_cfg->clk_gate_base)) {
++ unicam_err(unicam, "Failed to get 2nd io block\n");
++ return PTR_ERR(unicam_cfg->clk_gate_base);
++ }
++
++ unicam->clock = devm_clk_get(&pdev->dev, "lp");
++ if (IS_ERR(unicam->clock)) {
++ unicam_err(unicam, "Failed to get clock\n");
++ return PTR_ERR(unicam->clock);
++ }
++
++ ret = platform_get_irq(pdev, 0);
++ if (ret <= 0) {
++ dev_err(&pdev->dev, "No IRQ resource\n");
++ return -ENODEV;
++ }
++
++ ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
++ "unicam_capture0", unicam);
++ if (ret) {
++ dev_err(&pdev->dev, "Unable to request interrupt\n");
++ return -EINVAL;
++ }
++
++ unicam->mdev.dev = &pdev->dev;
++ strscpy(unicam->mdev.model, UNICAM_MODULE_NAME,
++ sizeof(unicam->mdev.model));
++ strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial));
++ snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info),
++ "platform:%s %s",
++ pdev->dev.driver->name, dev_name(&pdev->dev));
++ unicam->mdev.hw_revision = 1;
++
++ media_entity_pads_init(&unicam->video_dev.entity, 1, &unicam->pad);
++ media_device_init(&unicam->mdev);
++
++ unicam->v4l2_dev.mdev = &unicam->mdev;
++
++ ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev);
++ if (ret) {
++ unicam_err(unicam,
++ "Unable to register v4l2 device.\n");
++ goto media_cleanup;
++ }
++
++ ret = media_device_register(&unicam->mdev);
++ if (ret < 0) {
++ unicam_err(unicam,
++ "Unable to register media-controller device.\n");
++ goto probe_out_v4l2_unregister;
++ }
++
++ /* Reserve space for the controls */
++ hdl = &unicam->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(hdl, 16);
++ if (ret < 0)
++ goto media_unregister;
++ unicam->v4l2_dev.ctrl_handler = hdl;
++
++ /* set the driver data in platform device */
++ platform_set_drvdata(pdev, unicam);
++
++ ret = of_unicam_connect_subdevs(unicam);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to connect subdevs\n");
++ goto free_hdl;
++ }
++
++ /* Enable the block power domain */
++ pm_runtime_enable(&pdev->dev);
++
++ return 0;
++
++free_hdl:
++ v4l2_ctrl_handler_free(hdl);
++media_unregister:
++ media_device_unregister(&unicam->mdev);
++probe_out_v4l2_unregister:
++ v4l2_device_unregister(&unicam->v4l2_dev);
++media_cleanup:
++ media_device_cleanup(&unicam->mdev);
++
++ return ret;
++}
++
++static int unicam_remove(struct platform_device *pdev)
++{
++ struct unicam_device *unicam = platform_get_drvdata(pdev);
++
++ unicam_dbg(2, unicam, "%s\n", __func__);
++
++ pm_runtime_disable(&pdev->dev);
++
++ v4l2_async_notifier_unregister(&unicam->notifier);
++ v4l2_ctrl_handler_free(&unicam->ctrl_handler);
++ v4l2_device_unregister(&unicam->v4l2_dev);
++ video_unregister_device(&unicam->video_dev);
++ if (unicam->sensor_config)
++ v4l2_subdev_free_pad_config(unicam->sensor_config);
++ media_device_unregister(&unicam->mdev);
++ media_device_cleanup(&unicam->mdev);
++
++ return 0;
++}
++
++static const struct of_device_id unicam_of_match[] = {
++ { .compatible = "brcm,bcm2835-unicam", },
++ { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, unicam_of_match);
++
++static struct platform_driver unicam_driver = {
++ .probe = unicam_probe,
++ .remove = unicam_remove,
++ .driver = {
++ .name = UNICAM_MODULE_NAME,
++ .of_match_table = of_match_ptr(unicam_of_match),
++ },
++};
++
++module_platform_driver(unicam_driver);
++
++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
++MODULE_DESCRIPTION("BCM2835 Unicam driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(UNICAM_VERSION);
+--- /dev/null
++++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h
+@@ -0,0 +1,253 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++/*
++ * Copyright (C) 2017-2020 Raspberry Pi Trading.
++ * Dave Stevenson <dave.stevenson@raspberrypi.com>
++ */
++
++#ifndef VC4_REGS_UNICAM_H
++#define VC4_REGS_UNICAM_H
++
++/*
++ * The following values are taken from files found within the code drop
++ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
++ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
++ * They have been modified to be only the register offset.
++ */
++#define UNICAM_CTRL 0x000
++#define UNICAM_STA 0x004
++#define UNICAM_ANA 0x008
++#define UNICAM_PRI 0x00c
++#define UNICAM_CLK 0x010
++#define UNICAM_CLT 0x014
++#define UNICAM_DAT0 0x018
++#define UNICAM_DAT1 0x01c
++#define UNICAM_DAT2 0x020
++#define UNICAM_DAT3 0x024
++#define UNICAM_DLT 0x028
++#define UNICAM_CMP0 0x02c
++#define UNICAM_CMP1 0x030
++#define UNICAM_CAP0 0x034
++#define UNICAM_CAP1 0x038
++#define UNICAM_ICTL 0x100
++#define UNICAM_ISTA 0x104
++#define UNICAM_IDI0 0x108
++#define UNICAM_IPIPE 0x10c
++#define UNICAM_IBSA0 0x110
++#define UNICAM_IBEA0 0x114
++#define UNICAM_IBLS 0x118
++#define UNICAM_IBWP 0x11c
++#define UNICAM_IHWIN 0x120
++#define UNICAM_IHSTA 0x124
++#define UNICAM_IVWIN 0x128
++#define UNICAM_IVSTA 0x12c
++#define UNICAM_ICC 0x130
++#define UNICAM_ICS 0x134
++#define UNICAM_IDC 0x138
++#define UNICAM_IDPO 0x13c
++#define UNICAM_IDCA 0x140
++#define UNICAM_IDCD 0x144
++#define UNICAM_IDS 0x148
++#define UNICAM_DCS 0x200
++#define UNICAM_DBSA0 0x204
++#define UNICAM_DBEA0 0x208
++#define UNICAM_DBWP 0x20c
++#define UNICAM_DBCTL 0x300
++#define UNICAM_IBSA1 0x304
++#define UNICAM_IBEA1 0x308
++#define UNICAM_IDI1 0x30c
++#define UNICAM_DBSA1 0x310
++#define UNICAM_DBEA1 0x314
++#define UNICAM_MISC 0x400
++
++/*
++ * The following bitmasks are from the kernel released by Broadcom
++ * for Android - https://android.googlesource.com/kernel/bcm/
++ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
++ * Unicam block as BCM2835, as defined in eg
++ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
++ * Values reworked to use the kernel BIT and GENMASK macros.
++ *
++ * Some of the bit mnenomics have been amended to match the datasheet.
++ */
++/* UNICAM_CTRL Register */
++#define UNICAM_CPE BIT(0)
++#define UNICAM_MEM BIT(1)
++#define UNICAM_CPR BIT(2)
++#define UNICAM_CPM_MASK GENMASK(3, 3)
++#define UNICAM_CPM_CSI2 0
++#define UNICAM_CPM_CCP2 1
++#define UNICAM_SOE BIT(4)
++#define UNICAM_DCM_MASK GENMASK(5, 5)
++#define UNICAM_DCM_STROBE 0
++#define UNICAM_DCM_DATA 1
++#define UNICAM_SLS BIT(6)
++#define UNICAM_PFT_MASK GENMASK(11, 8)
++#define UNICAM_OET_MASK GENMASK(20, 12)
++
++/* UNICAM_STA Register */
++#define UNICAM_SYN BIT(0)
++#define UNICAM_CS BIT(1)
++#define UNICAM_SBE BIT(2)
++#define UNICAM_PBE BIT(3)
++#define UNICAM_HOE BIT(4)
++#define UNICAM_PLE BIT(5)
++#define UNICAM_SSC BIT(6)
++#define UNICAM_CRCE BIT(7)
++#define UNICAM_OES BIT(8)
++#define UNICAM_IFO BIT(9)
++#define UNICAM_OFO BIT(10)
++#define UNICAM_BFO BIT(11)
++#define UNICAM_DL BIT(12)
++#define UNICAM_PS BIT(13)
++#define UNICAM_IS BIT(14)
++#define UNICAM_PI0 BIT(15)
++#define UNICAM_PI1 BIT(16)
++#define UNICAM_FSI_S BIT(17)
++#define UNICAM_FEI_S BIT(18)
++#define UNICAM_LCI_S BIT(19)
++#define UNICAM_BUF0_RDY BIT(20)
++#define UNICAM_BUF0_NO BIT(21)
++#define UNICAM_BUF1_RDY BIT(22)
++#define UNICAM_BUF1_NO BIT(23)
++#define UNICAM_DI BIT(24)
++
++#define UNICAM_STA_MASK_ALL \
++ (UNICAM_DL + \
++ UNICAM_SBE + \
++ UNICAM_PBE + \
++ UNICAM_HOE + \
++ UNICAM_PLE + \
++ UNICAM_SSC + \
++ UNICAM_CRCE + \
++ UNICAM_IFO + \
++ UNICAM_OFO + \
++ UNICAM_PS + \
++ UNICAM_PI0 + \
++ UNICAM_PI1)
++
++/* UNICAM_ANA Register */
++#define UNICAM_APD BIT(0)
++#define UNICAM_BPD BIT(1)
++#define UNICAM_AR BIT(2)
++#define UNICAM_DDL BIT(3)
++#define UNICAM_CTATADJ_MASK GENMASK(7, 4)
++#define UNICAM_PTATADJ_MASK GENMASK(11, 8)
++
++/* UNICAM_PRI Register */
++#define UNICAM_PE BIT(0)
++#define UNICAM_PT_MASK GENMASK(2, 1)
++#define UNICAM_NP_MASK GENMASK(7, 4)
++#define UNICAM_PP_MASK GENMASK(11, 8)
++#define UNICAM_BS_MASK GENMASK(15, 12)
++#define UNICAM_BL_MASK GENMASK(17, 16)
++
++/* UNICAM_CLK Register */
++#define UNICAM_CLE BIT(0)
++#define UNICAM_CLPD BIT(1)
++#define UNICAM_CLLPE BIT(2)
++#define UNICAM_CLHSE BIT(3)
++#define UNICAM_CLTRE BIT(4)
++#define UNICAM_CLAC_MASK GENMASK(8, 5)
++#define UNICAM_CLSTE BIT(29)
++
++/* UNICAM_CLT Register */
++#define UNICAM_CLT1_MASK GENMASK(7, 0)
++#define UNICAM_CLT2_MASK GENMASK(15, 8)
++
++/* UNICAM_DATn Registers */
++#define UNICAM_DLE BIT(0)
++#define UNICAM_DLPD BIT(1)
++#define UNICAM_DLLPE BIT(2)
++#define UNICAM_DLHSE BIT(3)
++#define UNICAM_DLTRE BIT(4)
++#define UNICAM_DLSM BIT(5)
++#define UNICAM_DLFO BIT(28)
++#define UNICAM_DLSTE BIT(29)
++
++#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO)
++
++/* UNICAM_DLT Register */
++#define UNICAM_DLT1_MASK GENMASK(7, 0)
++#define UNICAM_DLT2_MASK GENMASK(15, 8)
++#define UNICAM_DLT3_MASK GENMASK(23, 16)
++
++/* UNICAM_ICTL Register */
++#define UNICAM_FSIE BIT(0)
++#define UNICAM_FEIE BIT(1)
++#define UNICAM_IBOB BIT(2)
++#define UNICAM_FCM BIT(3)
++#define UNICAM_TFC BIT(4)
++#define UNICAM_LIP_MASK GENMASK(6, 5)
++#define UNICAM_LCIE_MASK GENMASK(28, 16)
++
++/* UNICAM_IDI0/1 Register */
++#define UNICAM_ID0_MASK GENMASK(7, 0)
++#define UNICAM_ID1_MASK GENMASK(15, 8)
++#define UNICAM_ID2_MASK GENMASK(23, 16)
++#define UNICAM_ID3_MASK GENMASK(31, 24)
++
++/* UNICAM_ISTA Register */
++#define UNICAM_FSI BIT(0)
++#define UNICAM_FEI BIT(1)
++#define UNICAM_LCI BIT(2)
++
++#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI)
++
++/* UNICAM_IPIPE Register */
++#define UNICAM_PUM_MASK GENMASK(2, 0)
++ /* Unpacking modes */
++ #define UNICAM_PUM_NONE 0
++ #define UNICAM_PUM_UNPACK6 1
++ #define UNICAM_PUM_UNPACK7 2
++ #define UNICAM_PUM_UNPACK8 3
++ #define UNICAM_PUM_UNPACK10 4
++ #define UNICAM_PUM_UNPACK12 5
++ #define UNICAM_PUM_UNPACK14 6
++ #define UNICAM_PUM_UNPACK16 7
++#define UNICAM_DDM_MASK GENMASK(6, 3)
++#define UNICAM_PPM_MASK GENMASK(9, 7)
++ /* Packing modes */
++ #define UNICAM_PPM_NONE 0
++ #define UNICAM_PPM_PACK8 1
++ #define UNICAM_PPM_PACK10 2
++ #define UNICAM_PPM_PACK12 3
++ #define UNICAM_PPM_PACK14 4
++ #define UNICAM_PPM_PACK16 5
++#define UNICAM_DEM_MASK GENMASK(11, 10)
++#define UNICAM_DEBL_MASK GENMASK(14, 12)
++#define UNICAM_ICM_MASK GENMASK(16, 15)
++#define UNICAM_IDM_MASK GENMASK(17, 17)
++
++/* UNICAM_ICC Register */
++#define UNICAM_ICFL_MASK GENMASK(4, 0)
++#define UNICAM_ICFH_MASK GENMASK(9, 5)
++#define UNICAM_ICST_MASK GENMASK(12, 10)
++#define UNICAM_ICLT_MASK GENMASK(15, 13)
++#define UNICAM_ICLL_MASK GENMASK(31, 16)
++
++/* UNICAM_DCS Register */
++#define UNICAM_DIE BIT(0)
++#define UNICAM_DIM BIT(1)
++#define UNICAM_DBOB BIT(3)
++#define UNICAM_FDE BIT(4)
++#define UNICAM_LDP BIT(5)
++#define UNICAM_EDL_MASK GENMASK(15, 8)
++
++/* UNICAM_DBCTL Register */
++#define UNICAM_DBEN BIT(0)
++#define UNICAM_BUF0_IE BIT(1)
++#define UNICAM_BUF1_IE BIT(2)
++
++/* UNICAM_CMP[0,1] register */
++#define UNICAM_PCE BIT(31)
++#define UNICAM_GI BIT(9)
++#define UNICAM_CPH BIT(8)
++#define UNICAM_PCVC_MASK GENMASK(7, 6)
++#define UNICAM_PCDT_MASK GENMASK(5, 0)
++
++/* UNICAM_MISC register */
++#define UNICAM_FL0 BIT(6)
++#define UNICAM_FL1 BIT(9)
++
++#endif
diff --git a/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch b/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch
new file mode 100644
index 0000000000..de8f1d209a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch
@@ -0,0 +1,85 @@
+From 09f5e82f292a900d17a5205e54a35e24296bd9f7 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 1 Apr 2020 08:46:29 +0100
+Subject: [PATCH] media: uapi: v4l2-core: Add sensor ancillary data
+ V4L2 foucc type.
+
+Add V4L2_META_FMT_SENSOR_DATA format 4CC.
+
+This new format will be used by the BCM2835 Unicam device to return
+out camera sensor embedded data.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ Documentation/media/uapi/v4l/meta-formats.rst | 1 +
+ .../uapi/v4l/pixfmt-meta-sensor-data.rst | 32 +++++++++++++++++++
+ drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
+ include/uapi/linux/videodev2.h | 1 +
+ 4 files changed, 35 insertions(+)
+ create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst
+
+--- a/Documentation/media/uapi/v4l/meta-formats.rst
++++ b/Documentation/media/uapi/v4l/meta-formats.rst
+@@ -21,6 +21,7 @@ These formats are used for the :ref:`met
+
+ pixfmt-meta-d4xx
+ pixfmt-meta-intel-ipu3
++ pixfmt-meta-sensor-data
+ pixfmt-meta-uvc
+ pixfmt-meta-vsp1-hgo
+ pixfmt-meta-vsp1-hgt
+--- /dev/null
++++ b/Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst
+@@ -0,0 +1,32 @@
++.. Permission is granted to copy, distribute and/or modify this
++.. document under the terms of the GNU Free Documentation License,
++.. Version 1.1 or any later version published by the Free Software
++.. Foundation, with no Invariant Sections, no Front-Cover Texts
++.. and no Back-Cover Texts. A copy of the license is included at
++.. Documentation/media/uapi/fdl-appendix.rst.
++..
++.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
++
++.. _v4l2-meta-fmt-sensor-data:
++
++***********************************
++V4L2_META_FMT_SENSOR_DATA ('SENS')
++***********************************
++
++Sensor Ancillary Metadata
++
++Description
++===========
++
++This format describes ancillary data generated by a camera sensor and
++transmitted over a stream on the camera bus. Sensor vendors generally have their
++own custom format for this ancillary data. Some vendors follow a generic
++CSI-2/SMIA embedded data format as described in the `CSI-2 specification.
++<https://mipi.org/specifications/csi-2>`_
++
++The size of the embedded buffer is defined as a single line with a pixel width
++width specified in bytes. This is obtained by a call to the
++:c:type:`VIDIOC_SUBDEV_G_FMT` ioctl on the sensor subdevice where the ``pad``
++field in :c:type:`v4l2_subdev_format` is set to 1. Note that this size is fixed
++and cannot be modified with a call to :c:type:`VIDIOC_SUBDEV_S_FMT`.
++
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1332,6 +1332,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
+ case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break;
+ case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break;
++ case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break;
+
+ default:
+ /* Compressed formats */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -769,6 +769,7 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_VSP1_HGT v4l2_fourcc('V', 'S', 'P', 'T') /* R-Car VSP1 2-D Histogram */
+ #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */
+ #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
++#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */
+
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
diff --git a/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch b/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch
new file mode 100644
index 0000000000..65162cc5d5
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch
@@ -0,0 +1,64 @@
+From 65573c84d5a9115444cc5e365c94cb3ae0fb7e10 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 21 Jan 2020 14:06:47 +0000
+Subject: [PATCH] media: uapi: Add MEDIA_BUS_FMT_SENSOR_DATA media bus
+ format
+
+This patch adds MEDIA_BUS_FMT_SENSOR_DATA used by the bcm2835-unicam
+driver to support CSI-2 embedded data streams from camera sensors.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/uapi/v4l/subdev-formats.rst | 33 +++++++++++++++++++
+ include/uapi/linux/media-bus-format.h | 3 ++
+ 2 files changed, 36 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/subdev-formats.rst
++++ b/Documentation/media/uapi/v4l/subdev-formats.rst
+@@ -7794,3 +7794,36 @@ formats.
+ - 0x5001
+ - Interleaved raw UYVY and JPEG image format with embedded meta-data
+ used by Samsung S3C73MX camera sensors.
++
++
++
++.. _v4l2-mbus-sensor-data:
++
++Sensor Ancillary Metadata Formats
++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++This section lists ancillary data generated by a camera sensor and
++transmitted over a stream on the camera bus.
++
++The following table lists the existing sensor ancillary metadata formats:
++
++
++.. _v4l2-mbus-pixelcode-sensor-metadata:
++
++.. tabularcolumns:: |p{8.0cm}|p{1.4cm}|p{7.7cm}|
++
++.. flat-table:: Sensor ancillary metadata formats
++ :header-rows: 1
++ :stub-columns: 0
++
++ * - Identifier
++ - Code
++ - Comments
++ * .. _MEDIA_BUS_FMT_SENSOR_DATA:
++
++ - MEDIA_BUS_FMT_SENSOR_DATA
++ - 0x7001
++ - Sensor vendor specific ancillary metadata. Some vendors follow a generic
++ CSI-2/SMIA embedded data format as described in the `CSI-2 specification.
++ <https://mipi.org/specifications/csi-2>`_
++
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -155,4 +155,7 @@
+ /* HSV - next is 0x6002 */
+ #define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001
+
++/* Sensor ancillary metadata formats - next is 0x7002 */
++#define MEDIA_BUS_FMT_SENSOR_DATA 0x7001
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch b/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch
new file mode 100644
index 0000000000..315feff5d3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch
@@ -0,0 +1,1084 @@
+From b466d74b45466b417e364c85c7fce71e9fc3fc7c Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 7 Apr 2020 10:42:14 +0100
+Subject: [PATCH] media: bcm2835-unicam: Add support for mulitple
+ device nodes.
+
+Move device node specific state out of the device state structure and
+into a new node structure. This separation will be needed for future
+changes where we will add an embedded data node to the driver to work
+alongside the existing image data node.
+
+Currently only use a single image node, so this commit does not add
+any functional changes.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 484 ++++++++++--------
+ 1 file changed, 283 insertions(+), 201 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -109,7 +109,8 @@ MODULE_PARM_DESC(debug, "Debug level 0-3
+ /* Define a nominal minimum image size */
+ #define MIN_WIDTH 16
+ #define MIN_HEIGHT 16
+-
++/* Maximum number of simulataneous streams Uncaim can handle. */
++#define MAX_NODES 2
+ /*
+ * struct unicam_fmt - Unicam media bus format information
+ * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a.
+@@ -346,11 +347,37 @@ struct unicam_cfg {
+
+ #define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats))
+
+-struct unicam_device {
+- /* V4l2 specific parameters */
++struct unicam_node {
++ bool registered;
++ unsigned int pad_id;
++ /* Pointer pointing to current v4l2_buffer */
++ struct unicam_buffer *cur_frm;
++ /* Pointer pointing to next v4l2_buffer */
++ struct unicam_buffer *next_frm;
++ /* video capture */
++ const struct unicam_fmt *fmt;
++ /* Used to store current pixel format */
++ struct v4l2_format v_fmt;
++ /* Used to store current mbus frame format */
++ struct v4l2_mbus_framefmt m_fmt;
++ /* Buffer queue used in video-buf */
++ struct vb2_queue buffer_queue;
++ /* Queue of filled frames */
++ struct unicam_dmaqueue dma_queue;
++ /* IRQ lock for DMA queue */
++ spinlock_t dma_queue_lock;
++ /* lock used to access this structure */
++ struct mutex lock;
+ /* Identifies video device for this channel */
+ struct video_device video_dev;
++ /* Pointer to the parent handle */
++ struct unicam_device *dev;
++ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
++};
++
++struct unicam_device {
++ /* V4l2 specific parameters */
+
+ struct v4l2_fwnode_endpoint endpoint;
+
+@@ -363,7 +390,6 @@ struct unicam_device {
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ struct media_device mdev;
+- struct media_pad pad;
+
+ /* parent device */
+ struct platform_device *pdev;
+@@ -378,18 +404,6 @@ struct unicam_device {
+ /* current input at the sub device */
+ int current_input;
+
+- /* Pointer pointing to current v4l2_buffer */
+- struct unicam_buffer *cur_frm;
+- /* Pointer pointing to next v4l2_buffer */
+- struct unicam_buffer *next_frm;
+-
+- /* video capture */
+- const struct unicam_fmt *fmt;
+- /* Used to store current pixel format */
+- struct v4l2_format v_fmt;
+- /* Used to store current mbus frame format */
+- struct v4l2_mbus_framefmt m_fmt;
+-
+ unsigned int virtual_channel;
+ enum v4l2_mbus_type bus_type;
+ /*
+@@ -401,20 +415,10 @@ struct unicam_device {
+ unsigned int active_data_lanes;
+
+ struct v4l2_rect crop;
+-
+- /* Currently selected input on subdev */
+- int input;
+-
+- /* Buffer queue used in video-buf */
+- struct vb2_queue buffer_queue;
+- /* Queue of filled frames */
+- struct unicam_dmaqueue dma_queue;
+- /* IRQ lock for DMA queue */
+- spinlock_t dma_queue_lock;
+- /* lock used to access this structure */
+- struct mutex lock;
+ /* Flag to denote that we are processing buffers */
+ int streaming;
++
++ struct unicam_node node[MAX_NODES];
+ };
+
+ /* Hardware access */
+@@ -526,10 +530,11 @@ static inline unsigned int bytes_per_lin
+ }
+
+ static int __subdev_get_format(struct unicam_device *dev,
+- struct v4l2_mbus_framefmt *fmt)
++ struct v4l2_mbus_framefmt *fmt, int pad_id)
+ {
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .pad = pad_id
+ };
+ int ret;
+
+@@ -598,29 +603,30 @@ static int unicam_calc_format_size_bpl(s
+ return 0;
+ }
+
+-static int unicam_reset_format(struct unicam_device *dev)
++static int unicam_reset_format(struct unicam_node *node)
+ {
++ struct unicam_device *dev = node->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+- ret = __subdev_get_format(dev, &mbus_fmt);
++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
+ if (ret) {
+ unicam_err(dev, "Failed to get_format - ret %d\n", ret);
+ return ret;
+ }
+
+- if (mbus_fmt.code != dev->fmt->code) {
++ if (mbus_fmt.code != dev->node[0].fmt->code) {
+ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n",
+- dev->fmt->code, mbus_fmt.code);
++ dev->node[0].fmt->code, mbus_fmt.code);
+ return ret;
+ }
+
+- v4l2_fill_pix_format(&dev->v_fmt.fmt.pix, &mbus_fmt);
+- dev->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ v4l2_fill_pix_format(&dev->node[0].v_fmt.fmt.pix, &mbus_fmt);
++ dev->node[0].v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+- unicam_calc_format_size_bpl(dev, dev->fmt, &dev->v_fmt);
++ unicam_calc_format_size_bpl(dev, dev->node[0].fmt, &dev->node[0].v_fmt);
+
+- dev->m_fmt = mbus_fmt;
++ dev->node[0].m_fmt = mbus_fmt;
+
+ return 0;
+ }
+@@ -635,14 +641,14 @@ static void unicam_wr_dma_addr(struct un
+
+ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
+ reg_write(&dev->cfg, UNICAM_IBEA0,
+- dmaaddr + dev->v_fmt.fmt.pix.sizeimage);
++ dmaaddr + dev->node[0].v_fmt.fmt.pix.sizeimage);
+ }
+
+ static inline unsigned int unicam_get_lines_done(struct unicam_device *dev)
+ {
+ dma_addr_t start_addr, cur_addr;
+- unsigned int stride = dev->v_fmt.fmt.pix.bytesperline;
+- struct unicam_buffer *frm = dev->cur_frm;
++ unsigned int stride = dev->node[0].v_fmt.fmt.pix.bytesperline;
++ struct unicam_buffer *frm = dev->node[0].cur_frm;
+
+ if (!frm)
+ return 0;
+@@ -654,12 +660,12 @@ static inline unsigned int unicam_get_li
+
+ static inline void unicam_schedule_next_buffer(struct unicam_device *dev)
+ {
+- struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_dmaqueue *dma_q = &dev->node[0].dma_queue;
+ struct unicam_buffer *buf;
+ dma_addr_t addr;
+
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+- dev->next_frm = buf;
++ dev->node[0].next_frm = buf;
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+@@ -668,11 +674,11 @@ static inline void unicam_schedule_next_
+
+ static inline void unicam_process_buffer_complete(struct unicam_device *dev)
+ {
+- dev->cur_frm->vb.field = dev->m_fmt.field;
+- dev->cur_frm->vb.sequence = dev->sequence++;
++ dev->node[0].cur_frm->vb.field = dev->node[0].m_fmt.field;
++ dev->node[0].cur_frm->vb.sequence = dev->sequence++;
+
+- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+- dev->cur_frm = dev->next_frm;
++ vb2_buffer_done(&dev->node[0].cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++ dev->node[0].cur_frm = dev->node[0].next_frm;
+ }
+
+ /*
+@@ -687,7 +693,7 @@ static irqreturn_t unicam_isr(int irq, v
+ {
+ struct unicam_device *unicam = (struct unicam_device *)dev;
+ struct unicam_cfg *cfg = &unicam->cfg;
+- struct unicam_dmaqueue *dma_q = &unicam->dma_queue;
++ struct unicam_dmaqueue *dma_q = &unicam->node[0].dma_queue;
+ unsigned int lines_done = unicam_get_lines_done(dev);
+ unsigned int sequence = unicam->sequence;
+ int ista, sta;
+@@ -720,8 +726,9 @@ static irqreturn_t unicam_isr(int irq, v
+ * Timestamp is to be when the first data byte was captured,
+ * aka frame start.
+ */
+- if (unicam->cur_frm)
+- unicam->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
++ if (unicam->node[0].cur_frm)
++ unicam->node[0].cur_frm->vb.vb2_buf.timestamp =
++ ktime_get_ns();
+ }
+ if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
+ /*
+@@ -729,7 +736,8 @@ static irqreturn_t unicam_isr(int irq, v
+ * stop the peripheral. Overwrite the frame we've just
+ * captured instead.
+ */
+- if (unicam->cur_frm && unicam->cur_frm != unicam->next_frm)
++ if (unicam->node[0].cur_frm &&
++ unicam->node[0].cur_frm != unicam->node[0].next_frm)
+ unicam_process_buffer_complete(unicam);
+ }
+
+@@ -738,11 +746,11 @@ static irqreturn_t unicam_isr(int irq, v
+ * already started.
+ */
+ if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) {
+- spin_lock(&unicam->dma_queue_lock);
++ spin_lock(&unicam->node[0].dma_queue_lock);
+ if (!list_empty(&dma_q->active) &&
+- unicam->cur_frm == unicam->next_frm)
++ unicam->node[0].cur_frm == unicam->node[0].next_frm)
+ unicam_schedule_next_buffer(unicam);
+- spin_unlock(&unicam->dma_queue_lock);
++ spin_unlock(&unicam->node[0].dma_queue_lock);
+ }
+
+ if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
+@@ -756,7 +764,8 @@ static irqreturn_t unicam_isr(int irq, v
+ static int unicam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+@@ -770,7 +779,8 @@ static int unicam_querycap(struct file *
+ static int unicam_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ const struct unicam_fmt *fmt = NULL;
+ int index = 0;
+@@ -815,9 +825,9 @@ static int unicam_enum_fmt_vid_cap(struc
+ static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
+
+- *f = dev->v_fmt;
++ *f = node->v_fmt;
+
+ return 0;
+ }
+@@ -859,9 +869,11 @@ const struct unicam_fmt *get_first_suppo
+ static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
++ .pad = 0
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ const struct unicam_fmt *fmt;
+@@ -939,8 +951,9 @@ static int unicam_try_fmt_vid_cap(struct
+ static int unicam_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+ {
+- struct unicam_device *dev = video_drvdata(file);
+- struct vb2_queue *q = &dev->buffer_queue;
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ struct vb2_queue *q = &node->buffer_queue;
+ struct v4l2_mbus_framefmt mbus_fmt = {0};
+ const struct unicam_fmt *fmt;
+ int ret;
+@@ -985,17 +998,18 @@ static int unicam_s_fmt_vid_cap(struct f
+ return -EINVAL;
+ }
+
+- dev->fmt = fmt;
+- dev->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+- dev->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+- unicam_reset_format(dev);
+-
+- unicam_dbg(3, dev, "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n",
+- __func__, dev->v_fmt.fmt.pix.width,
+- dev->v_fmt.fmt.pix.height, mbus_fmt.code,
+- dev->v_fmt.fmt.pix.pixelformat);
++ node->fmt = fmt;
++ node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
++ node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
++ unicam_reset_format(node);
++
++ unicam_dbg(3, dev,
++ "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n",
++ __func__, node->v_fmt.fmt.pix.width,
++ node->v_fmt.fmt.pix.height, mbus_fmt.code,
++ node->v_fmt.fmt.pix.pixelformat);
+
+- *f = dev->v_fmt;
++ *f = node->v_fmt;
+
+ return 0;
+ }
+@@ -1006,8 +1020,9 @@ static int unicam_queue_setup(struct vb2
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+ {
+- struct unicam_device *dev = vb2_get_drv_priv(vq);
+- unsigned int size = dev->v_fmt.fmt.pix.sizeimage;
++ struct unicam_node *node = vb2_get_drv_priv(vq);
++ struct unicam_device *dev = node->dev;
++ unsigned int size = node->v_fmt.fmt.pix.sizeimage;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+@@ -1029,15 +1044,16 @@ static int unicam_queue_setup(struct vb2
+
+ static int unicam_buffer_prepare(struct vb2_buffer *vb)
+ {
+- struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
++ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct unicam_device *dev = node->dev;
+ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+ vb.vb2_buf);
+ unsigned long size;
+
+- if (WARN_ON(!dev->fmt))
++ if (WARN_ON(!node->fmt))
+ return -EINVAL;
+
+- size = dev->v_fmt.fmt.pix.sizeimage;
++ size = node->v_fmt.fmt.pix.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+@@ -1050,15 +1066,15 @@ static int unicam_buffer_prepare(struct
+
+ static void unicam_buffer_queue(struct vb2_buffer *vb)
+ {
+- struct unicam_device *dev = vb2_get_drv_priv(vb->vb2_queue);
++ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = container_of(vb, struct unicam_buffer,
+ vb.vb2_buf);
+- struct unicam_dmaqueue *dma_queue = &dev->dma_queue;
++ struct unicam_dmaqueue *dma_queue = &node->dma_queue;
+ unsigned long flags = 0;
+
+- spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ list_add_tail(&buf->list, &dma_queue->active);
+- spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+ }
+
+ static void unicam_set_packing_config(struct unicam_device *dev)
+@@ -1066,11 +1082,12 @@ static void unicam_set_packing_config(st
+ int pack, unpack;
+ u32 val;
+
+- if (dev->v_fmt.fmt.pix.pixelformat == dev->fmt->fourcc) {
++ if (dev->node[0].v_fmt.fmt.pix.pixelformat ==
++ dev->node[0].fmt->fourcc) {
+ unpack = UNICAM_PUM_NONE;
+ pack = UNICAM_PPM_NONE;
+ } else {
+- switch (dev->fmt->depth) {
++ switch (dev->node[0].fmt->depth) {
+ case 8:
+ unpack = UNICAM_PUM_UNPACK8;
+ break;
+@@ -1108,17 +1125,17 @@ static void unicam_cfg_image_id(struct u
+ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 mode */
+ reg_write(cfg, UNICAM_IDI0,
+- (dev->virtual_channel << 6) | dev->fmt->csi_dt);
++ (dev->virtual_channel << 6) | dev->node[0].fmt->csi_dt);
+ } else {
+ /* CCP2 mode */
+- reg_write(cfg, UNICAM_IDI0, (0x80 | dev->fmt->csi_dt));
++ reg_write(cfg, UNICAM_IDI0, (0x80 | dev->node[0].fmt->csi_dt));
+ }
+ }
+
+ static void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
+ {
+ struct unicam_cfg *cfg = &dev->cfg;
+- int line_int_freq = dev->v_fmt.fmt.pix.height >> 2;
++ int line_int_freq = dev->node[0].v_fmt.fmt.pix.height >> 2;
+ unsigned int i;
+ u32 val;
+
+@@ -1266,7 +1283,8 @@ static void unicam_start_rx(struct unica
+ reg_write(cfg, UNICAM_DAT3, val);
+ }
+
+- reg_write(&dev->cfg, UNICAM_IBLS, dev->v_fmt.fmt.pix.bytesperline);
++ reg_write(&dev->cfg, UNICAM_IBLS,
++ dev->node[0].v_fmt.fmt.pix.bytesperline);
+ unicam_wr_dma_addr(dev, addr);
+ unicam_set_packing_config(dev);
+ unicam_cfg_image_id(dev);
+@@ -1327,21 +1345,22 @@ static void unicam_disable(struct unicam
+
+ static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+ {
+- struct unicam_device *dev = vb2_get_drv_priv(vq);
+- struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_node *node = vb2_get_drv_priv(vq);
++ struct unicam_device *dev = node->dev;
++ struct unicam_dmaqueue *dma_q = &node->dma_queue;
+ struct unicam_buffer *buf, *tmp;
+ unsigned long addr = 0;
+ unsigned long flags;
+ int ret;
+
+- spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+- dev->cur_frm = buf;
+- dev->next_frm = buf;
++ node->cur_frm = buf;
++ node->next_frm = buf;
+ list_del(&buf->list);
+- spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+
+- addr = vb2_dma_contig_plane_dma_addr(&dev->cur_frm->vb.vb2_buf, 0);
++ addr = vb2_dma_contig_plane_dma_addr(&node->cur_frm->vb.vb2_buf, 0);
+ dev->sequence = 0;
+
+ ret = unicam_runtime_get(dev);
+@@ -1411,20 +1430,21 @@ err_release_buffers:
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+- if (dev->cur_frm != dev->next_frm)
+- vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
++ if (node->cur_frm != node->next_frm)
++ vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_QUEUED);
+- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+- dev->next_frm = NULL;
+- dev->cur_frm = NULL;
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
++ node->next_frm = NULL;
++ node->cur_frm = NULL;
+
+ return ret;
+ }
+
+ static void unicam_stop_streaming(struct vb2_queue *vq)
+ {
+- struct unicam_device *dev = vb2_get_drv_priv(vq);
+- struct unicam_dmaqueue *dma_q = &dev->dma_queue;
++ struct unicam_node *node = vb2_get_drv_priv(vq);
++ struct unicam_device *dev = node->dev;
++ struct unicam_dmaqueue *dma_q = &node->dma_queue;
+ struct unicam_buffer *buf, *tmp;
+ unsigned long flags;
+
+@@ -1434,22 +1454,24 @@ static void unicam_stop_streaming(struct
+ unicam_disable(dev);
+
+ /* Release all active buffers */
+- spin_lock_irqsave(&dev->dma_queue_lock, flags);
++ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+- if (dev->cur_frm == dev->next_frm) {
+- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++ if (node->cur_frm == node->next_frm) {
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
++ VB2_BUF_STATE_ERROR);
+ } else {
+- vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+- vb2_buffer_done(&dev->next_frm->vb.vb2_buf,
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
++ VB2_BUF_STATE_ERROR);
++ vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+- dev->cur_frm = NULL;
+- dev->next_frm = NULL;
+- spin_unlock_irqrestore(&dev->dma_queue_lock, flags);
++ node->cur_frm = NULL;
++ node->next_frm = NULL;
++ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+
+ clk_disable_unprepare(dev->clock);
+ unicam_runtime_put(dev);
+@@ -1458,7 +1480,8 @@ static void unicam_stop_streaming(struct
+ static int unicam_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ if (inp->index != 0)
+ return -EINVAL;
+@@ -1506,21 +1529,24 @@ static int unicam_s_input(struct file *f
+ static int unicam_querystd(struct file *file, void *priv,
+ v4l2_std_id *std)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, video, querystd, std);
+ }
+
+ static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, video, g_std, std);
+ }
+
+ static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ int ret;
+ v4l2_std_id current_std;
+
+@@ -1531,29 +1557,31 @@ static int unicam_s_std(struct file *fil
+ if (std == current_std)
+ return 0;
+
+- if (vb2_is_busy(&dev->buffer_queue))
++ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_std, std);
+
+ /* Force recomputation of bytesperline */
+- dev->v_fmt.fmt.pix.bytesperline = 0;
++ node->v_fmt.fmt.pix.bytesperline = 0;
+
+- unicam_reset_format(dev);
++ unicam_reset_format(node);
+
+ return ret;
+ }
+
+ static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, pad, set_edid, edid);
+ }
+
+ static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+ }
+@@ -1561,7 +1589,8 @@ static int unicam_g_edid(struct file *fi
+ static int unicam_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ const struct unicam_fmt *fmt;
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+@@ -1596,7 +1625,8 @@ static int unicam_enum_framesizes(struct
+ static int unicam_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ const struct unicam_fmt *fmt;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+@@ -1624,14 +1654,16 @@ static int unicam_enum_frameintervals(st
+
+ static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a);
+ }
+
+ static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a);
+ }
+@@ -1639,7 +1671,8 @@ static int unicam_s_parm(struct file *fi
+ static int unicam_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings);
+ }
+@@ -1647,7 +1680,8 @@ static int unicam_g_dv_timings(struct fi
+ static int unicam_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ struct v4l2_dv_timings current_timings;
+ int ret;
+
+@@ -1657,15 +1691,15 @@ static int unicam_s_dv_timings(struct fi
+ if (v4l2_match_dv_timings(timings, &current_timings, 0, false))
+ return 0;
+
+- if (vb2_is_busy(&dev->buffer_queue))
++ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings);
+
+ /* Force recomputation of bytesperline */
+- dev->v_fmt.fmt.pix.bytesperline = 0;
++ node->v_fmt.fmt.pix.bytesperline = 0;
+
+- unicam_reset_format(dev);
++ unicam_reset_format(node);
+
+ return ret;
+ }
+@@ -1673,7 +1707,8 @@ static int unicam_s_dv_timings(struct fi
+ static int unicam_query_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings);
+ }
+@@ -1681,7 +1716,8 @@ static int unicam_query_dv_timings(struc
+ static int unicam_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings);
+ }
+@@ -1689,7 +1725,8 @@ static int unicam_enum_dv_timings(struct
+ static int unicam_dv_timings_cap(struct file *file, void *priv,
+ struct v4l2_dv_timings_cap *cap)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+
+ return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap);
+ }
+@@ -1707,7 +1744,8 @@ static int unicam_subscribe_event(struct
+
+ static int unicam_log_status(struct file *file, void *fh)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ struct unicam_cfg *cfg = &dev->cfg;
+ u32 reg;
+
+@@ -1716,10 +1754,10 @@ static int unicam_log_status(struct file
+
+ unicam_info(dev, "-----Receiver status-----\n");
+ unicam_info(dev, "V4L2 width/height: %ux%u\n",
+- dev->v_fmt.fmt.pix.width, dev->v_fmt.fmt.pix.height);
+- unicam_info(dev, "Mediabus format: %08x\n", dev->fmt->code);
++ node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height);
++ unicam_info(dev, "Mediabus format: %08x\n", node->fmt->code);
+ unicam_info(dev, "V4L2 format: %08x\n",
+- dev->v_fmt.fmt.pix.pixelformat);
++ node->v_fmt.fmt.pix.pixelformat);
+ reg = reg_read(&dev->cfg, UNICAM_IPIPE);
+ unicam_info(dev, "Unpacking/packing: %u / %u\n",
+ get_field(reg, UNICAM_PUM_MASK),
+@@ -1744,7 +1782,7 @@ static void unicam_notify(struct v4l2_su
+
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+- v4l2_event_queue(&dev->video_dev, arg);
++ v4l2_event_queue(&dev->node[0].video_dev, arg);
+ break;
+ default:
+ break;
+@@ -1767,10 +1805,11 @@ static const struct vb2_ops unicam_video
+ */
+ static int unicam_open(struct file *file)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ int ret;
+
+- mutex_lock(&dev->lock);
++ mutex_lock(&node->lock);
+
+ ret = v4l2_fh_open(file);
+ if (ret) {
+@@ -1790,18 +1829,19 @@ static int unicam_open(struct file *file
+ ret = 0;
+
+ unlock:
+- mutex_unlock(&dev->lock);
++ mutex_unlock(&node->lock);
+ return ret;
+ }
+
+ static int unicam_release(struct file *file)
+ {
+- struct unicam_device *dev = video_drvdata(file);
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
+ struct v4l2_subdev *sd = dev->sensor;
+ bool fh_singular;
+ int ret;
+
+- mutex_lock(&dev->lock);
++ mutex_lock(&node->lock);
+
+ fh_singular = v4l2_fh_is_singular_file(file);
+
+@@ -1810,7 +1850,7 @@ static int unicam_release(struct file *f
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+- mutex_unlock(&dev->lock);
++ mutex_unlock(&node->lock);
+
+ return ret;
+ }
+@@ -1892,7 +1932,8 @@ unicam_async_bound(struct v4l2_async_not
+ return 0;
+ }
+
+-static int unicam_probe_complete(struct unicam_device *unicam)
++static int register_node(struct unicam_device *unicam, struct unicam_node *node,
++ enum v4l2_buf_type type, int pad_id)
+ {
+ struct video_device *vdev;
+ struct vb2_queue *q;
+@@ -1900,15 +1941,7 @@ static int unicam_probe_complete(struct
+ const struct unicam_fmt *fmt;
+ int ret;
+
+- v4l2_set_subdev_hostdata(unicam->sensor, unicam);
+-
+- unicam->v4l2_dev.notify = unicam_notify;
+-
+- unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
+- if (!unicam->sensor_config)
+- return -ENOMEM;
+-
+- ret = __subdev_get_format(unicam, &mbus_fmt);
++ ret = __subdev_get_format(unicam, &mbus_fmt, pad_id);
+ if (ret) {
+ unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
+ return ret;
+@@ -1938,14 +1971,15 @@ static int unicam_probe_complete(struct
+ return -EINVAL;
+ }
+
+- unicam->fmt = fmt;
++ node->pad_id = pad_id;
++ node->fmt = fmt;
+ if (fmt->fourcc)
+- unicam->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ else
+- unicam->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
++ node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
+
+ /* Read current subdev format */
+- unicam_reset_format(unicam);
++ unicam_reset_format(node);
+
+ if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_std_id tvnorms;
+@@ -1962,27 +1996,30 @@ static int unicam_probe_complete(struct
+ g_tvnorms, &tvnorms);
+ if (WARN_ON(ret))
+ return -EINVAL;
+- unicam->video_dev.tvnorms |= tvnorms;
++ node->video_dev.tvnorms |= tvnorms;
+ }
+
+- spin_lock_init(&unicam->dma_queue_lock);
+- mutex_init(&unicam->lock);
++ spin_lock_init(&node->dma_queue_lock);
++ mutex_init(&node->lock);
+
+- /* Add controls from the subdevice */
+- ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler,
+- unicam->sensor->ctrl_handler, NULL, true);
+- if (ret < 0)
+- return ret;
++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ /* Add controls from the subdevice */
++ ret = v4l2_ctrl_add_handler(&node->ctrl_handler,
++ unicam->sensor->ctrl_handler, NULL,
++ true);
++ if (ret < 0)
++ return ret;
++ }
+
+- q = &unicam->buffer_queue;
+- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ q = &node->buffer_queue;
++ q->type = type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+- q->drv_priv = unicam;
++ q->drv_priv = node;
+ q->ops = &unicam_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct unicam_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+- q->lock = &unicam->lock;
++ q->lock = &node->lock;
+ q->min_buffers_needed = 2;
+ q->dev = &unicam->pdev->dev;
+
+@@ -1992,9 +2029,9 @@ static int unicam_probe_complete(struct
+ return ret;
+ }
+
+- INIT_LIST_HEAD(&unicam->dma_queue.active);
++ INIT_LIST_HEAD(&node->dma_queue.active);
+
+- vdev = &unicam->video_dev;
++ vdev = &node->video_dev;
+ strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &unicam_fops;
+@@ -2002,69 +2039,113 @@ static int unicam_probe_complete(struct
+ vdev->v4l2_dev = &unicam->v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+- vdev->lock = &unicam->lock;
++ vdev->lock = &node->lock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+-
+ /* If the source has no controls then remove our ctrl handler. */
+- if (list_empty(&unicam->ctrl_handler.ctrls))
++ if (list_empty(&node->ctrl_handler.ctrls))
+ unicam->v4l2_dev.ctrl_handler = NULL;
+
+- video_set_drvdata(vdev, unicam);
++ node->dev = unicam;
++ video_set_drvdata(vdev, node);
+ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+ if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_STD);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_STD);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUMSTD);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD);
+ }
+ if (!v4l2_subdev_has_op(unicam->sensor, video, querystd))
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERYSTD);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD);
+ if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_EDID);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_EDID);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_DV_TIMINGS_CAP);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_DV_TIMINGS);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_DV_TIMINGS);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_DV_TIMINGS);
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_QUERY_DV_TIMINGS);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_DV_TIMINGS);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_DV_TIMINGS);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS);
+ }
+ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval))
+- v4l2_disable_ioctl(&unicam->video_dev,
++ v4l2_disable_ioctl(&node->video_dev,
+ VIDIOC_ENUM_FRAMEINTERVALS);
+ if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval))
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_G_PARM);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM);
+ if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval))
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_S_PARM);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM);
+
+ if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
+- v4l2_disable_ioctl(&unicam->video_dev, VIDIOC_ENUM_FRAMESIZES);
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ unicam_err(unicam, "Unable to register video device.\n");
+ return ret;
+ }
++ node->registered = true;
+
+- ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
++ ret = media_create_pad_link(&unicam->sensor->entity,
++ 0, &node->video_dev.entity, 0,
++ MEDIA_LNK_FL_ENABLED |
++ MEDIA_LNK_FL_IMMUTABLE);
++ if (ret)
++ unicam_err(unicam, "Unable to create pad links.\n");
++
++ return ret;
++}
++
++static void unregister_nodes(struct unicam_device *unicam)
++{
++ if (unicam->node[0].registered) {
++ video_unregister_device(&unicam->node[0].video_dev);
++ unicam->node[0].registered = false;
++ }
++ if (unicam->node[1].registered) {
++ video_unregister_device(&unicam->node[1].video_dev);
++ unicam->node[1].registered = false;
++ }
++}
++
++static int unicam_probe_complete(struct unicam_device *unicam)
++{
++ int ret;
++
++ v4l2_set_subdev_hostdata(unicam->sensor, unicam);
++
++ unicam->v4l2_dev.notify = unicam_notify;
++
++ unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor);
++ if (!unicam->sensor_config)
++ return -ENOMEM;
++
++ ret = register_node(unicam, &unicam->node[0],
++ V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
+ if (ret) {
+- unicam_err(unicam,
+- "Unable to register subdev nodes.\n");
+- video_unregister_device(&unicam->video_dev);
+- return ret;
++ unicam_err(unicam, "Unable to register subdev node 0.\n");
++ goto unregister;
++ }
++ if (unicam->sensor->entity.num_pads >= 2) {
++ ret = register_node(unicam, &unicam->node[1],
++ V4L2_BUF_TYPE_META_CAPTURE, 1);
++ if (ret) {
++ unicam_err(unicam,
++ "Unable to register subdev node 1.\n");
++ goto unregister;
++ }
+ }
+
+- ret = media_create_pad_link(&unicam->sensor->entity, 0,
+- &unicam->video_dev.entity, 0,
+- MEDIA_LNK_FL_ENABLED |
+- MEDIA_LNK_FL_IMMUTABLE);
++ ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
+ if (ret) {
+- unicam_err(unicam, "Unable to create pad links.\n");
+- video_unregister_device(&unicam->video_dev);
+- return ret;
++ unicam_err(unicam, "Unable to register subdev nodes.\n");
++ goto unregister;
+ }
+
+ return 0;
++
++unregister:
++ unregister_nodes(unicam);
++
++ return ret;
+ }
+
+ static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+@@ -2274,7 +2355,8 @@ static int unicam_probe(struct platform_
+ pdev->dev.driver->name, dev_name(&pdev->dev));
+ unicam->mdev.hw_revision = 1;
+
+- media_entity_pads_init(&unicam->video_dev.entity, 1, &unicam->pad);
++ media_entity_pads_init(&unicam->node[0].video_dev.entity, 1,
++ &unicam->node[0].pad);
+ media_device_init(&unicam->mdev);
+
+ unicam->v4l2_dev.mdev = &unicam->mdev;
+@@ -2294,7 +2376,7 @@ static int unicam_probe(struct platform_
+ }
+
+ /* Reserve space for the controls */
+- hdl = &unicam->ctrl_handler;
++ hdl = &unicam->node[0].ctrl_handler;
+ ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret < 0)
+ goto media_unregister;
+@@ -2335,9 +2417,9 @@ static int unicam_remove(struct platform
+ pm_runtime_disable(&pdev->dev);
+
+ v4l2_async_notifier_unregister(&unicam->notifier);
+- v4l2_ctrl_handler_free(&unicam->ctrl_handler);
++ v4l2_ctrl_handler_free(&unicam->node[0].ctrl_handler);
+ v4l2_device_unregister(&unicam->v4l2_dev);
+- video_unregister_device(&unicam->video_dev);
++ unregister_nodes(unicam);
+ if (unicam->sensor_config)
+ v4l2_subdev_free_pad_config(unicam->sensor_config);
+ media_device_unregister(&unicam->mdev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch b/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch
new file mode 100644
index 0000000000..a163a6f1a5
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch
@@ -0,0 +1,1170 @@
+From 272ee62d6410319ab4d73997de32776cc3e274cb Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Apr 2020 11:35:41 +0100
+Subject: [PATCH] media: bcm2835-unicam: Add embedded data node.
+
+This patch adds a new node in the bcm2835-unicam driver to support
+CSI-2 embedded data streams. The subdevice is queried to see if
+embedded data is available from the sensor.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 667 +++++++++++++-----
+ 1 file changed, 474 insertions(+), 193 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -109,8 +109,15 @@ MODULE_PARM_DESC(debug, "Debug level 0-3
+ /* Define a nominal minimum image size */
+ #define MIN_WIDTH 16
+ #define MIN_HEIGHT 16
+-/* Maximum number of simulataneous streams Uncaim can handle. */
+-#define MAX_NODES 2
++/* Default size of the embedded buffer */
++#define UNICAM_EMBEDDED_SIZE 8192
++
++enum pad_types {
++ IMAGE_PAD,
++ METADATA_PAD,
++ MAX_NODES
++};
++
+ /*
+ * struct unicam_fmt - Unicam media bus format information
+ * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a.
+@@ -327,6 +334,12 @@ static const struct unicam_fmt formats[]
+ .depth = 12,
+ .csi_dt = 0x2c,
+ },
++ /* Embedded data format */
++ {
++ .fourcc = V4L2_META_FMT_SENSOR_DATA,
++ .code = MEDIA_BUS_FMT_SENSOR_DATA,
++ .depth = 8,
++ }
+ };
+
+ struct unicam_dmaqueue {
+@@ -348,7 +361,9 @@ struct unicam_cfg {
+ #define MAX_POSSIBLE_PIX_FMTS (ARRAY_SIZE(formats))
+
+ struct unicam_node {
+- bool registered;
++ int registered;
++ int open;
++ int streaming;
+ unsigned int pad_id;
+ /* Pointer pointing to current v4l2_buffer */
+ struct unicam_buffer *cur_frm;
+@@ -374,6 +389,7 @@ struct unicam_node {
+ struct unicam_device *dev;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
++ unsigned int embedded_lines;
+ };
+
+ struct unicam_device {
+@@ -401,8 +417,6 @@ struct unicam_device {
+ struct v4l2_subdev *sensor;
+ /* Pad config for the sensor */
+ struct v4l2_subdev_pad_config *sensor_config;
+- /* current input at the sub device */
+- int current_input;
+
+ unsigned int virtual_channel;
+ enum v4l2_mbus_type bus_type;
+@@ -413,10 +427,7 @@ struct unicam_device {
+ unsigned int bus_flags;
+ unsigned int max_data_lanes;
+ unsigned int active_data_lanes;
+-
+- struct v4l2_rect crop;
+- /* Flag to denote that we are processing buffers */
+- int streaming;
++ bool sensor_embedded_data;
+
+ struct unicam_node node[MAX_NODES];
+ };
+@@ -488,6 +499,7 @@ static int check_mbus_format(struct unic
+ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = i;
++ mbus_code.pad = IMAGE_PAD;
+ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
+@@ -552,10 +564,11 @@ static int __subdev_get_format(struct un
+ }
+
+ static int __subdev_set_format(struct unicam_device *dev,
+- struct v4l2_mbus_framefmt *fmt)
++ struct v4l2_mbus_framefmt *fmt, int pad_id)
+ {
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .pad = pad_id
+ };
+ int ret;
+
+@@ -566,8 +579,12 @@ static int __subdev_set_format(struct un
+ if (ret < 0)
+ return ret;
+
+- unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__,
+- fmt->width, fmt->height, fmt->code);
++ if (pad_id == IMAGE_PAD)
++ unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width,
++ fmt->height, fmt->code);
++ else
++ unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__,
++ sd_fmt.format.code);
+
+ return 0;
+ }
+@@ -609,46 +626,70 @@ static int unicam_reset_format(struct un
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret;
+
+- ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
+- if (ret) {
+- unicam_err(dev, "Failed to get_format - ret %d\n", ret);
+- return ret;
+- }
++ if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) {
++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
++ if (ret) {
++ unicam_err(dev, "Failed to get_format - ret %d\n", ret);
++ return ret;
++ }
+
+- if (mbus_fmt.code != dev->node[0].fmt->code) {
+- unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n",
+- dev->node[0].fmt->code, mbus_fmt.code);
+- return ret;
++ if (mbus_fmt.code != node->fmt->code) {
++ unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n",
++ node->fmt->code, mbus_fmt.code);
++ return ret;
++ }
+ }
+
+- v4l2_fill_pix_format(&dev->node[0].v_fmt.fmt.pix, &mbus_fmt);
+- dev->node[0].v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-
+- unicam_calc_format_size_bpl(dev, dev->node[0].fmt, &dev->node[0].v_fmt);
+-
+- dev->node[0].m_fmt = mbus_fmt;
++ if (node->pad_id == IMAGE_PAD) {
++ v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt);
++ node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt);
++ } else {
++ node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
++ node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
++ if (dev->sensor_embedded_data) {
++ node->v_fmt.fmt.meta.buffersize =
++ mbus_fmt.width * mbus_fmt.height;
++ node->embedded_lines = mbus_fmt.height;
++ } else {
++ node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE;
++ node->embedded_lines = 1;
++ }
++ }
+
++ node->m_fmt = mbus_fmt;
+ return 0;
+ }
+
+-static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr)
++static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr,
++ int pad_id)
+ {
++ dma_addr_t endaddr;
++
+ /*
+ * dmaaddr should be a 32-bit address with the top two bits set to 0x3
+ * to signify uncached access through the Videocore memory controller.
+ */
+ BUG_ON((dmaaddr >> 30) != 0x3);
+
+- reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
+- reg_write(&dev->cfg, UNICAM_IBEA0,
+- dmaaddr + dev->node[0].v_fmt.fmt.pix.sizeimage);
++ if (pad_id == IMAGE_PAD) {
++ endaddr = dmaaddr +
++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage;
++ reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
++ reg_write(&dev->cfg, UNICAM_IBEA0, endaddr);
++ } else {
++ endaddr = dmaaddr +
++ dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize;
++ reg_write(&dev->cfg, UNICAM_DBSA0, dmaaddr);
++ reg_write(&dev->cfg, UNICAM_DBEA0, endaddr);
++ }
+ }
+
+ static inline unsigned int unicam_get_lines_done(struct unicam_device *dev)
+ {
+ dma_addr_t start_addr, cur_addr;
+- unsigned int stride = dev->node[0].v_fmt.fmt.pix.bytesperline;
+- struct unicam_buffer *frm = dev->node[0].cur_frm;
++ unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline;
++ struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm;
+
+ if (!frm)
+ return 0;
+@@ -658,27 +699,51 @@ static inline unsigned int unicam_get_li
+ return (unsigned int)(cur_addr - start_addr) / stride;
+ }
+
+-static inline void unicam_schedule_next_buffer(struct unicam_device *dev)
++static inline void unicam_schedule_next_buffer(struct unicam_node *node)
+ {
+- struct unicam_dmaqueue *dma_q = &dev->node[0].dma_queue;
++ struct unicam_device *dev = node->dev;
++ struct unicam_dmaqueue *dma_q = &node->dma_queue;
+ struct unicam_buffer *buf;
+ dma_addr_t addr;
+
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+- dev->node[0].next_frm = buf;
++ node->next_frm = buf;
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+- unicam_wr_dma_addr(dev, addr);
++ unicam_wr_dma_addr(dev, addr, node->pad_id);
+ }
+
+-static inline void unicam_process_buffer_complete(struct unicam_device *dev)
++static inline void unicam_process_buffer_complete(struct unicam_node *node,
++ unsigned int sequence)
+ {
+- dev->node[0].cur_frm->vb.field = dev->node[0].m_fmt.field;
+- dev->node[0].cur_frm->vb.sequence = dev->sequence++;
++ node->cur_frm->vb.field = node->m_fmt.field;
++ node->cur_frm->vb.sequence = sequence;
++
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++ node->cur_frm = node->next_frm;
++}
+
+- vb2_buffer_done(&dev->node[0].cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+- dev->node[0].cur_frm = dev->node[0].next_frm;
++static int unicam_num_nodes_streaming(struct unicam_device *dev)
++{
++ return dev->node[IMAGE_PAD].streaming +
++ dev->node[METADATA_PAD].streaming;
++}
++
++static int unicam_all_nodes_streaming(struct unicam_device *dev)
++{
++ int ret;
++
++ ret = dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming;
++ ret &= !dev->node[METADATA_PAD].open ||
++ dev->node[METADATA_PAD].streaming;
++ return ret;
++}
++
++static int unicam_all_nodes_disabled(struct unicam_device *dev)
++{
++ return !dev->node[IMAGE_PAD].streaming &&
++ !dev->node[METADATA_PAD].streaming;
+ }
+
+ /*
+@@ -693,10 +758,12 @@ static irqreturn_t unicam_isr(int irq, v
+ {
+ struct unicam_device *unicam = (struct unicam_device *)dev;
+ struct unicam_cfg *cfg = &unicam->cfg;
+- struct unicam_dmaqueue *dma_q = &unicam->node[0].dma_queue;
+ unsigned int lines_done = unicam_get_lines_done(dev);
+ unsigned int sequence = unicam->sequence;
++ int num_nodes_streaming = unicam_num_nodes_streaming(dev);
+ int ista, sta;
++ u64 ts;
++ int i;
+
+ /*
+ * Don't service interrupts if not streaming.
+@@ -704,7 +771,7 @@ static irqreturn_t unicam_isr(int irq, v
+ * peripheral without the kernel knowing (that
+ * shouldn't happen, but causes issues if it does).
+ */
+- if (!unicam->streaming)
++ if (unicam_all_nodes_disabled(unicam))
+ return IRQ_HANDLED;
+
+ sta = reg_read(cfg, UNICAM_STA);
+@@ -726,9 +793,12 @@ static irqreturn_t unicam_isr(int irq, v
+ * Timestamp is to be when the first data byte was captured,
+ * aka frame start.
+ */
+- if (unicam->node[0].cur_frm)
+- unicam->node[0].cur_frm->vb.vb2_buf.timestamp =
+- ktime_get_ns();
++ ts = ktime_get_ns();
++ for (i = 0; i < num_nodes_streaming; i++) {
++ if (unicam->node[i].cur_frm)
++ unicam->node[i].cur_frm->vb.vb2_buf.timestamp =
++ ts;
++ }
+ }
+ if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
+ /*
+@@ -736,9 +806,13 @@ static irqreturn_t unicam_isr(int irq, v
+ * stop the peripheral. Overwrite the frame we've just
+ * captured instead.
+ */
+- if (unicam->node[0].cur_frm &&
+- unicam->node[0].cur_frm != unicam->node[0].next_frm)
+- unicam_process_buffer_complete(unicam);
++ for (i = 0; i < num_nodes_streaming; i++) {
++ if (unicam->node[i].cur_frm &&
++ unicam->node[i].cur_frm != unicam->node[i].next_frm)
++ unicam_process_buffer_complete(&unicam->node[i],
++ sequence);
++ }
++ unicam->sequence++;
+ }
+
+ /* Cannot swap buffer at frame end, there may be a race condition
+@@ -746,11 +820,13 @@ static irqreturn_t unicam_isr(int irq, v
+ * already started.
+ */
+ if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) {
+- spin_lock(&unicam->node[0].dma_queue_lock);
+- if (!list_empty(&dma_q->active) &&
+- unicam->node[0].cur_frm == unicam->node[0].next_frm)
+- unicam_schedule_next_buffer(unicam);
+- spin_unlock(&unicam->node[0].dma_queue_lock);
++ for (i = 0; i < num_nodes_streaming; i++) {
++ spin_lock(&unicam->node[i].dma_queue_lock);
++ if (!list_empty(&unicam->node[i].dma_queue.active) &&
++ unicam->node[i].cur_frm == unicam->node[i].next_frm)
++ unicam_schedule_next_buffer(&unicam->node[i]);
++ spin_unlock(&unicam->node[i].dma_queue_lock);
++ }
+ }
+
+ if (reg_read(&unicam->cfg, UNICAM_ICTL) & UNICAM_FCM) {
+@@ -773,6 +849,15 @@ static int unicam_querycap(struct file *
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev->v4l2_dev.name);
+
++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
++ V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS |
++ V4L2_CAP_META_CAPTURE;
++
++ if (node->pad_id == IMAGE_PAD)
++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
++ else
++ cap->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
++
+ return 0;
+ }
+
+@@ -787,9 +872,14 @@ static int unicam_enum_fmt_vid_cap(struc
+ int ret = 0;
+ int i;
+
++ if (node->pad_id == METADATA_PAD)
++ return -EINVAL;
++
+ for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) {
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = i;
++ mbus_code.pad = IMAGE_PAD;
++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
+ NULL, &mbus_code);
+@@ -827,6 +917,9 @@ static int unicam_g_fmt_vid_cap(struct f
+ {
+ struct unicam_node *node = video_drvdata(file);
+
++ if (node->pad_id == METADATA_PAD)
++ return -EINVAL;
++
+ *f = node->v_fmt;
+
+ return 0;
+@@ -843,6 +936,9 @@ const struct unicam_fmt *get_first_suppo
+ for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
+ memset(&mbus_code, 0, sizeof(mbus_code));
+ mbus_code.index = j;
++ mbus_code.pad = IMAGE_PAD;
++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++
+ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
+ &mbus_code);
+ if (ret < 0) {
+@@ -873,12 +969,15 @@ static int unicam_try_fmt_vid_cap(struct
+ struct unicam_device *dev = node->dev;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+- .pad = 0
++ .pad = IMAGE_PAD
+ };
+ struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ const struct unicam_fmt *fmt;
+ int ret;
+
++ if (node->pad_id == METADATA_PAD)
++ return -EINVAL;
++
+ fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
+ if (!fmt) {
+ /* Pixel format not supported by unicam. Choose the first
+@@ -983,7 +1082,7 @@ static int unicam_s_fmt_vid_cap(struct f
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+- ret = __subdev_set_format(dev, &mbus_fmt);
++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id);
+ if (ret) {
+ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n",
+ __func__, ret);
+@@ -1014,6 +1113,106 @@ static int unicam_s_fmt_vid_cap(struct f
+ return 0;
+ }
+
++static int unicam_enum_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ struct v4l2_subdev_mbus_code_enum mbus_code;
++ const struct unicam_fmt *fmt = NULL;
++ int ret = 0;
++
++ if (node->pad_id != METADATA_PAD || f->index != 0)
++ return -EINVAL;
++
++ if (dev->sensor_embedded_data) {
++ memset(&mbus_code, 0, sizeof(mbus_code));
++ mbus_code.index = f->index;
++ mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
++ mbus_code.pad = METADATA_PAD;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
++ &mbus_code);
++ if (ret < 0) {
++ unicam_dbg(2, dev,
++ "subdev->enum_mbus_code idx 0 returned %d - index invalid\n",
++ ret);
++ return -EINVAL;
++ }
++ } else {
++ mbus_code.code = MEDIA_BUS_FMT_SENSOR_DATA;
++ }
++
++ fmt = find_format_by_code(mbus_code.code);
++ if (fmt)
++ f->pixelformat = fmt->fourcc;
++
++ return 0;
++}
++
++static int unicam_g_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_node *node = video_drvdata(file);
++
++ if (node->pad_id != METADATA_PAD)
++ return -EINVAL;
++
++ *f = node->v_fmt;
++
++ return 0;
++}
++
++static int unicam_try_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_node *node = video_drvdata(file);
++
++ if (node->pad_id != METADATA_PAD)
++ return -EINVAL;
++
++ *f = node->v_fmt;
++
++ return 0;
++}
++
++static int unicam_s_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ struct v4l2_mbus_framefmt mbus_fmt = { 0 };
++ const struct unicam_fmt *fmt;
++ int ret;
++
++ if (node->pad_id == IMAGE_PAD)
++ return -EINVAL;
++
++ if (dev->sensor_embedded_data) {
++ fmt = find_format_by_pix(dev, f->fmt.meta.dataformat);
++ if (!fmt) {
++ unicam_err(dev, "unknown format: V4L2 pix 0x%08x\n",
++ f->fmt.meta.dataformat);
++ return -EINVAL;
++ }
++ mbus_fmt.code = fmt->code;
++ ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id);
++ if (ret) {
++ unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n",
++ __func__, ret);
++ return ret;
++ }
++ }
++
++ *f = node->v_fmt;
++
++ unicam_dbg(3, dev, "%s size %d, V4L2 pix 0x%08x\n",
++ __func__, node->v_fmt.fmt.meta.buffersize,
++ node->v_fmt.fmt.meta.dataformat);
++
++ return 0;
++}
++
+ static int unicam_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+@@ -1022,7 +1221,9 @@ static int unicam_queue_setup(struct vb2
+ {
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *dev = node->dev;
+- unsigned int size = node->v_fmt.fmt.pix.sizeimage;
++ unsigned int size = node->pad_id == IMAGE_PAD ?
++ node->v_fmt.fmt.pix.sizeimage :
++ node->v_fmt.fmt.meta.buffersize;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+@@ -1053,7 +1254,8 @@ static int unicam_buffer_prepare(struct
+ if (WARN_ON(!node->fmt))
+ return -EINVAL;
+
+- size = node->v_fmt.fmt.pix.sizeimage;
++ size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage :
++ node->v_fmt.fmt.meta.buffersize;
+ if (vb2_plane_size(vb, 0) < size) {
+ unicam_err(dev, "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+@@ -1082,12 +1284,12 @@ static void unicam_set_packing_config(st
+ int pack, unpack;
+ u32 val;
+
+- if (dev->node[0].v_fmt.fmt.pix.pixelformat ==
+- dev->node[0].fmt->fourcc) {
++ if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat ==
++ dev->node[IMAGE_PAD].fmt->fourcc) {
+ unpack = UNICAM_PUM_NONE;
+ pack = UNICAM_PPM_NONE;
+ } else {
+- switch (dev->node[0].fmt->depth) {
++ switch (dev->node[IMAGE_PAD].fmt->depth) {
+ case 8:
+ unpack = UNICAM_PUM_UNPACK8;
+ break;
+@@ -1125,17 +1327,31 @@ static void unicam_cfg_image_id(struct u
+ if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 mode */
+ reg_write(cfg, UNICAM_IDI0,
+- (dev->virtual_channel << 6) | dev->node[0].fmt->csi_dt);
++ (dev->virtual_channel << 6) |
++ dev->node[IMAGE_PAD].fmt->csi_dt);
+ } else {
+ /* CCP2 mode */
+- reg_write(cfg, UNICAM_IDI0, (0x80 | dev->node[0].fmt->csi_dt));
++ reg_write(cfg, UNICAM_IDI0,
++ 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt);
+ }
+ }
+
+-static void unicam_start_rx(struct unicam_device *dev, unsigned long addr)
++static void unicam_enable_ed(struct unicam_device *dev)
++{
++ struct unicam_cfg *cfg = &dev->cfg;
++ u32 val = reg_read(cfg, UNICAM_DCS);
++
++ set_field(&val, 2, UNICAM_EDL_MASK);
++ /* Do not wrap at the end of the embedded data buffer */
++ set_field(&val, 0, UNICAM_DBOB);
++
++ reg_write(cfg, UNICAM_DCS, val);
++}
++
++static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr)
+ {
+ struct unicam_cfg *cfg = &dev->cfg;
+- int line_int_freq = dev->node[0].v_fmt.fmt.pix.height >> 2;
++ int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2;
+ unsigned int i;
+ u32 val;
+
+@@ -1284,27 +1500,31 @@ static void unicam_start_rx(struct unica
+ }
+
+ reg_write(&dev->cfg, UNICAM_IBLS,
+- dev->node[0].v_fmt.fmt.pix.bytesperline);
+- unicam_wr_dma_addr(dev, addr);
++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline);
++ unicam_wr_dma_addr(dev, addr[IMAGE_PAD], IMAGE_PAD);
+ unicam_set_packing_config(dev);
+ unicam_cfg_image_id(dev);
+
+- /* Disabled embedded data */
+- val = 0;
+- set_field(&val, 0, UNICAM_EDL_MASK);
+- reg_write(cfg, UNICAM_DCS, val);
+-
+ val = reg_read(cfg, UNICAM_MISC);
+ set_field(&val, 1, UNICAM_FL0);
+ set_field(&val, 1, UNICAM_FL1);
+ reg_write(cfg, UNICAM_MISC, val);
+
++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) {
++ unicam_enable_ed(dev);
++ unicam_wr_dma_addr(dev, addr[METADATA_PAD], METADATA_PAD);
++ }
++
+ /* Enable peripheral */
+ reg_write_field(cfg, UNICAM_CTRL, 1, UNICAM_CPE);
+
+ /* Load image pointers */
+ reg_write_field(cfg, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
++ /* Load embedded data buffer pointers if needed */
++ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data)
++ reg_write_field(cfg, UNICAM_DCS, 1, UNICAM_LDP);
++
+ /*
+ * Enable trigger only for the first frame to
+ * sync correctly to the FS from the source.
+@@ -1339,6 +1559,9 @@ static void unicam_disable(struct unicam
+ /* Disable peripheral */
+ reg_write_field(cfg, UNICAM_CTRL, 0, UNICAM_CPE);
+
++ /* Clear ED setup */
++ reg_write(cfg, UNICAM_DCS, 0);
++
+ /* Disable all lane clocks */
+ clk_write(cfg, 0);
+ }
+@@ -1347,26 +1570,23 @@ static int unicam_start_streaming(struct
+ {
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *dev = node->dev;
+- struct unicam_dmaqueue *dma_q = &node->dma_queue;
+- struct unicam_buffer *buf, *tmp;
+- unsigned long addr = 0;
++ struct unicam_buffer *buf;
++ dma_addr_t buffer_addr[MAX_NODES] = { 0 };
++ int num_nodes_streaming;
+ unsigned long flags;
+- int ret;
++ int ret, i;
+
+- spin_lock_irqsave(&node->dma_queue_lock, flags);
+- buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+- node->cur_frm = buf;
+- node->next_frm = buf;
+- list_del(&buf->list);
+- spin_unlock_irqrestore(&node->dma_queue_lock, flags);
++ node->streaming = 1;
++ if (!unicam_all_nodes_streaming(dev)) {
++ unicam_dbg(3, dev, "Not all nodes are streaming yet.");
++ return 0;
++ }
+
+- addr = vb2_dma_contig_plane_dma_addr(&node->cur_frm->vb.vb2_buf, 0);
+ dev->sequence = 0;
+-
+ ret = unicam_runtime_get(dev);
+ if (ret < 0) {
+ unicam_dbg(3, dev, "unicam_runtime_get failed\n");
+- goto err_release_buffers;
++ return ret;
+ }
+
+ dev->active_data_lanes = dev->max_data_lanes;
+@@ -1388,7 +1608,7 @@ static int unicam_start_streaming(struct
+ dev->active_data_lanes = dev->max_data_lanes;
+ }
+ if (dev->active_data_lanes > dev->max_data_lanes) {
+- unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
++ unicam_err(dev, "Device has requested %u data lanes, which is >%u configured in DT\n",
+ dev->active_data_lanes, dev->max_data_lanes);
+ ret = -EINVAL;
+ goto err_pm_put;
+@@ -1408,9 +1628,22 @@ static int unicam_start_streaming(struct
+ unicam_err(dev, "Failed to enable CSI clock: %d\n", ret);
+ goto err_pm_put;
+ }
+- dev->streaming = 1;
+
+- unicam_start_rx(dev, addr);
++ num_nodes_streaming = unicam_num_nodes_streaming(dev);
++ for (i = 0; i < num_nodes_streaming; i++) {
++ spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags);
++ buf = list_entry(dev->node[i].dma_queue.active.next,
++ struct unicam_buffer, list);
++ dev->node[i].cur_frm = buf;
++ dev->node[i].next_frm = buf;
++ list_del(&buf->list);
++ spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags);
++ buffer_addr[i] =
++ vb2_dma_contig_plane_dma_addr(&dev->node[i].cur_frm->vb.vb2_buf,
++ 0);
++ }
++
++ unicam_start_rx(dev, buffer_addr);
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
+ if (ret < 0) {
+@@ -1421,21 +1654,11 @@ static int unicam_start_streaming(struct
+ return 0;
+
+ err_disable_unicam:
++ node->streaming = 0;
+ unicam_disable(dev);
+ clk_disable_unprepare(dev->clock);
+ err_pm_put:
+ unicam_runtime_put(dev);
+-err_release_buffers:
+- list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+- list_del(&buf->list);
+- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+- }
+- if (node->cur_frm != node->next_frm)
+- vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+- VB2_BUF_STATE_QUEUED);
+- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+- node->next_frm = NULL;
+- node->cur_frm = NULL;
+
+ return ret;
+ }
+@@ -1448,33 +1671,47 @@ static void unicam_stop_streaming(struct
+ struct unicam_buffer *buf, *tmp;
+ unsigned long flags;
+
+- if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
+- unicam_err(dev, "stream off failed in subdev\n");
++ node->streaming = 0;
+
+- unicam_disable(dev);
++ if (node->pad_id == IMAGE_PAD) {
++ /* Stop streaming the sensor and disable the peripheral.
++ * We cannot continue streaming embedded data with the
++ * image pad disabled.
++ */
++ if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0)
++ unicam_err(dev, "stream off failed in subdev\n");
+
+- /* Release all active buffers */
++ unicam_disable(dev);
++ clk_disable_unprepare(dev->clock);
++ unicam_runtime_put(dev);
++
++ } else if (node->pad_id == METADATA_PAD) {
++ /* Null out the embedded data buffer address so the HW does
++ * not use it. This is only really needed if the embedded data
++ * pad is disabled before the image pad. The 0x3 in the top two
++ * bits signifies uncached accesses through the Videocore
++ * memory controller.
++ */
++ unicam_wr_dma_addr(dev, 0xc0000000, METADATA_PAD);
++ }
++
++ /* Clear all queued buffers for the node */
+ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &dma_q->active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+
+- if (node->cur_frm == node->next_frm) {
+- vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
+- VB2_BUF_STATE_ERROR);
+- } else {
++ if (node->cur_frm)
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
++ if (node->next_frm && node->cur_frm != node->next_frm)
+ vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+ VB2_BUF_STATE_ERROR);
+- }
++
+ node->cur_frm = NULL;
+ node->next_frm = NULL;
+ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+-
+- clk_disable_unprepare(dev->clock);
+- unicam_runtime_put(dev);
+ }
+
+ static int unicam_enum_input(struct file *file, void *priv,
+@@ -1595,17 +1832,23 @@ static int unicam_enum_framesizes(struct
+ struct v4l2_subdev_frame_size_enum fse;
+ int ret;
+
+- /* check for valid format */
+- fmt = find_format_by_pix(dev, fsize->pixel_format);
+- if (!fmt) {
+- unicam_dbg(3, dev, "Invalid pixel code: %x\n",
+- fsize->pixel_format);
+- return -EINVAL;
++ if (node->pad_id == IMAGE_PAD) {
++ /* check for valid format */
++ fmt = find_format_by_pix(dev, fsize->pixel_format);
++ if (!fmt) {
++ unicam_dbg(3, dev, "Invalid pixel code: %x\n",
++ fsize->pixel_format);
++ return -EINVAL;
++ }
++ fse.code = fmt->code;
++ } else {
++ /* This pad is for embedded data, so just set the format */
++ fse.code = MEDIA_BUS_FMT_SENSOR_DATA;
+ }
+
++ fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fse.index = fsize->index;
+- fse.pad = 0;
+- fse.code = fmt->code;
++ fse.pad = node->pad_id;
+
+ ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+@@ -1782,7 +2025,7 @@ static void unicam_notify(struct v4l2_su
+
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+- v4l2_event_queue(&dev->node[0].video_dev, arg);
++ v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg);
+ break;
+ default:
+ break;
+@@ -1826,6 +2069,7 @@ static int unicam_open(struct file *file
+ goto unlock;
+ }
+
++ node->open++;
+ ret = 0;
+
+ unlock:
+@@ -1850,6 +2094,10 @@ static int unicam_release(struct file *f
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
++ if (node->streaming)
++ unicam_stop_streaming(&node->buffer_queue);
++
++ node->open--;
+ mutex_unlock(&node->lock);
+
+ return ret;
+@@ -1874,6 +2122,11 @@ static const struct v4l2_ioctl_ops unica
+ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap,
+
++ .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap,
++ .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap,
++ .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta_cap,
++ .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta_cap,
++
+ .vidioc_enum_input = unicam_enum_input,
+ .vidioc_g_input = unicam_g_input,
+ .vidioc_s_input = unicam_s_input,
+@@ -1941,42 +2194,53 @@ static int register_node(struct unicam_d
+ const struct unicam_fmt *fmt;
+ int ret;
+
+- ret = __subdev_get_format(unicam, &mbus_fmt, pad_id);
+- if (ret) {
+- unicam_err(unicam, "Failed to get_format - ret %d\n", ret);
+- return ret;
+- }
+-
+- fmt = find_format_by_code(mbus_fmt.code);
+- if (!fmt) {
+- /* Find the first format that the sensor and unicam both
+- * support
+- */
+- fmt = get_first_supported_format(unicam);
++ if (unicam->sensor_embedded_data || pad_id != METADATA_PAD) {
++ ret = __subdev_get_format(unicam, &mbus_fmt, pad_id);
++ if (ret) {
++ unicam_err(unicam, "Failed to get_format - ret %d\n",
++ ret);
++ return ret;
++ }
+
+- if (!fmt)
+- /* No compatible formats */
+- return -EINVAL;
++ fmt = find_format_by_code(mbus_fmt.code);
++ if (!fmt) {
++ /* Find the first format that the sensor and unicam both
++ * support
++ */
++ fmt = get_first_supported_format(unicam);
+
+- mbus_fmt.code = fmt->code;
+- ret = __subdev_set_format(unicam, &mbus_fmt);
+- if (ret)
+- return -EINVAL;
+- }
+- if (mbus_fmt.field != V4L2_FIELD_NONE) {
+- /* Interlaced not supported - disable it now. */
+- mbus_fmt.field = V4L2_FIELD_NONE;
+- ret = __subdev_set_format(unicam, &mbus_fmt);
+- if (ret)
+- return -EINVAL;
++ if (!fmt)
++ /* No compatible formats */
++ return -EINVAL;
++
++ mbus_fmt.code = fmt->code;
++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
++ if (ret)
++ return -EINVAL;
++ }
++ if (mbus_fmt.field != V4L2_FIELD_NONE) {
++ /* Interlaced not supported - disable it now. */
++ mbus_fmt.field = V4L2_FIELD_NONE;
++ ret = __subdev_set_format(unicam, &mbus_fmt, pad_id);
++ if (ret)
++ return -EINVAL;
++ }
++ } else {
++ /* Fix this node format as embedded data. */
++ fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA);
+ }
+
++ node->dev = unicam;
+ node->pad_id = pad_id;
+ node->fmt = fmt;
+- if (fmt->fourcc)
+- node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+- else
++ if (fmt->fourcc) {
++ if (fmt->fourcc != V4L2_META_FMT_SENSOR_DATA)
++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
++ else
++ node->v_fmt.fmt.meta.dataformat = fmt->fourcc;
++ } else {
+ node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
++ }
+
+ /* Read current subdev format */
+ unicam_reset_format(node);
+@@ -2002,13 +2266,21 @@ static int register_node(struct unicam_d
+ spin_lock_init(&node->dma_queue_lock);
+ mutex_init(&node->lock);
+
+- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ vdev = &node->video_dev;
++ if (pad_id == IMAGE_PAD) {
+ /* Add controls from the subdevice */
+ ret = v4l2_ctrl_add_handler(&node->ctrl_handler,
+ unicam->sensor->ctrl_handler, NULL,
+ true);
+ if (ret < 0)
+ return ret;
++
++ /*
++ * If the sensor subdevice has any controls, associate the node
++ * with the ctrl handler to allow access from userland.
++ */
++ if (!list_empty(&node->ctrl_handler.ctrls))
++ vdev->ctrl_handler = &node->ctrl_handler;
+ }
+
+ q = &node->buffer_queue;
+@@ -2031,8 +2303,6 @@ static int register_node(struct unicam_d
+
+ INIT_LIST_HEAD(&node->dma_queue.active);
+
+- vdev = &node->video_dev;
+- strlcpy(vdev->name, UNICAM_MODULE_NAME, sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &unicam_fops;
+ vdev->ioctl_ops = &unicam_ioctl_ops;
+@@ -2040,24 +2310,28 @@ static int register_node(struct unicam_d
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &node->lock;
+- vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+- V4L2_CAP_READWRITE;
+- /* If the source has no controls then remove our ctrl handler. */
+- if (list_empty(&node->ctrl_handler.ctrls))
+- unicam->v4l2_dev.ctrl_handler = NULL;
++ vdev->device_caps = (pad_id == IMAGE_PAD) ?
++ (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING) :
++ (V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING);
++
++ /* Define the device names */
++ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME,
++ node->pad_id == IMAGE_PAD ? "image" : "embedded");
+
+- node->dev = unicam;
+ video_set_drvdata(vdev, node);
+ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+- if (!v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD);
+ }
+- if (!v4l2_subdev_has_op(unicam->sensor, video, querystd))
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, video, querystd))
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD);
+- if (!v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) {
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP);
+@@ -2066,15 +2340,19 @@ static int register_node(struct unicam_d
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS);
+ }
+- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval))
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval))
+ v4l2_disable_ioctl(&node->video_dev,
+ VIDIOC_ENUM_FRAMEINTERVALS);
+- if (!v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval))
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval))
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM);
+- if (!v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval))
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval))
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM);
+
+- if (!v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+@@ -2082,27 +2360,29 @@ static int register_node(struct unicam_d
+ unicam_err(unicam, "Unable to register video device.\n");
+ return ret;
+ }
+- node->registered = true;
++ node->registered = 1;
+
+- ret = media_create_pad_link(&unicam->sensor->entity,
+- 0, &node->video_dev.entity, 0,
+- MEDIA_LNK_FL_ENABLED |
+- MEDIA_LNK_FL_IMMUTABLE);
+- if (ret)
+- unicam_err(unicam, "Unable to create pad links.\n");
++ if (unicam->sensor_embedded_data) {
++ ret = media_create_pad_link(&unicam->sensor->entity, pad_id,
++ &node->video_dev.entity, 0,
++ MEDIA_LNK_FL_ENABLED |
++ MEDIA_LNK_FL_IMMUTABLE);
++ if (ret)
++ unicam_err(unicam, "Unable to create pad links.\n");
++ }
+
+ return ret;
+ }
+
+ static void unregister_nodes(struct unicam_device *unicam)
+ {
+- if (unicam->node[0].registered) {
+- video_unregister_device(&unicam->node[0].video_dev);
+- unicam->node[0].registered = false;
+- }
+- if (unicam->node[1].registered) {
+- video_unregister_device(&unicam->node[1].video_dev);
+- unicam->node[1].registered = false;
++ if (unicam->node[IMAGE_PAD].registered) {
++ video_unregister_device(&unicam->node[IMAGE_PAD].video_dev);
++ unicam->node[IMAGE_PAD].registered = 0;
++ }
++ if (unicam->node[METADATA_PAD].registered) {
++ video_unregister_device(&unicam->node[METADATA_PAD].video_dev);
++ unicam->node[METADATA_PAD].registered = 0;
+ }
+ }
+
+@@ -2118,20 +2398,20 @@ static int unicam_probe_complete(struct
+ if (!unicam->sensor_config)
+ return -ENOMEM;
+
+- ret = register_node(unicam, &unicam->node[0],
+- V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
++ unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2);
++
++ ret = register_node(unicam, &unicam->node[IMAGE_PAD],
++ V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD);
+ if (ret) {
+ unicam_err(unicam, "Unable to register subdev node 0.\n");
+ goto unregister;
+ }
+- if (unicam->sensor->entity.num_pads >= 2) {
+- ret = register_node(unicam, &unicam->node[1],
+- V4L2_BUF_TYPE_META_CAPTURE, 1);
+- if (ret) {
+- unicam_err(unicam,
+- "Unable to register subdev node 1.\n");
+- goto unregister;
+- }
++
++ ret = register_node(unicam, &unicam->node[METADATA_PAD],
++ V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD);
++ if (ret) {
++ unicam_err(unicam, "Unable to register subdev node 1.\n");
++ goto unregister;
+ }
+
+ ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev);
+@@ -2355,8 +2635,10 @@ static int unicam_probe(struct platform_
+ pdev->dev.driver->name, dev_name(&pdev->dev));
+ unicam->mdev.hw_revision = 1;
+
+- media_entity_pads_init(&unicam->node[0].video_dev.entity, 1,
+- &unicam->node[0].pad);
++ media_entity_pads_init(&unicam->node[IMAGE_PAD].video_dev.entity, 1,
++ &unicam->node[IMAGE_PAD].pad);
++ media_entity_pads_init(&unicam->node[METADATA_PAD].video_dev.entity, 1,
++ &unicam->node[METADATA_PAD].pad);
+ media_device_init(&unicam->mdev);
+
+ unicam->v4l2_dev.mdev = &unicam->mdev;
+@@ -2376,11 +2658,10 @@ static int unicam_probe(struct platform_
+ }
+
+ /* Reserve space for the controls */
+- hdl = &unicam->node[0].ctrl_handler;
++ hdl = &unicam->node[IMAGE_PAD].ctrl_handler;
+ ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret < 0)
+ goto media_unregister;
+- unicam->v4l2_dev.ctrl_handler = hdl;
+
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, unicam);
+@@ -2417,7 +2698,7 @@ static int unicam_remove(struct platform
+ pm_runtime_disable(&pdev->dev);
+
+ v4l2_async_notifier_unregister(&unicam->notifier);
+- v4l2_ctrl_handler_free(&unicam->node[0].ctrl_handler);
++ v4l2_ctrl_handler_free(&unicam->node[IMAGE_PAD].ctrl_handler);
+ v4l2_device_unregister(&unicam->v4l2_dev);
+ unregister_nodes(unicam);
+ if (unicam->sensor_config)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch b/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch
new file mode 100644
index 0000000000..836ced8a04
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch
@@ -0,0 +1,308 @@
+From 0eb6753788616ffed17a0484a14fd7d3df2a2a05 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 2 Apr 2020 16:08:51 +0100
+Subject: [PATCH] media: bcm2835-unicam: Use dummy buffer if none have
+ been queued
+
+If no buffer has been queued by a userland application, we use an
+internal dummy buffer for the hardware to spin in. This will allow
+the driver to release the existing userland buffer back to the
+application for processing.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 160 ++++++++++++------
+ 1 file changed, 110 insertions(+), 50 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -47,6 +47,7 @@
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
++#include <linux/dma-mapping.h>
+ #include <linux/err.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+@@ -112,6 +113,12 @@ MODULE_PARM_DESC(debug, "Debug level 0-3
+ /* Default size of the embedded buffer */
+ #define UNICAM_EMBEDDED_SIZE 8192
+
++/*
++ * Size of the dummy buffer. Can be any size really, but the DMA
++ * allocation works in units of page sizes.
++ */
++#define DUMMY_BUF_SIZE (PAGE_SIZE)
++
+ enum pad_types {
+ IMAGE_PAD,
+ METADATA_PAD,
+@@ -390,6 +397,12 @@ struct unicam_node {
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ unsigned int embedded_lines;
++ /*
++ * Dummy buffer intended to be used by unicam
++ * if we have no other queued buffers to swap to.
++ */
++ void *dummy_buf_cpu_addr;
++ dma_addr_t dummy_buf_dma_addr;
+ };
+
+ struct unicam_device {
+@@ -661,27 +674,24 @@ static int unicam_reset_format(struct un
+ return 0;
+ }
+
+-static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr,
+- int pad_id)
++static void unicam_wr_dma_addr(struct unicam_cfg *cfg, dma_addr_t dmaaddr,
++ unsigned int buffer_size, int pad_id)
+ {
+- dma_addr_t endaddr;
++ dma_addr_t endaddr = dmaaddr + buffer_size;
+
+ /*
+- * dmaaddr should be a 32-bit address with the top two bits set to 0x3
+- * to signify uncached access through the Videocore memory controller.
++ * dmaaddr and endaddr should be a 32-bit address with the top two bits
++ * set to 0x3 to signify uncached access through the Videocore memory
++ * controller.
+ */
+- BUG_ON((dmaaddr >> 30) != 0x3);
++ BUG_ON((dmaaddr >> 30) != 0x3 && (endaddr >> 30) != 0x3);
+
+ if (pad_id == IMAGE_PAD) {
+- endaddr = dmaaddr +
+- dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage;
+- reg_write(&dev->cfg, UNICAM_IBSA0, dmaaddr);
+- reg_write(&dev->cfg, UNICAM_IBEA0, endaddr);
++ reg_write(cfg, UNICAM_IBSA0, dmaaddr);
++ reg_write(cfg, UNICAM_IBEA0, endaddr);
+ } else {
+- endaddr = dmaaddr +
+- dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize;
+- reg_write(&dev->cfg, UNICAM_DBSA0, dmaaddr);
+- reg_write(&dev->cfg, UNICAM_DBEA0, endaddr);
++ reg_write(cfg, UNICAM_DBSA0, dmaaddr);
++ reg_write(cfg, UNICAM_DBEA0, endaddr);
+ }
+ }
+
+@@ -704,6 +714,7 @@ static inline void unicam_schedule_next_
+ struct unicam_device *dev = node->dev;
+ struct unicam_dmaqueue *dma_q = &node->dma_queue;
+ struct unicam_buffer *buf;
++ unsigned int size;
+ dma_addr_t addr;
+
+ buf = list_entry(dma_q->active.next, struct unicam_buffer, list);
+@@ -711,7 +722,23 @@ static inline void unicam_schedule_next_
+ list_del(&buf->list);
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+- unicam_wr_dma_addr(dev, addr, node->pad_id);
++ size = (node->pad_id == IMAGE_PAD) ?
++ dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage :
++ dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize;
++
++ unicam_wr_dma_addr(&dev->cfg, addr, size, node->pad_id);
++}
++
++static inline void unicam_schedule_dummy_buffer(struct unicam_node *node)
++{
++ struct unicam_device *dev = node->dev;
++ dma_addr_t addr = node->dummy_buf_dma_addr;
++
++ unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n",
++ node->pad_id);
++
++ unicam_wr_dma_addr(&dev->cfg, addr, DUMMY_BUF_SIZE, node->pad_id);
++ node->next_frm = NULL;
+ }
+
+ static inline void unicam_process_buffer_complete(struct unicam_node *node,
+@@ -721,7 +748,6 @@ static inline void unicam_process_buffer
+ node->cur_frm->vb.sequence = sequence;
+
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+- node->cur_frm = node->next_frm;
+ }
+
+ static int unicam_num_nodes_streaming(struct unicam_device *dev)
+@@ -788,6 +814,28 @@ static irqreturn_t unicam_isr(int irq, v
+ if (!(sta && (UNICAM_IS | UNICAM_PI0)))
+ return IRQ_HANDLED;
+
++ /*
++ * We must run the frame end handler first. If we have a valid next_frm
++ * and we get a simultaneout FE + FS interrupt, running the FS handler
++ * first would null out the next_frm ptr and we would have lost the
++ * buffer forever.
++ */
++ if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
++ /*
++ * Ensure we have swapped buffers already as we can't
++ * stop the peripheral. If no buffer is available, use a
++ * dummy buffer to dump out frames until we get a new buffer
++ * to use.
++ */
++ for (i = 0; i < num_nodes_streaming; i++) {
++ if (unicam->node[i].cur_frm)
++ unicam_process_buffer_complete(&unicam->node[i],
++ sequence);
++ unicam->node[i].cur_frm = unicam->node[i].next_frm;
++ }
++ unicam->sequence++;
++ }
++
+ if (ista & UNICAM_FSI) {
+ /*
+ * Timestamp is to be when the first data byte was captured,
+@@ -798,24 +846,16 @@ static irqreturn_t unicam_isr(int irq, v
+ if (unicam->node[i].cur_frm)
+ unicam->node[i].cur_frm->vb.vb2_buf.timestamp =
+ ts;
++ /*
++ * Set the next frame output to go to a dummy frame
++ * if we have not managed to obtain another frame
++ * from the queue.
++ */
++ unicam_schedule_dummy_buffer(&unicam->node[i]);
+ }
+ }
+- if (ista & UNICAM_FEI || sta & UNICAM_PI0) {
+- /*
+- * Ensure we have swapped buffers already as we can't
+- * stop the peripheral. Overwrite the frame we've just
+- * captured instead.
+- */
+- for (i = 0; i < num_nodes_streaming; i++) {
+- if (unicam->node[i].cur_frm &&
+- unicam->node[i].cur_frm != unicam->node[i].next_frm)
+- unicam_process_buffer_complete(&unicam->node[i],
+- sequence);
+- }
+- unicam->sequence++;
+- }
+-
+- /* Cannot swap buffer at frame end, there may be a race condition
++ /*
++ * Cannot swap buffer at frame end, there may be a race condition
+ * where the HW does not actually swap it if the new frame has
+ * already started.
+ */
+@@ -823,7 +863,7 @@ static irqreturn_t unicam_isr(int irq, v
+ for (i = 0; i < num_nodes_streaming; i++) {
+ spin_lock(&unicam->node[i].dma_queue_lock);
+ if (!list_empty(&unicam->node[i].dma_queue.active) &&
+- unicam->node[i].cur_frm == unicam->node[i].next_frm)
++ !unicam->node[i].next_frm)
+ unicam_schedule_next_buffer(&unicam->node[i]);
+ spin_unlock(&unicam->node[i].dma_queue_lock);
+ }
+@@ -1352,7 +1392,7 @@ static void unicam_start_rx(struct unica
+ {
+ struct unicam_cfg *cfg = &dev->cfg;
+ int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2;
+- unsigned int i;
++ unsigned int size, i;
+ u32 val;
+
+ if (line_int_freq < 128)
+@@ -1413,7 +1453,7 @@ static void unicam_start_rx(struct unica
+ reg_write_field(cfg, UNICAM_ANA, 0, UNICAM_DDL);
+
+ /* Always start in trigger frame capture mode (UNICAM_FCM set) */
+- val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM;
++ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB;
+ set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
+ reg_write(cfg, UNICAM_ICTL, val);
+ reg_write(cfg, UNICAM_STA, UNICAM_STA_MASK_ALL);
+@@ -1501,7 +1541,8 @@ static void unicam_start_rx(struct unica
+
+ reg_write(&dev->cfg, UNICAM_IBLS,
+ dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline);
+- unicam_wr_dma_addr(dev, addr[IMAGE_PAD], IMAGE_PAD);
++ size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage;
++ unicam_wr_dma_addr(&dev->cfg, addr[IMAGE_PAD], size, IMAGE_PAD);
+ unicam_set_packing_config(dev);
+ unicam_cfg_image_id(dev);
+
+@@ -1511,8 +1552,10 @@ static void unicam_start_rx(struct unica
+ reg_write(cfg, UNICAM_MISC, val);
+
+ if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) {
++ size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize;
+ unicam_enable_ed(dev);
+- unicam_wr_dma_addr(dev, addr[METADATA_PAD], METADATA_PAD);
++ unicam_wr_dma_addr(&dev->cfg, addr[METADATA_PAD], size,
++ METADATA_PAD);
+ }
+
+ /* Enable peripheral */
+@@ -1686,13 +1729,14 @@ static void unicam_stop_streaming(struct
+ unicam_runtime_put(dev);
+
+ } else if (node->pad_id == METADATA_PAD) {
+- /* Null out the embedded data buffer address so the HW does
+- * not use it. This is only really needed if the embedded data
+- * pad is disabled before the image pad. The 0x3 in the top two
+- * bits signifies uncached accesses through the Videocore
+- * memory controller.
++ /* Allow the hardware to spin in the dummy buffer.
++ * This is only really needed if the embedded data pad is
++ * disabled before the image pad. The 0x3 in the top two bits
++ * signifies uncached accesses through the Videocore memory
++ * controller.
+ */
+- unicam_wr_dma_addr(dev, 0xc0000000, METADATA_PAD);
++ unicam_wr_dma_addr(&dev->cfg, node->dummy_buf_dma_addr,
++ DUMMY_BUF_SIZE, METADATA_PAD);
+ }
+
+ /* Clear all queued buffers for the node */
+@@ -2321,6 +2365,15 @@ static int register_node(struct unicam_d
+ video_set_drvdata(vdev, node);
+ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
++ node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev,
++ DUMMY_BUF_SIZE,
++ &node->dummy_buf_dma_addr,
++ GFP_ATOMIC);
++ if (!node->dummy_buf_cpu_addr) {
++ unicam_err(unicam, "Unable to allocate dummy buffer.\n");
++ return -ENOMEM;
++ }
++
+ if (node->pad_id == METADATA_PAD ||
+ !v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
+@@ -2376,13 +2429,20 @@ static int register_node(struct unicam_d
+
+ static void unregister_nodes(struct unicam_device *unicam)
+ {
+- if (unicam->node[IMAGE_PAD].registered) {
+- video_unregister_device(&unicam->node[IMAGE_PAD].video_dev);
+- unicam->node[IMAGE_PAD].registered = 0;
+- }
+- if (unicam->node[METADATA_PAD].registered) {
+- video_unregister_device(&unicam->node[METADATA_PAD].video_dev);
+- unicam->node[METADATA_PAD].registered = 0;
++ struct unicam_node *node;
++ int i;
++
++ for (i = 0; i < MAX_NODES; i++) {
++ node = &unicam->node[i];
++ if (node->dummy_buf_cpu_addr) {
++ dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE,
++ node->dummy_buf_cpu_addr,
++ node->dummy_buf_dma_addr);
++ }
++ if (node->registered) {
++ video_unregister_device(&node->video_dev);
++ node->registered = 0;
++ }
+ }
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch b/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch
new file mode 100644
index 0000000000..43d10486de
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch
@@ -0,0 +1,48 @@
+From 5f2eface651ba5da9caaa84ccca14b9202ba6202 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 17 Apr 2020 10:46:19 +0100
+Subject: [PATCH] spi: Force CS_HIGH if GPIO descriptors are used
+
+Commit f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs")
+amended of_spi_parse_dt() to always set SPI_CS_HIGH for SPI slaves whose
+Chip Select is defined by a "cs-gpios" devicetree property.
+
+This change breaks drivers whose probe functions set the mode field of
+the spi_device because in doing so they clear the SPI_CS_HIGH flag.
+
+Fix by setting SPI_CS_HIGH in spi_setup (under the same conditions as
+in of_spi_parse_dt()).
+
+See also: 83b2a8fe43bd ("spi: spidev: Fix CS polarity if GPIO descriptors are used")
+
+Fixes: f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs")
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -3032,6 +3032,7 @@ static int __spi_validate_bits_per_word(
+ */
+ int spi_setup(struct spi_device *spi)
+ {
++ struct spi_controller *ctlr = spi->controller;
+ unsigned bad_bits, ugly_bits;
+ int status;
+
+@@ -3049,6 +3050,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;
++
++ if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
++ ctlr->cs_gpiods[spi->chip_select] && !(spi->mode & SPI_CS_HIGH)) {
++ dev_warn(&spi->dev,
++ "setup: forcing CS_HIGH (use_gpio_descriptors)\n");
++ spi->mode |= SPI_CS_HIGH;
++ }
++
+ /* help drivers fail *cleanly* when they need options
+ * that aren't supported with their current controller
+ * SPI_CS_WORD has a fallback software implementation,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch b/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch
new file mode 100644
index 0000000000..b85d917878
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch
@@ -0,0 +1,55 @@
+From 0fec1c81707f5335d0b04b5e97d2ddd0b902377b Mon Sep 17 00:00:00 2001
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Date: Tue, 10 Mar 2020 14:17:07 +0100
+Subject: [PATCH] media: i2c: imx219: Fix power sequence
+
+Commit ca45448a56659c6df6e0436188e97f6cc65dea8a upstream.
+
+When supporting Rpi Camera v2 Module on the RZ/G2E, found the driver had
+some issues with rcar mipi-csi driver. The sensor never entered into LP-11
+state.
+
+The powerup sequence in the datasheet[1] shows the sensor entering into
+LP-11 in streaming mode, so to fix this issue transitions are performed
+from "streaming -> standby" in the probe() after power up.
+
+With this commit the sensor is able to enter LP-11 mode during power up,
+as expected by some CSI-2 controllers.
+
+[1] https://publiclab.org/system/images/photos/000/023/294/original/
+RASPBERRY_PI_CAMERA_V2_DATASHEET_IMX219PQH5_7.0.0_Datasheet_XXX.PDF
+
+Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Acked-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ drivers/media/i2c/imx219.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -1224,6 +1224,23 @@ static int imx219_probe(struct i2c_clien
+ /* Set default mode to max resolution */
+ imx219->mode = &supported_modes[0];
+
++ /* sensor doesn't enter LP-11 state upon power up until and unless
++ * streaming is started, so upon power up switch the modes to:
++ * streaming -> standby
++ */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++ if (ret < 0)
++ goto error_power_off;
++ usleep_range(100, 110);
++
++ /* put sensor back to standby mode */
++ ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++ IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++ if (ret < 0)
++ goto error_power_off;
++ usleep_range(100, 110);
++
+ ret = imx219_init_controls(imx219);
+ if (ret)
+ goto error_power_off;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch b/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch
new file mode 100644
index 0000000000..552516bd67
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch
@@ -0,0 +1,319 @@
+From 1f00b84d993e1f8de17ef936e00f4264266cb5d1 Mon Sep 17 00:00:00 2001
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Date: Tue, 10 Mar 2020 14:17:08 +0100
+Subject: [PATCH] media: i2c: imx219: Add support for RAW8 bit bayer
+ format
+
+Commit 22da1d56e982151e0bdfafe9de6fe94098a51356 upstream.
+
+IMX219 sensor is capable for RAW8/RAW10 modes. This commit adds support
+for RAW8 bayer format.
+
+Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ drivers/media/i2c/imx219.c | 148 +++++++++++++++++++++++++++++--------
+ 1 file changed, 116 insertions(+), 32 deletions(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -168,15 +168,12 @@ static const struct imx219_reg mode_3280
+ {0x0171, 0x01},
+ {0x0174, 0x00},
+ {0x0175, 0x00},
+- {0x018c, 0x0a},
+- {0x018d, 0x0a},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x03},
+ {0x0305, 0x03},
+ {0x0306, 0x00},
+ {0x0307, 0x39},
+- {0x0309, 0x0a},
+ {0x030b, 0x01},
+ {0x030c, 0x00},
+ {0x030d, 0x72},
+@@ -230,15 +227,12 @@ static const struct imx219_reg mode_1920
+ {0x0171, 0x01},
+ {0x0174, 0x00},
+ {0x0175, 0x00},
+- {0x018c, 0x0a},
+- {0x018d, 0x0a},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x03},
+ {0x0305, 0x03},
+ {0x0306, 0x00},
+ {0x0307, 0x39},
+- {0x0309, 0x0a},
+ {0x030b, 0x01},
+ {0x030c, 0x00},
+ {0x030d, 0x72},
+@@ -290,15 +284,12 @@ static const struct imx219_reg mode_1640
+ {0x0171, 0x01},
+ {0x0174, 0x01},
+ {0x0175, 0x01},
+- {0x018c, 0x0a},
+- {0x018d, 0x0a},
+ {0x0301, 0x05},
+ {0x0303, 0x01},
+ {0x0304, 0x03},
+ {0x0305, 0x03},
+ {0x0306, 0x00},
+ {0x0307, 0x39},
+- {0x0309, 0x0a},
+ {0x030b, 0x01},
+ {0x030c, 0x00},
+ {0x030d, 0x72},
+@@ -322,6 +313,18 @@ static const struct imx219_reg mode_1640
+ {0x0163, 0x78},
+ };
+
++static const struct imx219_reg raw8_framefmt_regs[] = {
++ {0x018c, 0x08},
++ {0x018d, 0x08},
++ {0x0309, 0x08},
++};
++
++static const struct imx219_reg raw10_framefmt_regs[] = {
++ {0x018c, 0x0a},
++ {0x018d, 0x0a},
++ {0x0309, 0x0a},
++};
++
+ static const char * const imx219_test_pattern_menu[] = {
+ "Disabled",
+ "Color Bars",
+@@ -349,6 +352,27 @@ static const char * const imx219_supply_
+ #define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
+
+ /*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++
++ MEDIA_BUS_FMT_SRGGB8_1X8,
++ MEDIA_BUS_FMT_SGRBG8_1X8,
++ MEDIA_BUS_FMT_SGBRG8_1X8,
++ MEDIA_BUS_FMT_SBGGR8_1X8,
++};
++
++/*
+ * Initialisation delay between XCLR low->high and the moment when the sensor
+ * can start capture (i.e. can leave software stanby) must be not less than:
+ * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
+@@ -413,6 +437,8 @@ struct imx219 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
++ struct v4l2_mbus_framefmt fmt;
++
+ struct clk *xclk; /* system clock to IMX219 */
+ u32 xclk_freq;
+
+@@ -519,19 +545,40 @@ static int imx219_write_regs(struct imx2
+ }
+
+ /* Get bayer order based on flip setting. */
+-static u32 imx219_get_format_code(struct imx219 *imx219)
++static u32 imx219_get_format_code(struct imx219 *imx219, u32 code)
+ {
+- /*
+- * Only one bayer order is supported.
+- * It depends on the flip settings.
+- */
+- static const u32 codes[2][2] = {
+- { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
+- { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
+- };
++ unsigned int i;
+
+ lockdep_assert_held(&imx219->mutex);
+- return codes[imx219->vflip->val][imx219->hflip->val];
++
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == code)
++ break;
++
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
++ i = (i & ~3) | (imx219->vflip->val ? 2 : 0) |
++ (imx219->hflip->val ? 1 : 0);
++
++ return codes[i];
++}
++
++static void imx219_set_default_format(struct imx219 *imx219)
++{
++ struct v4l2_mbus_framefmt *fmt;
++
++ fmt = &imx219->fmt;
++ fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = supported_modes[0].width;
++ fmt->height = supported_modes[0].height;
++ fmt->field = V4L2_FIELD_NONE;
+ }
+
+ static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+@@ -545,7 +592,8 @@ static int imx219_open(struct v4l2_subde
+ /* Initialize try_fmt */
+ try_fmt->width = supported_modes[0].width;
+ try_fmt->height = supported_modes[0].height;
+- try_fmt->code = imx219_get_format_code(imx219);
++ try_fmt->code = imx219_get_format_code(imx219,
++ MEDIA_BUS_FMT_SRGGB10_1X10);
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&imx219->mutex);
+@@ -648,14 +696,10 @@ static int imx219_enum_mbus_code(struct
+ {
+ struct imx219 *imx219 = to_imx219(sd);
+
+- /*
+- * Only one bayer order is supported (though it depends on the flip
+- * settings)
+- */
+- if (code->index > 0)
++ if (code->index >= (ARRAY_SIZE(codes) / 4))
+ return -EINVAL;
+
+- code->code = imx219_get_format_code(imx219);
++ code->code = imx219_get_format_code(imx219, codes[code->index * 4]);
+
+ return 0;
+ }
+@@ -669,7 +713,7 @@ static int imx219_enum_frame_size(struct
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+- if (fse->code != imx219_get_format_code(imx219))
++ if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code))
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+@@ -696,9 +740,7 @@ static void imx219_update_pad_format(str
+ {
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+- fmt->format.code = imx219_get_format_code(imx219);
+ fmt->format.field = V4L2_FIELD_NONE;
+-
+ imx219_reset_colorspace(&fmt->format);
+ }
+
+@@ -710,10 +752,12 @@ static int __imx219_get_pad_format(struc
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
+ /* update the code which could change due to vflip or hflip: */
+- try_fmt->code = imx219_get_format_code(imx219);
++ try_fmt->code = imx219_get_format_code(imx219, try_fmt->code);
+ fmt->format = *try_fmt;
+ } else {
+ imx219_update_pad_format(imx219, imx219->mode, fmt);
++ fmt->format.code = imx219_get_format_code(imx219,
++ imx219->fmt.code);
+ }
+
+ return 0;
+@@ -741,11 +785,18 @@ static int imx219_set_pad_format(struct
+ const struct imx219_mode *mode;
+ struct v4l2_mbus_framefmt *framefmt;
+ int exposure_max, exposure_def, hblank;
++ unsigned int i;
+
+ mutex_lock(&imx219->mutex);
+
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == fmt->format.code)
++ break;
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
+ /* Bayer order varies with flips */
+- fmt->format.code = imx219_get_format_code(imx219);
++ fmt->format.code = imx219_get_format_code(imx219, codes[i]);
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+@@ -755,7 +806,9 @@ static int imx219_set_pad_format(struct
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ *framefmt = fmt->format;
+- } else if (imx219->mode != mode) {
++ } else if (imx219->mode != mode ||
++ imx219->fmt.code != fmt->format.code) {
++ imx219->fmt = fmt->format;
+ imx219->mode = mode;
+ /* Update limits and set FPS to default */
+ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
+@@ -786,6 +839,27 @@ static int imx219_set_pad_format(struct
+ return 0;
+ }
+
++static int imx219_set_framefmt(struct imx219 *imx219)
++{
++ switch (imx219->fmt.code) {
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ return imx219_write_regs(imx219, raw8_framefmt_regs,
++ ARRAY_SIZE(raw8_framefmt_regs));
++
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ return imx219_write_regs(imx219, raw10_framefmt_regs,
++ ARRAY_SIZE(raw10_framefmt_regs));
++ }
++
++ return -EINVAL;
++}
++
+ static int imx219_start_streaming(struct imx219 *imx219)
+ {
+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+@@ -800,6 +874,13 @@ static int imx219_start_streaming(struct
+ return ret;
+ }
+
++ ret = imx219_set_framefmt(imx219);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set frame format: %d\n",
++ __func__, ret);
++ return ret;
++ }
++
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
+ if (ret)
+@@ -1253,6 +1334,9 @@ static int imx219_probe(struct i2c_clien
+ /* Initialize source pad */
+ imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
+
++ /* Initialize default format */
++ imx219_set_default_format(imx219);
++
+ ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
+ if (ret) {
+ dev_err(dev, "failed to init entity pads: %d\n", ret);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch b/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch
new file mode 100644
index 0000000000..3ad377dfbe
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch
@@ -0,0 +1,118 @@
+From d8b1f6de10d7bd64d73b1c3099ad5e69e6fd7d9b Mon Sep 17 00:00:00 2001
+From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Date: Tue, 10 Mar 2020 14:17:09 +0100
+Subject: [PATCH] media: i2c: imx219: Add support for cropped 640x480
+ resolution
+
+Commit 25130b8ad409d5532f3763bcf891af74f550a70d upstream.
+
+This patch adds mode table entry for capturing cropped 640x480 resolution
+
+Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+Acked-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ drivers/media/i2c/imx219.c | 70 +++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 69 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -54,6 +54,7 @@
+ #define IMX219_VTS_15FPS 0x0dc6
+ #define IMX219_VTS_30FPS_1080P 0x06e3
+ #define IMX219_VTS_30FPS_BINNED 0x06e3
++#define IMX219_VTS_30FPS_640x480 0x06e3
+ #define IMX219_VTS_MAX 0xffff
+
+ #define IMX219_VBLANK_MIN 4
+@@ -138,7 +139,7 @@ struct imx219_mode {
+ /*
+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
+ * driver.
+- * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7.
+ */
+ static const struct imx219_reg mode_3280x2464_regs[] = {
+ {0x0100, 0x00},
+@@ -313,6 +314,63 @@ static const struct imx219_reg mode_1640
+ {0x0163, 0x78},
+ };
+
++static const struct imx219_reg mode_640_480_regs[] = {
++ {0x0100, 0x00},
++ {0x30eb, 0x05},
++ {0x30eb, 0x0c},
++ {0x300a, 0xff},
++ {0x300b, 0xff},
++ {0x30eb, 0x05},
++ {0x30eb, 0x09},
++ {0x0114, 0x01},
++ {0x0128, 0x00},
++ {0x012a, 0x18},
++ {0x012b, 0x00},
++ {0x0162, 0x0d},
++ {0x0163, 0x78},
++ {0x0164, 0x03},
++ {0x0165, 0xe8},
++ {0x0166, 0x08},
++ {0x0167, 0xe7},
++ {0x0168, 0x02},
++ {0x0169, 0xf0},
++ {0x016a, 0x06},
++ {0x016b, 0xaf},
++ {0x016c, 0x02},
++ {0x016d, 0x80},
++ {0x016e, 0x01},
++ {0x016f, 0xe0},
++ {0x0170, 0x01},
++ {0x0171, 0x01},
++ {0x0174, 0x03},
++ {0x0175, 0x03},
++ {0x0301, 0x05},
++ {0x0303, 0x01},
++ {0x0304, 0x03},
++ {0x0305, 0x03},
++ {0x0306, 0x00},
++ {0x0307, 0x39},
++ {0x030b, 0x01},
++ {0x030c, 0x00},
++ {0x030d, 0x72},
++ {0x0624, 0x06},
++ {0x0625, 0x68},
++ {0x0626, 0x04},
++ {0x0627, 0xd0},
++ {0x455e, 0x00},
++ {0x471e, 0x4b},
++ {0x4767, 0x0f},
++ {0x4750, 0x14},
++ {0x4540, 0x00},
++ {0x47b4, 0x14},
++ {0x4713, 0x30},
++ {0x478b, 0x10},
++ {0x478f, 0x10},
++ {0x4793, 0x10},
++ {0x4797, 0x0e},
++ {0x479b, 0x0e},
++};
++
+ static const struct imx219_reg raw8_framefmt_regs[] = {
+ {0x018c, 0x08},
+ {0x018d, 0x08},
+@@ -431,6 +489,16 @@ static const struct imx219_mode supporte
+ .regs = mode_1640_1232_regs,
+ },
+ },
++ {
++ /* 640x480 30fps mode */
++ .width = 640,
++ .height = 480,
++ .vts_def = IMX219_VTS_30FPS_640x480,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
++ .regs = mode_640_480_regs,
++ },
++ },
+ };
+
+ struct imx219 {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch b/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch
new file mode 100644
index 0000000000..ef8eff6c00
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch
@@ -0,0 +1,34 @@
+From 942a240a1999aaf1b8d18a88c63f418991bf7d22 Mon Sep 17 00:00:00 2001
+From: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
+Date: Tue, 31 Mar 2020 20:06:30 +0200
+Subject: [PATCH] media: i2c: imx219: Fix a bug in
+ imx219_enum_frame_size
+
+https://patchwork.linuxtv.org/patch/62740/
+
+When enumerating the frame sizes, the value sent to
+imx219_get_format_code should be fse->code
+(the code from the ioctl) and not imx219->fmt.code
+which is the code set currently in the driver.
+
+Fixes: 22da1d56e ("media: i2c: imx219: Add support for RAW8 bit bayer format")
+
+Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
+Reviewed-by: Helen Koike <helen.koike@collabora.com>
+Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+---
+ drivers/media/i2c/imx219.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -781,7 +781,7 @@ static int imx219_enum_frame_size(struct
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+- if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code))
++ if (fse->code != imx219_get_format_code(imx219, fse->code))
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch b/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch
new file mode 100644
index 0000000000..cb36ce2d96
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch
@@ -0,0 +1,31 @@
+From e8f2c38720e391ce44154f17f3602484c1827a59 Mon Sep 17 00:00:00 2001
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Tue, 24 Mar 2020 23:13:02 +0200
+Subject: [PATCH] media: bcm2835-unicam: Disable event-related ioctls
+ on metadata node
+
+The unicam driver supports both the SOURCE_CHANGE and CTRL events. Both
+events are only generated on the image video node, so the event-related
+ioctls are useless on the medatada node. Disable them.
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -2374,6 +2374,11 @@ static int register_node(struct unicam_d
+ return -ENOMEM;
+ }
+
++ if (node->pad_id == METADATA_PAD) {
++ v4l2_disable_ioctl(vdev, VIDIOC_DQEVENT);
++ v4l2_disable_ioctl(vdev, VIDIOC_SUBSCRIBE_EVENT);
++ v4l2_disable_ioctl(vdev, VIDIOC_UNSUBSCRIBE_EVENT);
++ }
+ if (node->pad_id == METADATA_PAD ||
+ !v4l2_subdev_has_op(unicam->sensor, video, s_std)) {
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch b/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch
new file mode 100644
index 0000000000..544d597a35
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch
@@ -0,0 +1,55 @@
+From 20caa3607c972f5c28b3e9aceb0b8a1d2094c0a7 Mon Sep 17 00:00:00 2001
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Tue, 24 Mar 2020 23:13:02 +0200
+Subject: [PATCH] media: bcm2835-unicam: Add support for the FRAME_SYNC
+ event
+
+The FRAME_SYNC event is useful for userspace image processing algorithms
+to program the camera sensor as early as possible after frame start.
+Support it.
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -772,6 +772,16 @@ static int unicam_all_nodes_disabled(str
+ !dev->node[METADATA_PAD].streaming;
+ }
+
++static void unicam_queue_event_sof(struct unicam_device *unicam)
++{
++ struct v4l2_event event = {
++ .type = V4L2_EVENT_FRAME_SYNC,
++ .u.frame_sync.frame_sequence = unicam->sequence,
++ };
++
++ v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event);
++}
++
+ /*
+ * unicam_isr : ISR handler for unicam capture
+ * @irq: irq number
+@@ -853,6 +863,8 @@ static irqreturn_t unicam_isr(int irq, v
+ */
+ unicam_schedule_dummy_buffer(&unicam->node[i]);
+ }
++
++ unicam_queue_event_sof(unicam);
+ }
+ /*
+ * Cannot swap buffer at frame end, there may be a race condition
+@@ -2022,6 +2034,8 @@ static int unicam_subscribe_event(struct
+ const struct v4l2_event_subscription *sub)
+ {
+ switch (sub->type) {
++ case V4L2_EVENT_FRAME_SYNC:
++ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_event_subscribe(fh, sub, 4, NULL);
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch b/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch
new file mode 100644
index 0000000000..01533d1f3f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch
@@ -0,0 +1,58 @@
+From b2691aee386b18425a7b96cca05e3bcceb8678ae Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 17 Apr 2020 16:20:55 +0100
+Subject: [PATCH] Revert "firmware: raspberrypi: register clk device"
+
+This reverts commit 91f2cf4a6b2131016b1ae9c9500245f0572112c7.
+
+Revert this because the clock driver won't probe without a DT node, and
+creating it as a platform device won't give it one. Doing so avoids the
+following error message:
+
+ raspberrypi-clk raspberrypi-clk: Missing firmware node
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/firmware/raspberrypi.c | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+--- a/drivers/firmware/raspberrypi.c
++++ b/drivers/firmware/raspberrypi.c
+@@ -21,7 +21,6 @@
+ #define MBOX_CHAN_PROPERTY 8
+
+ static struct platform_device *rpi_hwmon;
+-static struct platform_device *rpi_clk;
+
+ struct rpi_firmware {
+ struct mbox_client cl;
+@@ -299,12 +298,6 @@ rpi_register_hwmon_driver(struct device
+ }
+ }
+
+-static void rpi_register_clk_driver(struct device *dev)
+-{
+- rpi_clk = platform_device_register_data(dev, "raspberrypi-clk",
+- -1, NULL, 0);
+-}
+-
+ static int rpi_firmware_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -334,7 +327,6 @@ static int rpi_firmware_probe(struct pla
+ rpi_firmware_print_firmware_revision(fw);
+ rpi_firmware_print_firmware_hash(fw);
+ rpi_register_hwmon_driver(dev, fw);
+- rpi_register_clk_driver(dev);
+
+ return 0;
+ }
+@@ -355,8 +347,6 @@ static int rpi_firmware_remove(struct pl
+
+ platform_device_unregister(rpi_hwmon);
+ rpi_hwmon = NULL;
+- platform_device_unregister(rpi_clk);
+- rpi_clk = NULL;
+ mbox_free_channel(fw->chan);
+ g_pdev = NULL;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch b/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch
new file mode 100644
index 0000000000..c4c25d1512
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch
@@ -0,0 +1,335 @@
+From 372b138a6bc43b0fdff4e46ae4c799fd1b1f2785 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 12 Mar 2020 14:09:38 +0000
+Subject: [PATCH] media: imx219: Advertise embedded data node on media
+ pad 1
+
+This commit updates the imx219 driver to adverise support for embedded
+data streams. This can then be used by the bcm2835-unicam driver, which
+has recently been updated to expose the embedded data stream to
+userland.
+
+The imx219 sensor subdevice overloads the media pad to differentiate
+between image stream (pad 0) and embedded data stream (pad 1) when
+performing the v4l2_subdev_pad_ops functions.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx219.c | 226 +++++++++++++++++++++++++------------
+ 1 file changed, 155 insertions(+), 71 deletions(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -112,6 +112,16 @@
+ #define IMX219_TESTP_BLUE_DEFAULT 0
+ #define IMX219_TESTP_GREENB_DEFAULT 0
+
++/* Embedded metadata stream structure */
++#define IMX219_EMBEDDED_LINE_WIDTH 16384
++#define IMX219_NUM_EMBEDDED_LINES 1
++
++enum pad_types {
++ IMAGE_PAD,
++ METADATA_PAD,
++ NUM_PADS
++};
++
+ struct imx219_reg {
+ u16 address;
+ u8 val;
+@@ -503,7 +513,7 @@ static const struct imx219_mode supporte
+
+ struct imx219 {
+ struct v4l2_subdev sd;
+- struct media_pad pad;
++ struct media_pad pad[NUM_PADS];
+
+ struct v4l2_mbus_framefmt fmt;
+
+@@ -652,17 +662,25 @@ static void imx219_set_default_format(st
+ static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+ {
+ struct imx219 *imx219 = to_imx219(sd);
+- struct v4l2_mbus_framefmt *try_fmt =
+- v4l2_subdev_get_try_format(sd, fh->pad, 0);
++ struct v4l2_mbus_framefmt *try_fmt_img =
++ v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD);
++ struct v4l2_mbus_framefmt *try_fmt_meta =
++ v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD);
+
+ mutex_lock(&imx219->mutex);
+
+- /* Initialize try_fmt */
+- try_fmt->width = supported_modes[0].width;
+- try_fmt->height = supported_modes[0].height;
+- try_fmt->code = imx219_get_format_code(imx219,
+- MEDIA_BUS_FMT_SRGGB10_1X10);
+- try_fmt->field = V4L2_FIELD_NONE;
++ /* Initialize try_fmt for the image pad */
++ try_fmt_img->width = supported_modes[0].width;
++ try_fmt_img->height = supported_modes[0].height;
++ try_fmt_img->code = imx219_get_format_code(imx219,
++ MEDIA_BUS_FMT_SRGGB10_1X10);
++ try_fmt_img->field = V4L2_FIELD_NONE;
++
++ /* Initialize try_fmt for the embedded metadata pad */
++ try_fmt_meta->width = IMX219_EMBEDDED_LINE_WIDTH;
++ try_fmt_meta->height = IMX219_NUM_EMBEDDED_LINES;
++ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA;
++ try_fmt_meta->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&imx219->mutex);
+
+@@ -764,10 +782,21 @@ static int imx219_enum_mbus_code(struct
+ {
+ struct imx219 *imx219 = to_imx219(sd);
+
+- if (code->index >= (ARRAY_SIZE(codes) / 4))
++ if (code->pad >= NUM_PADS)
+ return -EINVAL;
+
+- code->code = imx219_get_format_code(imx219, codes[code->index * 4]);
++ if (code->pad == IMAGE_PAD) {
++ if (code->index >= (ARRAY_SIZE(codes) / 4))
++ return -EINVAL;
++
++ code->code = imx219_get_format_code(imx219,
++ codes[code->index * 4]);
++ } else {
++ if (code->index > 0)
++ return -EINVAL;
++
++ code->code = MEDIA_BUS_FMT_SENSOR_DATA;
++ }
+
+ return 0;
+ }
+@@ -778,16 +807,29 @@ static int imx219_enum_frame_size(struct
+ {
+ struct imx219 *imx219 = to_imx219(sd);
+
+- if (fse->index >= ARRAY_SIZE(supported_modes))
++ if (fse->pad >= NUM_PADS)
+ return -EINVAL;
+
+- if (fse->code != imx219_get_format_code(imx219, fse->code))
+- return -EINVAL;
++ if (fse->pad == IMAGE_PAD) {
++ if (fse->index >= ARRAY_SIZE(supported_modes))
++ return -EINVAL;
++
++ if (fse->code != imx219_get_format_code(imx219, fse->code))
++ return -EINVAL;
++
++ fse->min_width = supported_modes[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = supported_modes[fse->index].height;
++ fse->max_height = fse->min_height;
++ } else {
++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0)
++ return -EINVAL;
+
+- fse->min_width = supported_modes[fse->index].width;
+- fse->max_width = fse->min_width;
+- fse->min_height = supported_modes[fse->index].height;
+- fse->max_height = fse->min_height;
++ fse->min_width = IMX219_EMBEDDED_LINE_WIDTH;
++ fse->max_width = fse->min_width;
++ fse->min_height = IMX219_NUM_EMBEDDED_LINES;
++ fse->max_height = fse->min_height;
++ }
+
+ return 0;
+ }
+@@ -802,9 +844,9 @@ static void imx219_reset_colorspace(stru
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+ }
+
+-static void imx219_update_pad_format(struct imx219 *imx219,
+- const struct imx219_mode *mode,
+- struct v4l2_subdev_format *fmt)
++static void imx219_update_image_pad_format(struct imx219 *imx219,
++ const struct imx219_mode *mode,
++ struct v4l2_subdev_format *fmt)
+ {
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+@@ -812,20 +854,38 @@ static void imx219_update_pad_format(str
+ imx219_reset_colorspace(&fmt->format);
+ }
+
++static void imx219_update_metadata_pad_format(struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = IMX219_EMBEDDED_LINE_WIDTH;
++ fmt->format.height = IMX219_NUM_EMBEDDED_LINES;
++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA;
++ fmt->format.field = V4L2_FIELD_NONE;
++}
++
+ static int __imx219_get_pad_format(struct imx219 *imx219,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+ {
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
++
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
+ /* update the code which could change due to vflip or hflip: */
+- try_fmt->code = imx219_get_format_code(imx219, try_fmt->code);
++ try_fmt->code = fmt->pad == IMAGE_PAD ?
++ imx219_get_format_code(imx219, try_fmt->code) :
++ MEDIA_BUS_FMT_SENSOR_DATA;
+ fmt->format = *try_fmt;
+ } else {
+- imx219_update_pad_format(imx219, imx219->mode, fmt);
+- fmt->format.code = imx219_get_format_code(imx219,
+- imx219->fmt.code);
++ if (fmt->pad == IMAGE_PAD) {
++ imx219_update_image_pad_format(imx219, imx219->mode,
++ fmt);
++ fmt->format.code = imx219_get_format_code(imx219,
++ imx219->fmt.code);
++ } else {
++ imx219_update_metadata_pad_format(fmt);
++ }
+ }
+
+ return 0;
+@@ -855,51 +915,74 @@ static int imx219_set_pad_format(struct
+ int exposure_max, exposure_def, hblank;
+ unsigned int i;
+
+- mutex_lock(&imx219->mutex);
+-
+- for (i = 0; i < ARRAY_SIZE(codes); i++)
+- if (codes[i] == fmt->format.code)
+- break;
+- if (i >= ARRAY_SIZE(codes))
+- i = 0;
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
+
+- /* Bayer order varies with flips */
+- fmt->format.code = imx219_get_format_code(imx219, codes[i]);
++ mutex_lock(&imx219->mutex);
+
+- mode = v4l2_find_nearest_size(supported_modes,
+- ARRAY_SIZE(supported_modes),
+- width, height,
+- fmt->format.width, fmt->format.height);
+- imx219_update_pad_format(imx219, mode, fmt);
+- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+- framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+- *framefmt = fmt->format;
+- } else if (imx219->mode != mode ||
+- imx219->fmt.code != fmt->format.code) {
+- imx219->fmt = fmt->format;
+- imx219->mode = mode;
+- /* Update limits and set FPS to default */
+- __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
+- IMX219_VTS_MAX - mode->height, 1,
+- mode->vts_def - mode->height);
+- __v4l2_ctrl_s_ctrl(imx219->vblank,
+- mode->vts_def - mode->height);
+- /* Update max exposure while meeting expected vblanking */
+- exposure_max = mode->vts_def - 4;
+- exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
+- exposure_max : IMX219_EXPOSURE_DEFAULT;
+- __v4l2_ctrl_modify_range(imx219->exposure,
+- imx219->exposure->minimum,
+- exposure_max, imx219->exposure->step,
+- exposure_def);
+- /*
+- * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
+- * depends on mode->width only, and is not changeble in any
+- * way other than changing the mode.
+- */
+- hblank = IMX219_PPL_DEFAULT - mode->width;
+- __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
+- hblank);
++ if (fmt->pad == IMAGE_PAD) {
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == fmt->format.code)
++ break;
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx219_get_format_code(imx219, codes[i]);
++
++ mode = v4l2_find_nearest_size(supported_modes,
++ ARRAY_SIZE(supported_modes),
++ width, height,
++ fmt->format.width,
++ fmt->format.height);
++ imx219_update_image_pad_format(imx219, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else if (imx219->mode != mode ||
++ imx219->fmt.code != fmt->format.code) {
++ imx219->fmt = fmt->format;
++ imx219->mode = mode;
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(imx219->vblank,
++ IMX219_VBLANK_MIN,
++ IMX219_VTS_MAX - mode->height,
++ 1,
++ mode->vts_def - mode->height);
++ __v4l2_ctrl_s_ctrl(imx219->vblank,
++ mode->vts_def - mode->height);
++ /*
++ * Update max exposure while meeting
++ * expected vblanking
++ */
++ exposure_max = mode->vts_def - 4;
++ exposure_def =
++ (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++ exposure_max : IMX219_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(imx219->exposure,
++ imx219->exposure->minimum,
++ exposure_max,
++ imx219->exposure->step,
++ exposure_def);
++ /*
++ * Currently PPL is fixed to IMX219_PPL_DEFAULT, so
++ * hblank depends on mode->width only, and is not
++ * changeble in any way other than changing the mode.
++ */
++ hblank = IMX219_PPL_DEFAULT - mode->width;
++ __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank,
++ 1, hblank);
++ }
++ } else {
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else {
++ /* Only one embedded data mode is supported */
++ imx219_update_metadata_pad_format(fmt);
++ }
+ }
+
+ mutex_unlock(&imx219->mutex);
+@@ -1399,13 +1482,14 @@ static int imx219_probe(struct i2c_clien
+ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+- /* Initialize source pad */
+- imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
++ /* Initialize source pads */
++ imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
++ imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+ /* Initialize default format */
+ imx219_set_default_format(imx219);
+
+- ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
++ ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad);
+ if (ret) {
+ dev_err(dev, "failed to init entity pads: %d\n", ret);
+ goto error_handler_free;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch b/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch
new file mode 100644
index 0000000000..4c5e50636c
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch
@@ -0,0 +1,31 @@
+From 4f2da50bb75ec7b74a23e119062d945626398e30 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Apr 2020 11:25:18 +0100
+Subject: [PATCH] dts: bcm2711: EMMC2 can address the whole first GB
+
+Although 0xfc000000 looks like an inaccessible RAM address (due to the
+peripheral mappings), with RAM mapped at 0xc0000000 (as it is on the
+30/32-bit VPU bus) this is actually 0x3c000000 in the ARM memory space,
+which is fine.
+
+This difference is potentially the cause of some warnings seen in
+sdhci_send_command.
+
+Fixes: "dts: bcm2711: Move emmc2 to its own 'bus'"
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -223,7 +223,7 @@
+ #size-cells = <1>;
+
+ ranges = <0x0 0x7e000000 0x0 0xfe000000 0x01800000>;
+- dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x3c000000>;
++ dma-ranges = <0x0 0xc0000000 0x0 0x00000000 0x40000000>;
+
+ emmc2: emmc2@7e340000 {
+ compatible = "brcm,bcm2711-emmc2";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch b/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch
new file mode 100644
index 0000000000..a3896be439
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch
@@ -0,0 +1,53 @@
+From 77beb3055b14910fa3ef9af606476520e956bf93 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Apr 2020 22:18:52 +0100
+Subject: [PATCH] driver: char: rpivid: Remove legacy name support
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/broadcom/rpivid-mem.c | 22 ----------------------
+ 1 file changed, 22 deletions(-)
+
+--- a/drivers/char/broadcom/rpivid-mem.c
++++ b/drivers/char/broadcom/rpivid-mem.c
+@@ -193,32 +193,11 @@ static int rpivid_mem_probe(struct platf
+ goto failed_device_create;
+ }
+
+- /* Legacy alias */
+- {
+- char *oldname = kstrdup(priv->name, GFP_KERNEL);
+-
+- oldname[1] = 'a';
+- oldname[2] = 'r';
+- oldname[3] = 'g';
+- oldname[4] = 'o';
+- oldname[5] = 'n';
+- dev = device_create(priv->class, NULL, priv->devid + 1, NULL,
+- oldname + 1);
+- kfree(oldname);
+-
+- if (IS_ERR(dev)) {
+- err = PTR_ERR(dev);
+- goto failed_legacy_device_create;
+- }
+- }
+-
+ dev_info(priv->dev, "%s initialised: Registers at 0x%08lx length 0x%08lx",
+ priv->name, priv->regs_phys, priv->mem_window_len);
+
+ return 0;
+
+-failed_legacy_device_create:
+- device_destroy(priv->class, priv->devid);
+ failed_device_create:
+ class_destroy(priv->class);
+ failed_class_create:
+@@ -238,7 +217,6 @@ static int rpivid_mem_remove(struct plat
+ struct device *dev = &pdev->dev;
+ struct rpivid_mem_priv *priv = platform_get_drvdata(pdev);
+
+- device_destroy(priv->class, priv->devid + 1);
+ device_destroy(priv->class, priv->devid);
+ class_destroy(priv->class);
+ cdev_del(&priv->rpivid_mem_cdev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch b/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch
new file mode 100644
index 0000000000..afc1a5a6a8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch
@@ -0,0 +1,51 @@
+From 8c2369b39b1dafe7a26907173bb47d37ec53bfa2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 21 Apr 2020 11:30:23 +0100
+Subject: [PATCH] driver: char: rpivid: Don't map more than wanted
+
+Limit mappings to the permitted range, but don't map more than asked
+for otherwise we walk off the end of the allocated VMA.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/broadcom/rpivid-mem.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/char/broadcom/rpivid-mem.c
++++ b/drivers/char/broadcom/rpivid-mem.c
+@@ -100,6 +100,7 @@ static int rpivid_mem_mmap(struct file *
+ {
+ struct rpivid_mem_priv *priv;
+ unsigned long pages;
++ unsigned long len;
+
+ priv = file->private_data;
+ pages = priv->regs_phys >> PAGE_SHIFT;
+@@ -107,14 +108,13 @@ static int rpivid_mem_mmap(struct file *
+ * The address decode is far larger than the actual number of registers.
+ * Just map the whole lot in.
+ */
+- vma->vm_page_prot = phys_mem_access_prot(file, pages,
+- priv->mem_window_len,
++ len = min(vma->vm_end - vma->vm_start, priv->mem_window_len);
++ vma->vm_page_prot = phys_mem_access_prot(file, pages, len,
+ vma->vm_page_prot);
+ vma->vm_ops = &rpivid_mem_vm_ops;
+ if (remap_pfn_range(vma, vma->vm_start,
+- pages,
+- priv->mem_window_len,
+- vma->vm_page_prot)) {
++ pages, len,
++ vma->vm_page_prot)) {
+ return -EAGAIN;
+ }
+ return 0;
+@@ -156,7 +156,7 @@ static int rpivid_mem_probe(struct platf
+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (ioresource) {
+ priv->regs_phys = ioresource->start;
+- priv->mem_window_len = ioresource->end - ioresource->start;
++ priv->mem_window_len = (ioresource->end + 1) - ioresource->start;
+ } else {
+ dev_err(priv->dev, "failed to get IO resource");
+ err = -ENOENT;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch b/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch
new file mode 100644
index 0000000000..002ad8686e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch
@@ -0,0 +1,434 @@
+From 77d7427bed21c92d1c10e0cc9beabb5ce9bb6c0b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 9 Apr 2020 12:46:13 +0100
+Subject: [PATCH] dt: Implement an I2C pinctrl mux for BSC0.
+
+BSC0 serves either the HAT EEPROM pins on the 40pin connector,
+or the display and camera on a board specific pairing of either
+GPIO 28&29, or 44&45.
+
+Use I2C_MUX_PINCTRL to allow exposing both pairs of pins as I2C
+busses.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2708-rpi-b.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2708-rpi-cm.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2708-rpi-zero-w.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2708-rpi-zero.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 9 ++++---
+ arch/arm/boot/dts/bcm270x-rpi.dtsi | 7 ++---
+ arch/arm/boot/dts/bcm2710-rpi-2-b.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 9 ++++---
+ arch/arm/boot/dts/bcm2710-rpi-cm3.dts | 10 ++++---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 5 ++--
+ arch/arm/boot/dts/bcm2711.dtsi | 2 +-
+ .../boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi | 4 +++
+ .../boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi | 4 +++
+ arch/arm/boot/dts/bcm283x.dtsi | 26 ++++++++++++++++++-
+ 16 files changed, 100 insertions(+), 39 deletions(-)
+ create mode 100644 arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi
+ create mode 100644 arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi
+
+--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2708-rpi.dtsi"
+ #include "bcm283x-rpi-smsc9514.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,model-b-plus", "brcm,bcm2835";
+@@ -68,12 +69,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2708-rpi.dtsi"
+ #include "bcm283x-rpi-smsc9512.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,model-b", "brcm,bcm2835";
+@@ -68,12 +69,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts
+@@ -3,6 +3,7 @@
+ #include "bcm2708-rpi-cm.dtsi"
+ #include "bcm283x-rpi-csi0-2lane.dtsi"
+ #include "bcm283x-rpi-csi1-4lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,compute-module", "brcm,bcm2835";
+@@ -67,12 +68,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
+@@ -3,6 +3,7 @@
+ #include "bcm2708.dtsi"
+ #include "bcm2708-rpi.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,model-zero-w", "brcm,bcm2835";
+@@ -116,12 +117,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts
+@@ -3,6 +3,7 @@
+ #include "bcm2708.dtsi"
+ #include "bcm2708-rpi.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,model-zero", "brcm,bcm2835";
+@@ -71,12 +72,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2709-rpi.dtsi"
+ #include "bcm283x-rpi-smsc9514.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,2-model-b", "brcm,bcm2836";
+@@ -68,12 +69,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -21,6 +21,7 @@
+ i2s = &i2s;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
++ i2c10 = &i2c_csi_dsi;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ spi2 = &spi2;
+@@ -83,9 +84,9 @@
+ uart1 = <&uart1>,"status";
+ i2s = <&i2s>,"status";
+ spi = <&spi0>,"status";
+- i2c0 = <&i2c0>,"status";
++ i2c0 = <&i2c0if>,"status",<&i2c0mux>,"status";
+ i2c1 = <&i2c1>,"status";
+- i2c0_baudrate = <&i2c0>,"clock-frequency:0";
++ i2c0_baudrate = <&i2c0if>,"clock-frequency:0";
+ i2c1_baudrate = <&i2c1>,"clock-frequency:0";
+
+ audio = <&audio>,"status";
+@@ -105,7 +106,7 @@
+ status = "disabled";
+ };
+
+-&i2c0 {
++&i2c0if {
+ status = "disabled";
+ };
+
+--- a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2709-rpi.dtsi"
+ #include "bcm283x-rpi-smsc9514.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+
+ / {
+ compatible = "raspberrypi,2-model-b-rev2", "brcm,bcm2837";
+@@ -68,12 +69,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2709-rpi.dtsi"
+ #include "bcm283x-rpi-lan7515.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+ / {
+ compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
+@@ -126,12 +127,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
+@@ -4,6 +4,7 @@
+ #include "bcm2709-rpi.dtsi"
+ #include "bcm283x-rpi-smsc9514.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+ / {
+ compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
+@@ -137,12 +138,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
+@@ -4,7 +4,7 @@
+ #include "bcm2709-rpi.dtsi"
+ #include "bcm283x-rpi-csi0-2lane.dtsi"
+ #include "bcm283x-rpi-csi1-4lane.dtsi"
+-
++#include "bcm283x-rpi-i2c0mux_0_28.dtsi"
+ / {
+ compatible = "raspberrypi,3-compute-module", "brcm,bcm2837";
+ model = "Raspberry Pi Compute Module 3";
+@@ -88,12 +88,14 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_pins>;
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_pins>;
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -192,6 +192,7 @@
+
+ #include "bcm2711-rpi.dtsi"
+ #include "bcm283x-rpi-csi1-2lane.dtsi"
++#include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+ /delete-node/ &emmc2;
+
+@@ -421,9 +422,7 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_pins>;
++&i2c0if {
+ clock-frequency = <100000>;
+ };
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -986,7 +986,7 @@
+ alloc-ranges = <0x0 0x00000000 0x40000000>;
+ };
+
+-&i2c0 {
++&i2c0if {
+ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
+ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_28.dtsi
+@@ -0,0 +1,4 @@
++&i2c0mux {
++ pinctrl-0 = <&i2c0_gpio0>;
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm283x-rpi-i2c0mux_0_44.dtsi
+@@ -0,0 +1,4 @@
++&i2c0mux {
++ pinctrl-0 = <&i2c0_gpio0>;
++ pinctrl-1 = <&i2c0_gpio44>;
++};
+--- a/arch/arm/boot/dts/bcm283x.dtsi
++++ b/arch/arm/boot/dts/bcm283x.dtsi
+@@ -340,7 +340,7 @@
+ status = "disabled";
+ };
+
+- i2c0: i2c@7e205000 {
++ i2c0if: i2c@7e205000 {
+ compatible = "brcm,bcm2835-i2c";
+ reg = <0x7e205000 0x200>;
+ interrupts = <2 21>;
+@@ -350,6 +350,30 @@
+ status = "disabled";
+ };
+
++ i2c0mux: i2c0mux {
++ compatible = "i2c-mux-pinctrl";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ i2c-parent = <&i2c0if>;
++
++ pinctrl-names = "i2c0", "i2c_csi_dsi";
++
++ status = "disabled";
++
++ i2c0: i2c@0 {
++ reg = <0>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++ i2c_csi_dsi: i2c@1 {
++ reg = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++ };
++
+ dpi: dpi@7e208000 {
+ compatible = "brcm,bcm2835-dpi";
+ reg = <0x7e208000 0x8c>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch b/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch
new file mode 100644
index 0000000000..f6b6340c7f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch
@@ -0,0 +1,425 @@
+From bd24924fea541c114c7761f4698f3fe29d7257e1 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 9 Apr 2020 15:04:14 +0100
+Subject: [PATCH] dtoverlays: Update CSI overlays to use i2c_csi_dsi
+
+Update all overlays that were using i2c_vc for talking to CSI
+source devices to use the new i2c_csi_dsi node via i2c_mux_pinctrl.
+Remove the pins overrides as well.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 42 ++++---------------
+ .../boot/dts/overlays/adv7282m-overlay.dts | 29 +++----------
+ arch/arm/boot/dts/overlays/imx219-overlay.dts | 41 +++++-------------
+ .../arm/boot/dts/overlays/irs1125-overlay.dts | 33 ++++-----------
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 33 ++++-----------
+ .../boot/dts/overlays/tc358743-overlay.dts | 32 ++++----------
+ 6 files changed, 47 insertions(+), 163 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -331,22 +331,14 @@ Info: Analog Devices ADV7282M analogue
+ Uses Unicam1, which is the standard camera connector on most Pi
+ variants.
+ Load: dtoverlay=adv7282m,<param>=<val>
+-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
+- addr Overrides the I2C address (default 0x21)
++Params: addr Overrides the I2C address (default 0x21)
+
+
+ Name: adv728x-m
+ Info: Analog Devices ADV728[0|1|2]-M analogue video to CSI2 bridges.
+ This is a wrapper for adv7282m, and defaults to ADV7282M.
+ Load: dtoverlay=adv728x-m,<param>=<val>
+-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
+- addr Overrides the I2C address (default 0x21)
++Params: addr Overrides the I2C address (default 0x21)
+ adv7280m Select ADV7280-M.
+ adv7281m Select ADV7281-M.
+ adv7281ma Select ADV7281-MA.
+@@ -1384,12 +1376,8 @@ Name: imx219
+ Info: Sony IMX219 camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+ variants.
+-Load: dtoverlay=imx219,<param>=<val>
+-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+-
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
++Load: dtoverlay=imx219
++Params: <None>
+
+
+ Name: iqaudio-codec
+@@ -1453,12 +1441,8 @@ Name: irs1125
+ Info: Infineon irs1125 TOF camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+ variants.
+-Load: dtoverlay=irs1125,<param>=<val>
+-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+-
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
++Load: dtoverlay=irs1125
++Params: <None>
+
+
+ Name: jedec-spi-nor
+@@ -1743,12 +1727,8 @@ Name: ov5647
+ Info: Omnivision OV5647 camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+ variants.
+-Load: dtoverlay=ov5647,<param>=<val>
+-Params: i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+-
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
++Load: dtoverlay=ov5647
++Params: <None>
+
+
+ Name: papirus
+@@ -2555,12 +2535,6 @@ Params: 4lane Use 4 la
+ (574Mbit/s) and 486000000 (972Mbit/s - default)
+ are supported by the driver.
+
+- i2c_pins_0_1 Use pins 0&1 for the I2C instead of 44&45.
+- Useful on Compute Modules.
+-
+- i2c_pins_28_29 Use pins 28&29 for the I2C instead of 44&45.
+- This is required for Pi B+, 2, 0, and 0W.
+-
+
+ Name: tc358743-audio
+ Info: Used in combination with the tc358743-fast overlay to route the audio
+--- a/arch/arm/boot/dts/overlays/adv7282m-overlay.dts
++++ b/arch/arm/boot/dts/overlays/adv7282m-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -45,37 +45,20 @@
+ };
+ };
+ fragment@2 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <28 29>;
+- brcm,function = <4>; /* alt0 */
+- };
+-
+- };
+- fragment@3 {
+- target = <&i2c0_pins>;
++ target = <&i2c0if>;
+ __overlay__ {
+- brcm,pins = <44 45>;
+- brcm,function = <5>; /* alt1 */
+- };
+- };
+- fragment@4 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <0 1>;
+- brcm,function = <4>; /* alt0 */
++ status = "okay";
+ };
+ };
+- fragment@5 {
+- target = <&i2c_vc>;
++
++ fragment@3 {
++ target = <&i2c0mux>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+ __overrides__ {
+- i2c_pins_0_1 = <0>,"-2-3+4";
+- i2c_pins_28_29 = <0>,"+2-3-4";
+ addr = <&adv728x>,"reg:0";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -61,34 +61,13 @@
+ };
+
+ fragment@2 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <28 29>;
+- brcm,function = <4>; /* alt0 */
+- };
+- };
+- fragment@3 {
+- target = <&i2c0_pins>;
+- __overlay__ {
+- brcm,pins = <44 45>;
+- brcm,function = <5>; /* alt1 */
+- };
+- };
+- fragment@4 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <0 1>;
+- brcm,function = <4>; /* alt0 */
+- };
+- };
+- fragment@5 {
+- target = <&i2c_vc>;
++ target = <&i2c0if>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@6 {
++ fragment@3 {
+ target-path="/";
+ __overlay__ {
+ imx219_vana: fixedregulator@0 {
+@@ -114,16 +93,18 @@
+ };
+ };
+
+- fragment@7 {
++ fragment@4 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@5 {
+ target-path="/__overrides__";
+ __overlay__ {
+ cam0-pwdn-ctrl = <&imx219_vana>,"gpio:0";
+ cam0-pwdn = <&imx219_vana>,"gpio:4";
+ };
+ };
+-
+- __overrides__ {
+- i2c_pins_0_1 = <0>,"-2-3+4";
+- i2c_pins_28_29 = <0>,"+2-3-4";
+- };
+ };
+--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts
++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -55,43 +55,24 @@
+ };
+
+ fragment@2 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <28 29>;
+- brcm,function = <4>; /* alt0 */
+- };
+- };
+- fragment@3 {
+- target = <&i2c0_pins>;
++ target = <&i2c0if>;
+ __overlay__ {
+- brcm,pins = <44 45>;
+- brcm,function = <5>; /* alt1 */
+- };
+- };
+- fragment@4 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <0 1>;
+- brcm,function = <4>; /* alt0 */
++ status = "okay";
+ };
+ };
+- fragment@5 {
+- target = <&i2c_vc>;
++
++ fragment@3 {
++ target = <&i2c0mux>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@6 {
++ fragment@4 {
+ target-path="/__overrides__";
+ __overlay__ {
+ cam0-pwdn-ctrl = <&irs1125>,"pwdn-gpios:0";
+ cam0-pwdn = <&irs1125>,"pwdn-gpios:4";
+ };
+ };
+-
+- __overrides__ {
+- i2c_pins_0_1 = <0>,"-2-3+4";
+- i2c_pins_28_29 = <0>,"+2-3-4";
+- };
+ };
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -55,34 +55,20 @@
+ };
+
+ fragment@2 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <28 29>;
+- brcm,function = <4>; /* alt0 */
+- };
+- };
+- fragment@3 {
+- target = <&i2c0_pins>;
++ target = <&i2c0if>;
+ __overlay__ {
+- brcm,pins = <44 45>;
+- brcm,function = <5>; /* alt1 */
+- };
+- };
+- fragment@4 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <0 1>;
+- brcm,function = <4>; /* alt0 */
++ status = "okay";
+ };
+ };
+- fragment@5 {
+- target = <&i2c_vc>;
++
++ fragment@3 {
++ target = <&i2c0mux>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+- fragment@6 {
++ fragment@4 {
+ target-path="/__overrides__";
+ __overlay__ {
+ cam0-pwdn-ctrl = <&ov5647>,"pwdn-gpios:0";
+@@ -91,9 +77,4 @@
+ cam0-led = <&ov5647>,"pwdn-gpios:16";
+ };
+ };
+-
+- __overrides__ {
+- i2c_pins_0_1 = <0>,"-2-3+4";
+- i2c_pins_28_29 = <0>,"+2-3-4";
+- };
+ };
+--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -54,7 +54,7 @@
+ };
+
+ fragment@2 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ tc358743@0f {
+ port {
+@@ -67,7 +67,7 @@
+ };
+
+ fragment@3 {
+- target = <&i2c_vc>;
++ target = <&i2c_csi_dsi>;
+ __dormant__ {
+ tc358743@0f {
+ port {
+@@ -80,36 +80,20 @@
+ };
+
+ fragment@4 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <28 29>;
+- brcm,function = <4>; /* alt0 */
+- };
+- };
+- fragment@5 {
+- target = <&i2c0_pins>;
++ target = <&i2c0if>;
+ __overlay__ {
+- brcm,pins = <44 45>;
+- brcm,function = <5>; /* alt1 */
+- };
+- };
+- fragment@6 {
+- target = <&i2c0_pins>;
+- __dormant__ {
+- brcm,pins = <0 1>;
+- brcm,function = <4>; /* alt0 */
++ status = "okay";
+ };
+ };
+- fragment@7 {
+- target = <&i2c_vc>;
++
++ fragment@5 {
++ target = <&i2c0mux>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+ __overrides__ {
+- i2c_pins_0_1 = <0>,"-4-5+6";
+- i2c_pins_28_29 = <0>,"+4-5-6";
+ 4lane = <0>, "-2+3";
+ link-frequency = <&tc358743>,"link-frequencies#0";
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch b/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch
new file mode 100644
index 0000000000..f5e4e4b008
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch
@@ -0,0 +1,201 @@
+From 393b01ee7330723b5f27b86d1b03bed88f8a8ffa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 9 Apr 2020 17:26:13 +0100
+Subject: [PATCH] dt: Update all mainline bcm283x dt files for i2c0
+ pinctrl mux
+
+BSC0 (aka i2c0) can me muxed via pinctrl to GPIOs 0&1, 28&29, or
+44&45. These have different uses based on the platform (40pin header,
+and CSI/DSI connectors), so add a pinctrl I2C mux between the
+different options.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2835-rpi-a-plus.dts | 5 +++++
+ arch/arm/boot/dts/bcm2835-rpi-a.dts | 7 +++++++
+ arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 5 +++++
+ arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 7 +++++++
+ arch/arm/boot/dts/bcm2835-rpi-b.dts | 7 +++++++
+ arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts | 5 +++++
+ arch/arm/boot/dts/bcm2835-rpi-zero-w.dts | 5 +++++
+ arch/arm/boot/dts/bcm2835-rpi-zero.dts | 5 +++++
+ arch/arm/boot/dts/bcm2835-rpi.dtsi | 10 +++++++---
+ arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 5 +++++
+ arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts | 5 +++++
+ arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts | 5 +++++
+ arch/arm/boot/dts/bcm2837-rpi-3-b.dts | 5 +++++
+ arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts | 5 +++++
+ 14 files changed, 78 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts
+@@ -126,3 +126,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 28&29 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-a.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-a.dts
+@@ -121,3 +121,10 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header.
++ * To avoid having to remap everything, map both ports to gpios 0&1
++ */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio0>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts
+@@ -128,3 +128,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 28&29 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts
+@@ -121,3 +121,10 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c0 on camera/display connector is gpio 0&1. Not exposed on header.
++ * To avoid having to remap everything, map both ports to gpios 0&1
++ */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio0>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts
+@@ -116,3 +116,10 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* camera/display connector use BSC1 on GPIOS 2&3.
++ * To avoid having to remap everything, map both ports to gpios 0&1
++ */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio0>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
+@@ -95,3 +95,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* WHAT TO DO HERE? */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-zero-w.dts
+@@ -149,3 +149,8 @@
+ pinctrl-0 = <&uart1_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 28&29 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi-zero.dts
++++ b/arch/arm/boot/dts/bcm2835-rpi-zero.dts
+@@ -117,3 +117,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 28&29 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
+@@ -46,13 +46,17 @@
+ };
+ };
+
+-&i2c0 {
+- pinctrl-names = "default";
+- pinctrl-0 = <&i2c0_gpio0>;
++&i2c0if {
+ status = "okay";
+ clock-frequency = <100000>;
+ };
+
++&i2c0mux {
++ pinctrl-0 = <&i2c0_gpio0>;
++ /* pinctrl-1 varies based on platform */
++ status = "okay";
++};
++
+ &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c1_gpio2>;
+--- a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts
+@@ -128,3 +128,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 28&29 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
+--- a/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts
++++ b/arch/arm/boot/dts/bcm2837-rpi-3-a-plus.dts
+@@ -176,3 +176,8 @@
+ pinctrl-0 = <&uart1_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 44&45 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio44>;
++};
+--- a/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts
+@@ -179,3 +179,8 @@
+ pinctrl-0 = <&uart1_gpio14>;
+ status = "okay";
+ };
++
++/* i2c on camera/display connector is gpio 44&45 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio44>;
++};
+--- a/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
++++ b/arch/arm/boot/dts/bcm2837-rpi-3-b.dts
+@@ -174,3 +174,8 @@
+ status = "okay";
+ bus-width = <4>;
+ };
++
++/* i2c on camera/display connector is gpio 44&45 */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio44>;
++};
+--- a/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts
++++ b/arch/arm/boot/dts/bcm2837-rpi-cm3-io3.dts
+@@ -94,3 +94,8 @@
+ pinctrl-0 = <&uart0_gpio14>;
+ status = "okay";
+ };
++
++/* WHAT TO DO HERE? */
++&i2c0mux {
++ pinctrl-1 = <&i2c0_gpio28>;
++};
diff --git a/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch b/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch
new file mode 100644
index 0000000000..42067a7816
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch
@@ -0,0 +1,182 @@
+From bd291f0ff613ad270a7c9352f3f27a09c058553f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 21 Apr 2020 17:34:27 +0100
+Subject: [PATCH] ARM: dts: Create bcm2708-rpi-b-rev1.dts
+
+The first revision of the Pi Model B used I2C0 to address the camera
+and I2C0 was available for user applications on the 26-pin header.
+The second revision switched the roles, kept I2C0 on the 26-pin header
+and added I2C1 on a new 8-way header (P5).
+
+Up to now, downstream DTS has used a single file for both revisions of
+the board, with a small amount of patching from the firmware. With the
+introduction of an I2C mux to share I2C0 between the camera/display
+connectors and the IDC headers, the difference between the two versions
+becomes too great to comfortably manage with tweaking, hence this split.
+
+Upstream DTS files already have bcm2835-rpi-b.dts and
+bcm2835-rpi-b-rev2.dts, but for backwards compatibility the new file is
+being added as bcm2708-rpi-b-rev1.dts, rather than renaming the old
+shared version to bcm2708-rpi-b-rev2.dts.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts | 127 +++++++++++++++++++++++
+ arch/arm/boot/dts/bcm270x-rpi.dtsi | 4 +
+ 3 files changed, 132 insertions(+)
+ create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -2,6 +2,7 @@
+
+ dtb-$(CONFIG_ARCH_BCM2835) += \
+ bcm2708-rpi-b.dtb \
++ bcm2708-rpi-b-rev1.dtb \
+ bcm2708-rpi-b-plus.dtb \
+ bcm2708-rpi-cm.dtb \
+ bcm2708-rpi-zero.dtb \
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
+@@ -0,0 +1,127 @@
++/dts-v1/;
++
++#include "bcm2708.dtsi"
++#include "bcm2708-rpi.dtsi"
++#include "bcm283x-rpi-smsc9512.dtsi"
++#include "bcm283x-rpi-csi1-2lane.dtsi"
++
++/ {
++ compatible = "raspberrypi,model-b", "brcm,bcm2835";
++ model = "Raspberry Pi Model B";
++};
++
++&gpio {
++ spi0_pins: spi0_pins {
++ brcm,pins = <9 10 11>;
++ brcm,function = <4>; /* alt0 */
++ };
++
++ spi0_cs_pins: spi0_cs_pins {
++ brcm,pins = <8 7>;
++ brcm,function = <1>; /* output */
++ };
++
++ i2c0_pins: i2c0 {
++ brcm,pins = <0 1>;
++ brcm,function = <4>;
++ };
++
++ i2c1_pins: i2c1 {
++ brcm,pins = <2 3>;
++ brcm,function = <4>;
++ };
++
++ i2s_pins: i2s {
++ brcm,pins = <28 29 30 31>;
++ brcm,function = <6>; /* alt2 */
++ };
++
++ audio_pins: audio_pins {
++ brcm,pins = <40 45>;
++ brcm,function = <4>;
++ };
++};
++
++&uart0 {
++ status = "okay";
++};
++
++&spi0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
++
++ spidev0: spidev@0{
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ };
++
++ spidev1: spidev@1{
++ compatible = "spidev";
++ reg = <1>; /* CE1 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ };
++};
++
++/delete-node/ &i2c0mux;
++
++i2c0: &i2c0if {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c0_pins>;
++ clock-frequency = <100000>;
++};
++
++i2c_csi_dsi: &i2c1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2c1_pins>;
++ clock-frequency = <100000>;
++};
++
++/ {
++ aliases {
++ i2c0 = &i2c0;
++ };
++
++ __overrides__ {
++ i2c0 = <&i2c0>, "status";
++ };
++};
++
++&i2c2 {
++ clock-frequency = <100000>;
++};
++
++&i2s {
++ pinctrl-names = "default";
++ pinctrl-0 = <&i2s_pins>;
++};
++
++&leds {
++ act_led: act {
++ label = "led0";
++ linux,default-trigger = "mmc0";
++ gpios = <&gpio 16 1>;
++ };
++};
++
++&hdmi {
++ hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
++};
++
++&audio {
++ pinctrl-names = "default";
++ pinctrl-0 = <&audio_pins>;
++};
++
++/ {
++ __overrides__ {
++ act_led_gpio = <&act_led>,"gpios:4";
++ act_led_activelow = <&act_led>,"gpios:8";
++ act_led_trigger = <&act_led>,"linux,default-trigger";
++ };
++};
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -110,6 +110,10 @@
+ status = "disabled";
+ };
+
++&i2c0mux {
++ status = "disabled";
++};
++
+ &i2c1 {
+ status = "disabled";
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch b/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch
new file mode 100644
index 0000000000..fa5a2d4f54
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch
@@ -0,0 +1,117 @@
+From 6fe41cac345c8010943defa4ebc2496dd7ca05a1 Mon Sep 17 00:00:00 2001
+From: Hristo Venev <hristo@venev.name>
+Date: Wed, 22 Apr 2020 13:40:47 +0300
+Subject: [PATCH] dts: bcm2711: set #size-cells = <2>
+
+There already is one 4 GiB range, and one more will appear when high
+peripheral mode is enabled.
+
+Signed-off-by: Hristo Venev <hristo@venev.name>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 27 +++++++++++++--------------
+ arch/arm/boot/dts/bcm2711.dtsi | 10 +++++-----
+ 2 files changed, 18 insertions(+), 19 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -65,17 +65,16 @@
+ };
+
+ &scb {
+- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
+- <0x0 0x40000000 0x0 0xff800000 0x00800000>,
+- <0x6 0x00000000 0x6 0x00000000 0x40000000>,
+- <0x0 0x00000000 0x0 0x00000000 0xfc000000>;
+- dma-ranges = <0x0 0x00000000 0x0 0x00000000 0xfc000000>,
+- <0x1 0x00000000 0x1 0x00000000 0x80000000>,
+- <0x1 0x80000000 0x1 0x80000000 0x80000000>;
++ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x0 0x03800000>,
++ <0x0 0x40000000 0x0 0xff800000 0x0 0x00800000>,
++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>,
++ <0x0 0x00000000 0x0 0x00000000 0x0 0xfc000000>;
++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x0 0xfc000000>,
++ <0x1 0x00000000 0x1 0x00000000 0x1 0x00000000>;
+
+ dma40: dma@7e007b00 {
+ compatible = "brcm,bcm2711-dma";
+- reg = <0x0 0x7e007b00 0x400>;
++ reg = <0x0 0x7e007b00 0x0 0x400>;
+ interrupts =
+ <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 11 */
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 12 */
+@@ -91,39 +90,39 @@
+
+ vchiq: mailbox@7e00b840 {
+ compatible = "brcm,bcm2711-vchiq";
+- reg = <0 0x7e00b840 0x3c>;
++ reg = <0 0x7e00b840 0x0 0x3c>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ xhci: xhci@7e9c0000 {
+ compatible = "generic-xhci";
+ status = "disabled";
+- reg = <0x0 0x7e9c0000 0x100000>;
++ reg = <0x0 0x7e9c0000 0x0 0x100000>;
+ interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ hevc-decoder@7eb00000 {
+ compatible = "raspberrypi,rpivid-hevc-decoder";
+- reg = <0x0 0x7eb00000 0x10000>;
++ reg = <0x0 0x7eb00000 0x0 0x10000>;
+ status = "okay";
+ };
+
+ rpivid-local-intc@7eb10000 {
+ compatible = "raspberrypi,rpivid-local-intc";
+- reg = <0x0 0x7eb10000 0x1000>;
++ reg = <0x0 0x7eb10000 0x0 0x1000>;
+ status = "okay";
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ h264-decoder@7eb20000 {
+ compatible = "raspberrypi,rpivid-h264-decoder";
+- reg = <0x0 0x7eb20000 0x10000>;
++ reg = <0x0 0x7eb20000 0x0 0x10000>;
+ status = "okay";
+ };
+
+ vp9-decoder@7eb30000 {
+ compatible = "raspberrypi,rpivid-vp9-decoder";
+- reg = <0x0 0x7eb30000 0x10000>;
++ reg = <0x0 0x7eb30000 0x0 0x10000>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -445,14 +445,14 @@
+ scb {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+- #size-cells = <1>;
++ #size-cells = <2>;
+
+- ranges = <0x0 0x7c000000 0x0 0xfc000000 0x03800000>,
+- <0x6 0x00000000 0x6 0x00000000 0x40000000>;
++ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x0 0x03800000>,
++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>;
+
+ pcie0: pcie@7d500000 {
+ compatible = "brcm,bcm2711-pcie";
+- reg = <0x0 0x7d500000 0x9310>;
++ reg = <0x0 0x7d500000 0x0 0x9310>;
+ device_type = "pci";
+ #address-cells = <3>;
+ #interrupt-cells = <1>;
+@@ -480,7 +480,7 @@
+
+ genet: ethernet@7d580000 {
+ compatible = "brcm,bcm2711-genet-v5";
+- reg = <0x0 0x7d580000 0x10000>;
++ reg = <0x0 0x7d580000 0x0 0x10000>;
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch
new file mode 100644
index 0000000000..e24f5de000
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch
@@ -0,0 +1,140 @@
+From f6d6731d8e896ab029466547dfa66d91a9a6b73a Mon Sep 17 00:00:00 2001
+From: Hristo Venev <hristo@venev.name>
+Date: Wed, 22 Apr 2020 16:34:59 +0300
+Subject: [PATCH] dts: bcm2711: add "High Peripheral" mode overlay
+
+The following addresses change:
+
+ - 0xfc00_0000 -> 0x4_7c00_0000
+ - 0xff80_0000 -> 0x4_c000_0000
+
+The range 0xfc00_0000-0xffff_ffff becomes available as system RAM on
+devices with >= 4 GiB of RAM. Firmware should initialize the memory node
+appropriately.
+
+Signed-off-by: Hristo Venev <hristo@venev.name>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 2 +-
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 ++
+ .../boot/dts/overlays/highperi-overlay.dts | 64 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++
+ 5 files changed, 76 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/overlays/highperi-overlay.dts
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -16,7 +16,7 @@
+ compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu";
+ };
+
+- v3dbus {
++ v3dbus: v3dbus {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <2>;
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -64,6 +64,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ hifiberry-dacplushd.dtbo \
+ hifiberry-digi.dtbo \
+ hifiberry-digi-pro.dtbo \
++ highperi.dtbo \
+ hy28a.dtbo \
+ hy28b.dtbo \
+ hy28b-2017.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1019,6 +1019,12 @@ Load: dtoverlay=hifiberry-digi-pro
+ Params: <None>
+
+
++Name: highperi
++Info: Enables "High Peripheral" mode
++Load: dtoverlay=highperi
++Params: <None>
++
++
+ Name: hy28a
+ Info: HY28A - 2.8" TFT LCD Display Module by HAOYU Electronics
+ Default values match Texy's display shield
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/highperi-overlay.dts
+@@ -0,0 +1,64 @@
++/*
++ * highperi.dts
++ */
++
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2711";
++
++ fragment@0 {
++ target = <&soc>;
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0x7c000000 0x4 0x7c000000 0x04000000>,
++ <0x40000000 0x4 0xc0000000 0x00800000>;
++ };
++ };
++
++ fragment@1 {
++ target = <&scb>;
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ __overlay__ {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ ranges = <0x0 0x7c000000 0x4 0x7c000000 0x0 0x04000000>,
++ <0x0 0x40000000 0x4 0xc0000000 0x0 0x00800000>,
++ <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>,
++ <0x0 0x00000000 0x0 0x00000000 0x1 0x00000000>;
++ dma-ranges = <0x0 0x00000000 0x0 0x00000000 0x2 0x00000000>;
++ };
++ };
++
++ fragment@2 {
++ target = <&v3dbus>;
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <2>;
++ ranges = <0x7c500000 0x4 0x7c500000 0x0 0x03300000>,
++ <0x40000000 0x4 0xc0000000 0x0 0x00800000>;
++ };
++ };
++
++ fragment@3 {
++ target = <&emmc2bus>;
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ __overlay__ {
++ #address-cells = <2>;
++ #size-cells = <1>;
++ ranges = <0x0 0x7e000000 0x4 0x7e000000 0x01800000>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -5,6 +5,10 @@
+ deprecated = "use i2c-sensor,bmp085";
+ };
+
++ highperi {
++ bcm2711;
++ };
++
+ i2c0-bcm2708 {
+ deprecated = "use i2c0";
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch b/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch
new file mode 100644
index 0000000000..fa01cd8532
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch
@@ -0,0 +1,32 @@
+From 4c7f1a1c3d1bfd35b5a4089766ff0882d7b4ee0d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Apr 2020 13:41:10 +0100
+Subject: [PATCH] Revert "spi: spidev: Fix CS polarity if GPIO
+ descriptors are used"
+
+This reverts commit 83b2a8fe43bda0c11981ad6afa5dd0104d78be28.
+---
+ drivers/spi/spidev.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -394,7 +394,6 @@ spidev_ioctl(struct file *filp, unsigned
+ else
+ retval = get_user(tmp, (u32 __user *)arg);
+ if (retval == 0) {
+- struct spi_controller *ctlr = spi->controller;
+ u32 save = spi->mode;
+
+ if (tmp & ~SPI_MODE_MASK) {
+@@ -402,10 +401,6 @@ spidev_ioctl(struct file *filp, unsigned
+ break;
+ }
+
+- if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
+- ctlr->cs_gpiods[spi->chip_select])
+- tmp |= SPI_CS_HIGH;
+-
+ tmp |= spi->mode & ~SPI_MODE_MASK;
+ spi->mode = (u16)tmp;
+ retval = spi_setup(spi);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch b/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch
new file mode 100644
index 0000000000..8068ecc9c4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch
@@ -0,0 +1,55 @@
+From b4659f44df3454c6b37ba206a0347af3b8d6a744 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Apr 2020 13:30:49 +0100
+Subject: [PATCH] spi: use_gpio_descriptor fixup moved to spi_setup
+
+Commits [1] and [2] including code that forces SPI_CS_HIGH for SPI
+controllers that use GPIO descriptors, the SPI_CS_HIGH flag being
+there to avoid a double-negation (since SPI CS is usually active-low).
+The motivation for pushing the knowledge of the required polarity into
+the GPIO descriptor allows the switch to an output to request the
+correct inactive level, avoiding a needless glitch.
+
+The problem with setting the flag early as [1] does is that it appears
+in the mode field that is passed to client drivers during their probing,
+when they may want to choose SPI_POL, SPI_PHA and (just possibly)
+SPI_CS_HIGH. Since SPI_CS_HIGH is the exception, most drivers won't
+set it and the anti-negation negation is lost. [2] acknowledges that
+problem and patches things up for the case of users of spidev, but
+omits regular kernel-mode drivers.
+
+Downstream commit [3] moves the forcing of SPI_CS_HIGH to spi_setup,
+after the driver probing. Since this code is called before any CS
+manipulation it is early enough to be effective, but late enough that
+clients have already had their chance to change the mode field.
+
+This is a partial reversion of [1], and is accompanied by a complete
+reversion of [2], neither of which is needed any longer.
+
+[1] f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs")
+[2] 83b2a8fe43bd ("spi: spidev: Fix CS polarity if GPIO descriptors are used")
+[3] <varies> ("spi: Force CS_HIGH if GPIO descriptors are used")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi.c | 9 ---------
+ 1 file changed, 9 deletions(-)
+
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -1775,15 +1775,6 @@ static int of_spi_parse_dt(struct spi_co
+ }
+ spi->chip_select = value;
+
+- /*
+- * For descriptors associated with the device, polarity inversion is
+- * handled in the gpiolib, so all gpio chip selects are "active high"
+- * in the logical sense, the gpiolib will invert the line if need be.
+- */
+- if ((ctlr->use_gpio_descriptors) && ctlr->cs_gpiods &&
+- ctlr->cs_gpiods[spi->chip_select])
+- spi->mode |= SPI_CS_HIGH;
+-
+ /* Device speed */
+ rc = of_property_read_u32(nc, "spi-max-frequency", &value);
+ if (rc) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch b/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch
new file mode 100644
index 0000000000..4a1757dd44
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch
@@ -0,0 +1,30 @@
+From 096fc044170aa40a99dd66b0a8b072ef76327ddb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 24 Apr 2020 15:17:06 +0100
+Subject: [PATCH] overlays: rpivid-v4l2 also needs size-cells = 2
+
+Fixes: "dts: bcm2711: set #size-cells = <2>"
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+@@ -13,11 +13,12 @@
+ __overlay__ {
+ /* needed to avoid dtc warning */
+ #address-cells = <2>;
+- #size-cells = <1>;
++ #size-cells = <2>;
++
+ codec@7eb10000 {
+ compatible = "raspberrypi,rpivid-vid-decoder";
+- reg = <0x0 0x7eb10000 0x1000>, /* INTC */
+- <0x0 0x7eb00000 0x10000>; /* HEVC */
++ reg = <0x0 0x7eb10000 0x0 0x1000>, /* INTC */
++ <0x0 0x7eb00000 0x0 0x10000>; /* HEVC */
+ reg-names = "intc",
+ "hevc";
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch b/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch
new file mode 100644
index 0000000000..4d43c97e56
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch
@@ -0,0 +1,49 @@
+From 53d7c93a14d1e0804a96e3a21f472ba5ff033b14 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 21 Apr 2020 16:26:03 +0100
+Subject: [PATCH] media: bcm2835-unicam: Re-fetch mbus code from subdev
+ on a g_fmt call
+
+The sensor subdevice may change the Bayer order if a H/V flip is
+requested after a s_fmt call. Unicam g_fmt must call the subdev get_fmt
+in case this has happened and return out the correct format 4cc.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 21 ++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -967,11 +967,30 @@ static int unicam_enum_fmt_vid_cap(struc
+ static int unicam_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+ {
++ struct v4l2_mbus_framefmt mbus_fmt = {0};
+ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ const struct unicam_fmt *fmt = NULL;
++ int ret;
+
+- if (node->pad_id == METADATA_PAD)
++ if (node->pad_id != IMAGE_PAD)
+ return -EINVAL;
+
++ /*
++ * If a flip has occurred in the sensor, the fmt code might have
++ * changed. So we will need to re-fetch the format from the subdevice.
++ */
++ ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id);
++ if (ret)
++ return -EINVAL;
++
++ /* Find the V4L2 format from mbus code. We must match a known format. */
++ fmt = find_format_by_code(mbus_fmt.code);
++ if (!fmt)
++ return -EINVAL;
++
++ node->fmt = fmt;
++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ *f = node->v_fmt;
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch b/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch
new file mode 100644
index 0000000000..9f41783b46
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch
@@ -0,0 +1,337 @@
+From b5d50012157f909eff0e8775c1e040b7dfde0705 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 23 Apr 2020 10:18:15 +0100
+Subject: [PATCH] uapi: bcm2835-isp: Add bcm2835-isp uapi header file
+
+This file defines the userland interface to the bcm2835-isp driver
+that will follow in a separate commit.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ include/uapi/linux/bcm2835-isp.h | 320 +++++++++++++++++++++++++++++++
+ 1 file changed, 320 insertions(+)
+ create mode 100644 include/uapi/linux/bcm2835-isp.h
+
+--- /dev/null
++++ b/include/uapi/linux/bcm2835-isp.h
+@@ -0,0 +1,320 @@
++/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
++/*
++ * bcm2835-isp.h
++ *
++ * BCM2835 ISP driver - user space header file.
++ *
++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
++ *
++ * Author: Naushir Patuck (naush@raspberrypi.com)
++ *
++ */
++
++#ifndef __BCM2835_ISP_H_
++#define __BCM2835_ISP_H_
++
++#include <linux/v4l2-controls.h>
++
++#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001)
++#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002)
++#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003)
++#define V4L2_CID_USER_BCM2835_ISP_GEQ \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004)
++#define V4L2_CID_USER_BCM2835_ISP_GAMMA \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005)
++#define V4L2_CID_USER_BCM2835_ISP_DENOISE \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006)
++#define V4L2_CID_USER_BCM2835_ISP_SHARPEN \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007)
++#define V4L2_CID_USER_BCM2835_ISP_DPC \
++ (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008)
++
++/*
++ * All structs below are directly mapped onto the equivalent structs in
++ * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
++ * for convenience.
++ */
++
++/**
++ * struct bcm2835_isp_rational - Rational value type.
++ *
++ * @num: Numerator.
++ * @den: Denominator.
++ */
++struct bcm2835_isp_rational {
++ __s32 num;
++ __s32 den;
++};
++
++/**
++ * struct bcm2835_isp_ccm - Colour correction matrix.
++ *
++ * @ccm: 3x3 correction matrix coefficients.
++ * @offsets: 1x3 correction offsets.
++ */
++struct bcm2835_isp_ccm {
++ struct bcm2835_isp_rational ccm[3][3];
++ __s32 offsets[3];
++};
++
++/**
++ * struct bcm2835_isp_custom_ccm - Custom CCM applied with the
++ * V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl.
++ *
++ * @enabled: Enable custom CCM.
++ * @ccm: Custom CCM coefficients and offsets.
++ */
++struct bcm2835_isp_custom_ccm {
++ __u32 enabled;
++ struct bcm2835_isp_ccm ccm;
++};
++
++/**
++ * enum bcm2835_isp_gain_format - format of the gains in the lens shading
++ * tables used with the
++ * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl.
++ *
++ * @GAIN_FORMAT_U0P8_1: Gains are u0.8 format, starting at 1.0
++ * @GAIN_FORMAT_U1P7_0: Gains are u1.7 format, starting at 0.0
++ * @GAIN_FORMAT_U1P7_1: Gains are u1.7 format, starting at 1.0
++ * @GAIN_FORMAT_U2P6_0: Gains are u2.6 format, starting at 0.0
++ * @GAIN_FORMAT_U2P6_1: Gains are u2.6 format, starting at 1.0
++ * @GAIN_FORMAT_U3P5_0: Gains are u3.5 format, starting at 0.0
++ * @GAIN_FORMAT_U3P5_1: Gains are u3.5 format, starting at 1.0
++ * @GAIN_FORMAT_U4P10: Gains are u4.10 format, starting at 0.0
++ */
++enum bcm2835_isp_gain_format {
++ GAIN_FORMAT_U0P8_1 = 0,
++ GAIN_FORMAT_U1P7_0 = 1,
++ GAIN_FORMAT_U1P7_1 = 2,
++ GAIN_FORMAT_U2P6_0 = 3,
++ GAIN_FORMAT_U2P6_1 = 4,
++ GAIN_FORMAT_U3P5_0 = 5,
++ GAIN_FORMAT_U3P5_1 = 6,
++ GAIN_FORMAT_U4P10 = 7,
++};
++
++/**
++ * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the
++ * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING
++ * ctrl.
++ *
++ * @enabled: Enable lens shading.
++ * @grid_cell_size: Size of grid cells in samples (16, 32, 64, 128 or 256).
++ * @grid_width: Width of lens shading tables in grid cells.
++ * @grid_stride: Row to row distance (in grid cells) between grid cells
++ * in the same horizontal location.
++ * @grid_height: Height of lens shading tables in grid cells.
++ * @mem_handle_table: Memory handle to the tables.
++ * @ref_transform: Reference transform - unsupported, please pass zero.
++ * @corner_sampled: Whether the gains are sampled at the corner points
++ * of the grid cells or in the cell centres.
++ * @gain_format: Format of the gains (see enum &bcm2835_isp_gain_format).
++ */
++struct bcm2835_isp_lens_shading {
++ __u32 enabled;
++ __u32 grid_cell_size;
++ __u32 grid_width;
++ __u32 grid_stride;
++ __u32 grid_height;
++ __u32 mem_handle_table;
++ __u32 ref_transform;
++ __u32 corner_sampled;
++ __u32 gain_format;
++};
++
++/**
++ * struct bcm2835_isp_black_level - Sensor black level set with the
++ * V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl.
++ *
++ * @enabled: Enable black level.
++ * @black_level_r: Black level for red channel.
++ * @black_level_g: Black level for green channels.
++ * @black_level_b: Black level for blue channel.
++ */
++struct bcm2835_isp_black_level {
++ __u32 enabled;
++ __u16 black_level_r;
++ __u16 black_level_g;
++ __u16 black_level_b;
++ __u8 pad_[2]; /* Unused */
++};
++
++/**
++ * struct bcm2835_isp_geq - Green equalisation parameters set with the
++ * V4L2_CID_USER_BCM2835_ISP_GEQ ctrl.
++ *
++ * @enabled: Enable green equalisation.
++ * @offset: Fixed offset of the green equalisation threshold.
++ * @slope: Slope of the green equalisation threshold.
++ */
++struct bcm2835_isp_geq {
++ __u32 enabled;
++ __u32 offset;
++ struct bcm2835_isp_rational slope;
++};
++
++#define BCM2835_NUM_GAMMA_PTS 33
++
++/**
++ * struct bcm2835_isp_gamma - Gamma parameters set with the
++ * V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl.
++ *
++ * @enabled: Enable gamma adjustment.
++ * @X: X values of the points defining the gamma curve.
++ * Values should be scaled to 16 bits.
++ * @Y: Y values of the points defining the gamma curve.
++ * Values should be scaled to 16 bits.
++ */
++struct bcm2835_isp_gamma {
++ __u32 enabled;
++ __u16 x[BCM2835_NUM_GAMMA_PTS];
++ __u16 y[BCM2835_NUM_GAMMA_PTS];
++};
++
++/**
++ * struct bcm2835_isp_denoise - Denoise parameters set with the
++ * V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl.
++ *
++ * @enabled: Enable denoise.
++ * @constant: Fixed offset of the noise threshold.
++ * @slope: Slope of the noise threshold.
++ * @strength: Denoise strength between 0.0 (off) and 1.0 (maximum).
++ */
++struct bcm2835_isp_denoise {
++ __u32 enabled;
++ __u32 constant;
++ struct bcm2835_isp_rational slope;
++ struct bcm2835_isp_rational strength;
++};
++
++/**
++ * struct bcm2835_isp_sharpen - Sharpen parameters set with the
++ * V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl.
++ *
++ * @enabled: Enable sharpening.
++ * @threshold: Threshold at which to start sharpening pixels.
++ * @strength: Strength with which pixel sharpening increases.
++ * @limit: Limit to the amount of sharpening applied.
++ */
++struct bcm2835_isp_sharpen {
++ __u32 enabled;
++ struct bcm2835_isp_rational threshold;
++ struct bcm2835_isp_rational strength;
++ struct bcm2835_isp_rational limit;
++};
++
++/**
++ * enum bcm2835_isp_dpc_mode - defective pixel correction (DPC) strength.
++ *
++ * @DPC_MODE_OFF: No DPC.
++ * @DPC_MODE_NORMAL: Normal DPC.
++ * @DPC_MODE_STRONG: Strong DPC.
++ */
++enum bcm2835_isp_dpc_mode {
++ DPC_MODE_OFF = 0,
++ DPC_MODE_NORMAL = 1,
++ DPC_MODE_STRONG = 2,
++};
++
++/**
++ * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set
++ * with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl.
++ *
++ * @enabled: Enable DPC.
++ * @strength: DPC strength (see enum &bcm2835_isp_dpc_mode).
++ */
++struct bcm2835_isp_dpc {
++ __u32 enabled;
++ __u32 strength;
++};
++
++/*
++ * ISP statistics structures.
++ *
++ * The bcm2835_isp_stats structure is generated at the output of the
++ * statistics node. Note that this does not directly map onto the statistics
++ * output of the ISP HW. Instead, the MMAL firmware code maps the HW statistics
++ * to the bcm2835_isp_stats structure.
++ */
++#define DEFAULT_AWB_REGIONS_X 16
++#define DEFAULT_AWB_REGIONS_Y 12
++
++#define NUM_HISTOGRAMS 2
++#define NUM_HISTOGRAM_BINS 128
++#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y)
++#define FLOATING_REGIONS 16
++#define AGC_REGIONS 16
++#define FOCUS_REGIONS 12
++
++/**
++ * struct bcm2835_isp_stats_hist - Histogram statistics
++ *
++ * @r_hist: Red channel histogram.
++ * @g_hist: Combined green channel histogram.
++ * @b_hist: Blue channel histogram.
++ */
++struct bcm2835_isp_stats_hist {
++ __u32 r_hist[NUM_HISTOGRAM_BINS];
++ __u32 g_hist[NUM_HISTOGRAM_BINS];
++ __u32 b_hist[NUM_HISTOGRAM_BINS];
++};
++
++/**
++ * struct bcm2835_isp_stats_region - Region sums.
++ *
++ * @counted: The number of 2x2 bayer tiles accumulated.
++ * @notcounted: The number of 2x2 bayer tiles not accumulated.
++ * @r_sum: Total sum of counted pixels in the red channel for a region.
++ * @g_sum: Total sum of counted pixels in the green channel for a region.
++ * @b_sum: Total sum of counted pixels in the blue channel for a region.
++ */
++struct bcm2835_isp_stats_region {
++ __u32 counted;
++ __u32 notcounted;
++ __u64 r_sum;
++ __u64 g_sum;
++ __u64 b_sum;
++};
++
++/**
++ * struct bcm2835_isp_stats_focus - Focus statistics.
++ *
++ * @contrast_val: Focus measure - accumulated output of the focus filter.
++ * In the first dimension, index [0] counts pixels below a
++ * preset threshold, and index [1] counts pixels above the
++ * threshold. In the second dimension, index [0] uses the
++ * first predefined filter, and index [1] uses the second
++ * predefined filter.
++ * @contrast_val_num: The number of counted pixels in the above accumulation.
++ */
++struct bcm2835_isp_stats_focus {
++ __u64 contrast_val[2][2];
++ __u32 contrast_val_num[2][2];
++};
++
++/**
++ * struct bcm2835_isp_stats - ISP statistics.
++ *
++ * @version: Version of the bcm2835_isp_stats structure.
++ * @size: Size of the bcm2835_isp_stats structure.
++ * @hist: Histogram statistics for the entire image.
++ * @awb_stats: Statistics for the regions defined for AWB calculations.
++ * @floating_stats: Statistics for arbitrarily placed (floating) regions.
++ * @agc_stats: Statistics for the regions defined for AGC calculations.
++ * @focus_stats: Focus filter statistics for the focus regions.
++ */
++struct bcm2835_isp_stats {
++ __u32 version;
++ __u32 size;
++ struct bcm2835_isp_stats_hist hist[NUM_HISTOGRAMS];
++ struct bcm2835_isp_stats_region awb_stats[AWB_REGIONS];
++ struct bcm2835_isp_stats_region floating_stats[FLOATING_REGIONS];
++ struct bcm2835_isp_stats_region agc_stats[AGC_REGIONS];
++ struct bcm2835_isp_stats_focus focus_stats[FOCUS_REGIONS];
++};
++
++#endif /* __BCM2835_ISP_H_ */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch b/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch
new file mode 100644
index 0000000000..1af97e8351
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch
@@ -0,0 +1,94 @@
+From 8dbbff7b75eee842c00ebaa53fa0a34b3e9bbcaa Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 23 Apr 2020 10:20:26 +0100
+Subject: [PATCH] media: uapi: v4l2-core: Add ISP statistics output
+ V4L2 fourcc type
+
+Add V4L2_META_FMT_BCM2835_ISP_STATS V4L2 format type.
+
+This new format will be used by the BCM2835 ISP device to return
+out ISP statistics for 3A.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ Documentation/media/uapi/v4l/meta-formats.rst | 1 +
+ .../v4l/pixfmt-meta-bcm2835-isp-stats.rst | 41 +++++++++++++++++++
+ drivers/media/v4l2-core/v4l2-ioctl.c | 1 +
+ include/uapi/linux/videodev2.h | 1 +
+ 4 files changed, 44 insertions(+)
+ create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst
+
+--- a/Documentation/media/uapi/v4l/meta-formats.rst
++++ b/Documentation/media/uapi/v4l/meta-formats.rst
+@@ -19,6 +19,7 @@ These formats are used for the :ref:`met
+ .. toctree::
+ :maxdepth: 1
+
++ pixfmt-meta-bcm2835-isp-stats
+ pixfmt-meta-d4xx
+ pixfmt-meta-intel-ipu3
+ pixfmt-meta-sensor-data
+--- /dev/null
++++ b/Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst
+@@ -0,0 +1,41 @@
++.. Permission is granted to copy, distribute and/or modify this
++.. document under the terms of the GNU Free Documentation License,
++.. Version 1.1 or any later version published by the Free Software
++.. Foundation, with no Invariant Sections, no Front-Cover Texts
++.. and no Back-Cover Texts. A copy of the license is included at
++.. Documentation/media/uapi/fdl-appendix.rst.
++..
++.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
++
++.. _v4l2-meta-fmt-bcm2835-isp-stats:
++
++*****************************************
++V4L2_META_FMT_BCM2835_ISP_STATS ('BSTA')
++*****************************************
++
++BCM2835 ISP Statistics
++
++Description
++===========
++
++The BCM2835 ISP hardware calculate image statistics for an input Bayer frame.
++These statistics are obtained from the "bcm2835-isp0-capture3" device node
++using the :c:type:`v4l2_meta_format` interface. They are formatted as described
++by the :c:type:`bcm2835_isp_stats` structure below.
++
++.. code-block:: c
++
++ #define DEFAULT_AWB_REGIONS_X 16
++ #define DEFAULT_AWB_REGIONS_Y 12
++
++ #define NUM_HISTOGRAMS 2
++ #define NUM_HISTOGRAM_BINS 128
++ #define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y)
++ #define FLOATING_REGIONS 16
++ #define AGC_REGIONS 16
++ #define FOCUS_REGIONS 12
++
++.. kernel-doc:: include/uapi/linux/bcm2835-isp.h
++ :functions: bcm2835_isp_stats_hist bcm2835_isp_stats_region
++ bcm2835_isp_stats_focus bcm2835_isp_stats
++
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1333,6 +1333,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break;
+ case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break;
+ case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break;
++ case V4L2_META_FMT_BCM2835_ISP_STATS: descr = "BCM2835 ISP Image Statistics"; break;
+
+ default:
+ /* Compressed formats */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -770,6 +770,7 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */
+ #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */
+ #define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */
++#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') /* BCM2835 ISP image statistics output */
+
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
diff --git a/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch b/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch
new file mode 100644
index 0000000000..96e6e124af
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch
@@ -0,0 +1,169 @@
+From 23afbeb993acfd94fa21341f01819ab6505444d2 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 21 Apr 2020 15:06:19 +0100
+Subject: [PATCH] media: uapi: v4l-ctrls: Add CID base for the
+ bcm2835-isp driver
+
+We are reserving controls for the new bcm2835-isp driver.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/v4l-drivers/bcm2835-isp.rst | 127 ++++++++++++++++++
+ Documentation/media/v4l-drivers/index.rst | 1 +
+ include/uapi/linux/v4l2-controls.h | 4 +
+ 3 files changed, 132 insertions(+)
+ create mode 100644 Documentation/media/v4l-drivers/bcm2835-isp.rst
+
+--- /dev/null
++++ b/Documentation/media/v4l-drivers/bcm2835-isp.rst
+@@ -0,0 +1,127 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++BCM2835 ISP Driver
++==================
++
++Introduction
++------------
++
++The BCM2835 Image Sensor Pipeline (ISP) is a fixed function hardware pipeline
++for performing image processing operations. Images are fed to the input
++of the ISP through memory frame buffers. These images may be in various YUV,
++RGB, or Bayer formats. A typical use case would have Bayer images obtained from
++an image sensor by the BCM2835 Unicam peripheral, written to a memory
++frame buffer, and finally fed into the input of the ISP. Two concurrent output
++images may be generated in YUV or RGB format at different resolutions.
++Statistics output is also generated for Bayer input images.
++
++The bcm2835-isp driver exposes the following media pads as V4L2 device nodes:
++
++.. tabularcolumns:: |l|l|l|l|
++
++.. cssclass: longtable
++
++.. flat-table::
++
++ * - *Pad*
++ - *Direction*
++ - *Purpose*
++ - *Formats*
++
++ * - "bcm2835-isp0-output0"
++ - sink
++ - Accepts Bayer, RGB or YUV format frame buffers as input to the ISP HW
++ pipeline.
++ - :ref:`RAW8 <V4L2-PIX-FMT-SRGGB8>`,
++ :ref:`RAW10P <V4L2-PIX-FMT-SRGGB10P>`,
++ :ref:`RAW12P <V4L2-PIX-FMT-SRGGB12P>`,
++ :ref:`RAW14P <V4L2-PIX-FMT-SRGGB14P>`,
++ :ref:`RAW16 <V4L2-PIX-FMT-SRGGB16>`,
++ :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`,
++ :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`,
++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`
++
++ * - "bcm2835-isp0-capture1"
++ - source
++ - High resolution YUV or RGB processed output from the ISP.
++ - :ref:`RGB565 <V4L2-PIX-FMT-RGB565>`,
++ :ref:`RGB24/BGR24 <V4L2-PIX-FMT-RGB24>`,
++ :ref:`ABGR32 <V4L2-PIX-FMT-ABGR32>`,
++ :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`.
++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`,
++ :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`,
++
++ * - "bcm2835-isp0-capture2"
++ - source
++ - Low resolution YUV processed output from the ISP. The output of
++ this pad cannot have a resolution larger than the "bcm2835-isp0-capture1" pad in any dimension.
++ - :ref:`YUYV <V4L2-PIX-FMT-YUYV>`,
++ :ref:`YVYU <V4L2-PIX-FMT-YVYU>`,
++ :ref:`UYVY <V4L2-PIX-FMT-UYVY>`,
++ :ref:`VYUY <V4L2-PIX-FMT-VYUY>`.
++ :ref:`YUV420/YVU420 <V4L2-PIX-FMT-YUV420>`,
++ :ref:`NV12/NV21 <V4L2-PIX-FMT-NV12>`,
++
++ * - "bcm2835-isp0-capture1"
++ - source
++ - Image statistics calculated from the input image provided on the
++ "bcm2835-isp0-output0" pad. Statistics are only available for Bayer
++ format input images.
++ - :ref:`v4l2-meta-fmt-bcm2835-isp-stats`.
++
++Pipeline Configuration
++----------------------
++
++The ISP pipeline can be configure through user-space by calling
++:ref:`VIDIOC_S_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` on the “bcm2835-isp0-output0”
++node with the appropriate parameters as shown in the table below.
++
++.. tabularcolumns:: |p{2cm}|p{5.0cm}|
++
++.. cssclass: longtable
++
++.. flat-table::
++
++ * - *id*
++ - *Parameter*
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_CC_MATRIX``
++ - struct :c:type:`bcm2835_isp_custom_ccm`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_LENS_SHADING``
++ - struct :c:type:`bcm2835_isp_lens_shading`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL``
++ - struct :c:type:`bcm2835_isp_black_level`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_GEQ``
++ - struct :c:type:`bcm2835_isp_geq`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_GAMMA``
++ - struct :c:type:`bcm2835_isp_gamma`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_DENOISE``
++ - struct :c:type:`bcm2835_isp_denoise`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_SHARPEN``
++ - struct :c:type:`bcm2835_isp_sharpen`
++
++ * - ``V4L2_CID_USER_BCM2835_ISP_DPC``
++ - struct :c:type:`bcm2835_isp_dpc`
++
++++++++++++++++++++++++++
++Configuration Parameters
++++++++++++++++++++++++++
++
++.. kernel-doc:: include/uapi/linux/bcm2835-isp.h
++ :functions: bcm2835_isp_rational bcm2835_isp_ccm bcm2835_isp_custom_ccm
++ bcm2835_isp_gain_format bcm2835_isp_lens_shading
++ bcm2835_isp_black_level bcm2835_isp_geq bcm2835_isp_gamma
++ bcm2835_isp_denoise bcm2835_isp_sharpen
++ bcm2835_isp_dpc_mode bcm2835_isp_dpc
+--- a/Documentation/media/v4l-drivers/index.rst
++++ b/Documentation/media/v4l-drivers/index.rst
+@@ -35,6 +35,7 @@ For more details see the file COPYING in
+ v4l-with-ir
+ tuners
+ cardlist
++ bcm2835-isp
+ bttv
+ cafe_ccic
+ cpia2
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -192,6 +192,10 @@ enum v4l2_colorfx {
+ * We reserve 16 controls for this driver. */
+ #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0)
+
++/* The base for the bcm2835-isp driver controls.
++ * We reserve 16 controls for this driver. */
++#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10c0)
++
+ /* MPEG-class control IDs */
+ /* The MPEG controls are applicable to all codec controls
+ * and the 'MPEG' part of the define is historical */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch b/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch
new file mode 100644
index 0000000000..f428ce5e9f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch
@@ -0,0 +1,116 @@
+From 3319293da05e444e0673c1aba5507e539ccff043 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 23 Apr 2020 10:12:24 +0100
+Subject: [PATCH] staging: mmal-vchiq: Fix formatting errors in
+ mmal_parameters.h
+
+No functional changes in this commit.
+
+- Remove erroneous whitespace.
+- Remove _t postfix label on structs and enums.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../bcm2835-camera/bcm2835-camera.c | 2 +-
+ .../vchiq-mmal/mmal-parameters.h | 46 +++++++++----------
+ 2 files changed, 24 insertions(+), 24 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
++++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
+@@ -1523,7 +1523,7 @@ static int get_num_cameras(struct vchiq_
+ {
+ int ret;
+ struct vchiq_mmal_component *cam_info_component;
+- struct mmal_parameter_camera_info_t cam_info = {0};
++ struct mmal_parameter_camera_info cam_info = {0};
+ u32 param_size = sizeof(cam_info);
+ int i;
+
+--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
+@@ -23,21 +23,21 @@
+ #define MMAL_PARAMETERS_H
+
+ /** Common parameter ID group, used with many types of component. */
+-#define MMAL_PARAMETER_GROUP_COMMON (0 << 16)
++#define MMAL_PARAMETER_GROUP_COMMON (0 << 16)
+ /** Camera-specific parameter ID group. */
+-#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16)
++#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16)
+ /** Video-specific parameter ID group. */
+-#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16)
++#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16)
+ /** Audio-specific parameter ID group. */
+-#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16)
++#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16)
+ /** Clock-specific parameter ID group. */
+-#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16)
++#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16)
+ /** Miracast-specific parameter ID group. */
+-#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16)
++#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16)
+
+ /* Common parameters */
+ enum mmal_parameter_common_type {
+- /**< Never a valid parameter ID */
++ /**< Never a valid parameter ID */
+ MMAL_PARAMETER_UNUSED = MMAL_PARAMETER_GROUP_COMMON,
+
+ /**< MMAL_PARAMETER_ENCODING_T */
+@@ -342,7 +342,7 @@ enum mmal_parameter_imagefx {
+ MMAL_PARAM_IMAGEFX_CARTOON,
+ };
+
+-enum MMAL_PARAM_FLICKERAVOID_T {
++enum MMAL_PARAM_FLICKERAVOID {
+ MMAL_PARAM_FLICKERAVOID_OFF,
+ MMAL_PARAM_FLICKERAVOID_AUTO,
+ MMAL_PARAM_FLICKERAVOID_50HZ,
+@@ -754,15 +754,15 @@ struct mmal_parameter_imagefx_parameters
+ #define MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES 2
+ #define MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN 16
+
+-struct mmal_parameter_camera_info_camera_t {
+- u32 port_id;
+- u32 max_width;
+- u32 max_height;
+- u32 lens_present;
+- u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN];
++struct mmal_parameter_camera_info_camera {
++ u32 port_id;
++ u32 max_width;
++ u32 max_height;
++ u32 lens_present;
++ u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN];
+ };
+
+-enum mmal_parameter_camera_info_flash_type_t {
++enum mmal_parameter_camera_info_flash_type {
+ /* Make values explicit to ensure they match values in config ini */
+ MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_XENON = 0,
+ MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_LED = 1,
+@@ -770,16 +770,16 @@ enum mmal_parameter_camera_info_flash_ty
+ MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_MAX = 0x7FFFFFFF
+ };
+
+-struct mmal_parameter_camera_info_flash_t {
+- enum mmal_parameter_camera_info_flash_type_t flash_type;
++struct mmal_parameter_camera_info_flash {
++ enum mmal_parameter_camera_info_flash_type flash_type;
+ };
+
+-struct mmal_parameter_camera_info_t {
+- u32 num_cameras;
+- u32 num_flashes;
+- struct mmal_parameter_camera_info_camera_t
+- cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS];
+- struct mmal_parameter_camera_info_flash_t
++struct mmal_parameter_camera_info {
++ u32 num_cameras;
++ u32 num_flashes;
++ struct mmal_parameter_camera_info_camera
++ cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS];
++ struct mmal_parameter_camera_info_flash
+ flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES];
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch
new file mode 100644
index 0000000000..38015cc98a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch
@@ -0,0 +1,2255 @@
+From 05a5bc2bfa028885c844ccc2263029b5db9160b4 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 23 Apr 2020 10:17:37 +0100
+Subject: [PATCH] staging: vc04_services: ISP: Add a more complex ISP
+ processing component
+
+Driver for the BCM2835 ISP hardware block. This driver uses the MMAL
+component to program the ISP hardware through the VC firmware.
+
+The ISP component can produce two video stream outputs, and Bayer
+image statistics. This can't be encompassed in a simple V4L2
+M2M device, so create a new device that registers 4 video nodes.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ MAINTAINERS | 9 +
+ drivers/staging/vc04_services/Kconfig | 1 +
+ drivers/staging/vc04_services/Makefile | 1 +
+ .../staging/vc04_services/bcm2835-isp/Kconfig | 14 +
+ .../vc04_services/bcm2835-isp/Makefile | 8 +
+ .../bcm2835-isp/bcm2835-v4l2-isp.c | 1627 +++++++++++++++++
+ .../bcm2835-isp/bcm2835_isp_ctrls.h | 67 +
+ .../bcm2835-isp/bcm2835_isp_fmts.h | 272 +++
+ .../vc04_services/vchiq-mmal/mmal-encodings.h | 4 +
+ .../vchiq-mmal/mmal-parameters.h | 153 +-
+ 10 files changed, 2155 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Kconfig
+ create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Makefile
+ create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+ create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h
+ create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3212,6 +3212,15 @@ S: Maintained
+ F: drivers/media/platform/bcm2835/
+ F: Documentation/devicetree/bindings/media/bcm2835-unicam.txt
+
++BROADCOM BCM2835 ISP DRIVER
++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++F: drivers/staging/vc04_services/bcm2835-isp
++F: include/uapi/linux/bcm2835-isp.h
++F: Documentation/media/v4l-drivers/bcm2835-isp.rst
++F: Documentation/media/uapi/v4l/pixfmt-meta-bcm2835-isp-stats.rst
++
+ BROADCOM BCM47XX MIPS ARCHITECTURE
+ M: Hauke Mehrtens <hauke@hauke-m.de>
+ M: Rafał Miłecki <zajec5@gmail.com>
+--- a/drivers/staging/vc04_services/Kconfig
++++ b/drivers/staging/vc04_services/Kconfig
+@@ -25,6 +25,7 @@ source "drivers/staging/vc04_services/bc
+ source "drivers/staging/vc04_services/vchiq-mmal/Kconfig"
+ source "drivers/staging/vc04_services/vc-sm-cma/Kconfig"
+ source "drivers/staging/vc04_services/bcm2835-codec/Kconfig"
++source "drivers/staging/vc04_services/bcm2835-isp/Kconfig"
+
+ endif
+
+--- a/drivers/staging/vc04_services/Makefile
++++ b/drivers/staging/vc04_services/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-
+ obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/
+ obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/
+ obj-$(CONFIG_VIDEO_CODEC_BCM2835) += bcm2835-codec/
++obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp/
+
+ ccflags-y += -Idrivers/staging/vc04_services -D__VCCOREVER__=0x04000000
+
+--- /dev/null
++++ b/drivers/staging/vc04_services/bcm2835-isp/Kconfig
+@@ -0,0 +1,14 @@
++config VIDEO_ISP_BCM2835
++ tristate "BCM2835 ISP support"
++ depends on MEDIA_SUPPORT
++ depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST)
++ depends on MEDIA_CONTROLLER
++ select BCM2835_VCHIQ_MMAL
++ select VIDEOBUF2_DMA_CONTIG
++ help
++ This is the V4L2 driver for the Broadcom BCM2835 ISP hardware.
++ This operates over the VCHIQ interface to a service running on
++ VideoCore.
++
++ To compile this driver as a module, choose M here: the module
++ will be called bcm2835-isp.
+--- /dev/null
++++ b/drivers/staging/vc04_services/bcm2835-isp/Makefile
+@@ -0,0 +1,8 @@
++# SPDX-License-Identifier: GPL-2.0
++bcm2835-isp-objs := bcm2835-v4l2-isp.o
++
++obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp.o
++
++ccflags-y += \
++ -I$(srctree)/drivers/staging/vc04_services \
++ -D__VCCOREVER__=0x04000000
+--- /dev/null
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -0,0 +1,1627 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Broadcom BCM2835 ISP driver
++ *
++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
++ *
++ * Author: Naushir Patuck (naush@raspberrypi.com)
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-ioctl.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "vchiq-mmal/mmal-msg.h"
++#include "vchiq-mmal/mmal-parameters.h"
++#include "vchiq-mmal/mmal-vchiq.h"
++
++#include "bcm2835_isp_ctrls.h"
++#include "bcm2835_isp_fmts.h"
++
++static unsigned int debug;
++module_param(debug, uint, 0644);
++MODULE_PARM_DESC(debug, "activates debug info");
++
++static unsigned int video_nr = 13;
++module_param(video_nr, uint, 0644);
++MODULE_PARM_DESC(video_nr, "base video device number");
++
++#define BCM2835_ISP_NAME "bcm2835-isp"
++#define BCM2835_ISP_ENTITY_NAME_LEN 32
++
++#define BCM2835_ISP_NUM_OUTPUTS 1
++#define BCM2835_ISP_NUM_CAPTURES 2
++#define BCM2835_ISP_NUM_METADATA 1
++
++#define BCM2835_ISP_NUM_NODES \
++ (BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES + \
++ BCM2835_ISP_NUM_METADATA)
++
++/* Default frame dimension of 1280 pixels. */
++#define DEFAULT_DIM 1280U
++/*
++ * Maximum frame dimension of 16384 pixels. Even though the ISP runs in tiles,
++ * have a sensible limit so that we do not create an excessive number of tiles
++ * to process.
++ */
++#define MAX_DIM 16384U
++/*
++ * Minimum frame dimension of 64 pixels. Anything lower, and the tiling
++ * algorihtm may not be able to cope when applying filter context.
++ */
++#define MIN_DIM 64U
++
++/* Per-queue, driver-specific private data */
++struct bcm2835_isp_q_data {
++ /*
++ * These parameters should be treated as gospel, with everything else
++ * being determined from them.
++ */
++ unsigned int bytesperline;
++ unsigned int width;
++ unsigned int height;
++ unsigned int sizeimage;
++ struct bcm2835_isp_fmt *fmt;
++};
++
++/*
++ * Structure to describe a single node /dev/video<N> which represents a single
++ * input or output queue to the ISP device.
++ */
++struct bcm2835_isp_node {
++ int vfl_dir;
++ unsigned int id;
++ const char *name;
++ struct video_device vfd;
++ struct media_pad pad;
++ struct media_intf_devnode *intf_devnode;
++ struct media_link *intf_link;
++ struct mutex lock; /* top level device node lock */
++ struct mutex queue_lock;
++
++ struct vb2_queue queue;
++ unsigned int sequence;
++
++ /* The list of formats supported on the node. */
++ struct bcm2835_isp_fmt_list supported_fmts;
++
++ struct bcm2835_isp_q_data q_data;
++
++ /* Parent device structure */
++ struct bcm2835_isp_dev *dev;
++
++ bool registered;
++ bool media_node_registered;
++ bool queue_init;
++};
++
++/*
++ * Structure representing the entire ISP device, comprising several input and
++ * output nodes /dev/video<N>.
++ */
++struct bcm2835_isp_dev {
++ struct v4l2_device v4l2_dev;
++ struct device *dev;
++ struct v4l2_ctrl_handler ctrl_handler;
++ struct media_device mdev;
++ struct media_entity entity;
++ bool media_device_registered;
++ bool media_entity_registered;
++ struct vchiq_mmal_instance *mmal_instance;
++ struct vchiq_mmal_component *component;
++ struct completion frame_cmplt;
++
++ struct bcm2835_isp_node node[BCM2835_ISP_NUM_NODES];
++ struct media_pad pad[BCM2835_ISP_NUM_NODES];
++ atomic_t num_streaming;
++
++ /* Image pipeline controls. */
++ int r_gain;
++ int b_gain;
++};
++
++struct bcm2835_isp_buffer {
++ struct vb2_v4l2_buffer vb;
++ struct mmal_buffer mmal;
++};
++
++static
++inline struct bcm2835_isp_dev *node_get_dev(struct bcm2835_isp_node *node)
++{
++ return node->dev;
++}
++
++static inline bool node_is_output(struct bcm2835_isp_node *node)
++{
++ return node->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT;
++}
++
++static inline bool node_is_capture(struct bcm2835_isp_node *node)
++{
++ return node->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE;
++}
++
++static inline bool node_is_stats(struct bcm2835_isp_node *node)
++{
++ return node->queue.type == V4L2_BUF_TYPE_META_CAPTURE;
++}
++
++static inline enum v4l2_buf_type index_to_queue_type(int index)
++{
++ if (index < BCM2835_ISP_NUM_OUTPUTS)
++ return V4L2_BUF_TYPE_VIDEO_OUTPUT;
++ else if (index < BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES)
++ return V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ else
++ return V4L2_BUF_TYPE_META_CAPTURE;
++}
++
++static struct vchiq_mmal_port *get_port_data(struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++
++ if (!dev->component)
++ return NULL;
++
++ switch (node->queue.type) {
++ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
++ return &dev->component->input[node->id];
++ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
++ case V4L2_BUF_TYPE_META_CAPTURE:
++ return &dev->component->output[node->id];
++ default:
++ v4l2_err(&dev->v4l2_dev, "%s: Invalid queue type %u\n",
++ __func__, node->queue.type);
++ break;
++ }
++ return NULL;
++}
++
++static int set_isp_param(struct bcm2835_isp_node *node, u32 parameter,
++ void *value, u32 value_size)
++{
++ struct vchiq_mmal_port *port = get_port_data(node);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++
++ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port,
++ parameter, value, value_size);
++}
++
++static int set_wb_gains(struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct mmal_parameter_awbgains gains = {
++ .r_gain = { dev->r_gain, 1000 },
++ .b_gain = { dev->b_gain, 1000 }
++ };
++
++ return set_isp_param(node, MMAL_PARAMETER_CUSTOM_AWB_GAINS,
++ &gains, sizeof(gains));
++}
++
++static int set_digital_gain(struct bcm2835_isp_node *node, uint32_t gain)
++{
++ struct mmal_parameter_rational digital_gain = {
++ .num = gain,
++ .den = 1000
++ };
++
++ return set_isp_param(node, MMAL_PARAMETER_DIGITAL_GAIN,
++ &digital_gain, sizeof(digital_gain));
++}
++
++static const struct bcm2835_isp_fmt *get_fmt(u32 mmal_fmt)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
++ if (supported_formats[i].mmal_fmt == mmal_fmt)
++ return &supported_formats[i];
++ }
++ return NULL;
++}
++
++static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
++ struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts;
++ struct bcm2835_isp_fmt *fmt;
++ unsigned int i;
++
++ for (i = 0; i < fmts->num_entries; i++) {
++ fmt = &fmts->list[i];
++ if (fmt->fourcc == (node_is_stats(node) ?
++ f->fmt.meta.dataformat :
++ f->fmt.pix.pixelformat))
++ return fmt;
++ }
++
++ return NULL;
++}
++
++/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL
++ *
++ * Copies all the required fields from a VB2 buffer to the MMAL buffer header,
++ * ready for sending to the VPU.
++ */
++static void vb2_to_mmal_buffer(struct mmal_buffer *buf,
++ struct vb2_v4l2_buffer *vb2)
++{
++ u64 pts;
++
++ buf->mmal_flags = 0;
++ if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME)
++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME;
++
++ /* Data must be framed correctly as one frame per buffer. */
++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
++
++ buf->length = vb2->vb2_buf.planes[0].bytesused;
++ /*
++ * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length
++ * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream.
++ * Handle either.
++ */
++ if (!buf->length || vb2->flags & V4L2_BUF_FLAG_LAST)
++ buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
++
++ /* vb2 timestamps in nsecs, mmal in usecs */
++ pts = vb2->vb2_buf.timestamp;
++ do_div(pts, 1000);
++ buf->pts = pts;
++ buf->dts = MMAL_TIME_UNKNOWN;
++}
++
++static void mmal_buffer_cb(struct vchiq_mmal_instance *instance,
++ struct vchiq_mmal_port *port, int status,
++ struct mmal_buffer *mmal_buf)
++{
++ struct bcm2835_isp_buffer *q_buf;
++ struct bcm2835_isp_node *node = port->cb_ctx;
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vb2_v4l2_buffer *vb2;
++
++ q_buf = container_of(mmal_buf, struct bcm2835_isp_buffer, mmal);
++ vb2 = &q_buf->vb;
++ v4l2_dbg(2, debug, &dev->v4l2_dev,
++ "%s: port:%s[%d], status:%d, buf:%p, dmabuf:%p, length:%lu, flags %u, pts %lld\n",
++ __func__, node_is_output(node) ? "input" : "output", node->id,
++ status, mmal_buf, mmal_buf->dma_buf, mmal_buf->length,
++ mmal_buf->mmal_flags, mmal_buf->pts);
++
++ if (mmal_buf->cmd)
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Unexpected event on output callback - %08x\n",
++ __func__, mmal_buf->cmd);
++
++ if (status) {
++ /* error in transfer */
++ if (vb2) {
++ /* there was a buffer with the error so return it */
++ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR);
++ }
++ return;
++ }
++
++ /* vb2 timestamps in nsecs, mmal in usecs */
++ vb2->vb2_buf.timestamp = mmal_buf->pts * 1000;
++ vb2->sequence = node->sequence++;
++ vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length);
++ vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_DONE);
++
++ if (!port->enabled)
++ complete(&dev->frame_cmplt);
++}
++
++static void setup_mmal_port_format(struct bcm2835_isp_node *node,
++ struct vchiq_mmal_port *port)
++{
++ struct bcm2835_isp_q_data *q_data = &node->q_data;
++
++ port->format.encoding = q_data->fmt->mmal_fmt;
++ /* Raw image format - set width/height */
++ port->es.video.width = (q_data->bytesperline << 3) / q_data->fmt->depth;
++ port->es.video.height = q_data->height;
++ port->es.video.crop.width = q_data->width;
++ port->es.video.crop.height = q_data->height;
++ port->es.video.crop.x = 0;
++ port->es.video.crop.y = 0;
++};
++
++static int setup_mmal_port(struct bcm2835_isp_node *node)
++{
++ struct vchiq_mmal_port *port = get_port_data(node);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ unsigned int enable = 1;
++ int ret;
++
++ v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: setup %s[%d]\n", __func__,
++ node->name, node->id);
++
++ vchiq_mmal_port_parameter_set(dev->mmal_instance, port,
++ MMAL_PARAMETER_ZERO_COPY, &enable,
++ sizeof(enable));
++ setup_mmal_port_format(node, port);
++ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port);
++ if (ret < 0) {
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: vchiq_mmal_port_set_format failed\n",
++ __func__);
++ return ret;
++ }
++
++ if (node->q_data.sizeimage < port->minimum_buffer.size) {
++ v4l2_err(&dev->v4l2_dev,
++ "buffer size mismatch sizeimage %u < min size %u\n",
++ node->q_data.sizeimage, port->minimum_buffer.size);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int bcm2835_isp_mmal_buf_cleanup(struct mmal_buffer *mmal_buf)
++{
++ mmal_vchi_buffer_cleanup(mmal_buf);
++
++ if (mmal_buf->dma_buf) {
++ dma_buf_put(mmal_buf->dma_buf);
++ mmal_buf->dma_buf = NULL;
++ }
++
++ return 0;
++}
++
++static int bcm2835_isp_node_queue_setup(struct vb2_queue *q,
++ unsigned int *nbuffers,
++ unsigned int *nplanes,
++ unsigned int sizes[],
++ struct device *alloc_devs[])
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
++ struct vchiq_mmal_port *port;
++ unsigned int size;
++
++ if (setup_mmal_port(node))
++ return -EINVAL;
++
++ size = node->q_data.sizeimage;
++ if (size == 0) {
++ v4l2_info(&node_get_dev(node)->v4l2_dev,
++ "%s: Image size unset in queue_setup for node %s[%d]\n",
++ __func__, node->name, node->id);
++ return -EINVAL;
++ }
++
++ if (*nplanes)
++ return sizes[0] < size ? -EINVAL : 0;
++
++ *nplanes = 1;
++ sizes[0] = size;
++
++ port = get_port_data(node);
++ port->current_buffer.size = size;
++
++ if (*nbuffers < port->minimum_buffer.num)
++ *nbuffers = port->minimum_buffer.num;
++
++ port->current_buffer.num = *nbuffers;
++
++ v4l2_dbg(2, debug, &node_get_dev(node)->v4l2_dev,
++ "%s: Image size %u, nbuffers %u for node %s[%d]\n",
++ __func__, sizes[0], *nbuffers, node->name, node->id);
++ return 0;
++}
++
++static int bcm2835_isp_buf_init(struct vb2_buffer *vb)
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
++ struct bcm2835_isp_buffer *buf =
++ container_of(vb2, struct bcm2835_isp_buffer, vb);
++
++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: vb %p\n", __func__, vb);
++
++ buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
++ buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0);
++ mmal_vchi_buffer_init(dev->mmal_instance, &buf->mmal);
++ return 0;
++}
++
++static int bcm2835_isp_buf_prepare(struct vb2_buffer *vb)
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
++ struct bcm2835_isp_buffer *buf =
++ container_of(vb2, struct bcm2835_isp_buffer, vb);
++ struct dma_buf *dma_buf;
++ int ret;
++
++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: type: %d ptr %p\n",
++ __func__, vb->vb2_queue->type, vb);
++
++ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
++ if (vb2->field == V4L2_FIELD_ANY)
++ vb2->field = V4L2_FIELD_NONE;
++ if (vb2->field != V4L2_FIELD_NONE) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s field isn't supported\n", __func__);
++ return -EINVAL;
++ }
++ }
++
++ if (vb2_plane_size(vb, 0) < node->q_data.sizeimage) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s data will not fit into plane (%lu < %lu)\n",
++ __func__, vb2_plane_size(vb, 0),
++ (long)node->q_data.sizeimage);
++ return -EINVAL;
++ }
++
++ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
++ vb2_set_plane_payload(vb, 0, node->q_data.sizeimage);
++
++ switch (vb->memory) {
++ case VB2_MEMORY_DMABUF:
++ dma_buf = dma_buf_get(vb->planes[0].m.fd);
++
++ if (dma_buf != buf->mmal.dma_buf) {
++ /*
++ * dmabuf either hasn't already been mapped, or it has
++ * changed.
++ */
++ if (buf->mmal.dma_buf) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s Buffer changed - why did the core not call cleanup?\n",
++ __func__);
++ bcm2835_isp_mmal_buf_cleanup(&buf->mmal);
++ }
++
++ buf->mmal.dma_buf = dma_buf;
++ } else {
++ /*
++ * Already have a reference to the buffer, so release it
++ * here.
++ */
++ dma_buf_put(dma_buf);
++ }
++ ret = 0;
++ break;
++ case VB2_MEMORY_MMAP:
++ /*
++ * We want to do this at init, but vb2_core_expbuf checks that
++ * the index < q->num_buffers, and q->num_buffers only gets
++ * updated once all the buffers are allocated.
++ */
++ if (!buf->mmal.dma_buf) {
++ ret = vb2_core_expbuf_dmabuf(vb->vb2_queue,
++ vb->vb2_queue->type,
++ vb->index, 0, O_CLOEXEC,
++ &buf->mmal.dma_buf);
++ v4l2_dbg(3, debug, &dev->v4l2_dev,
++ "%s: exporting ptr %p to dmabuf %p\n",
++ __func__, vb, buf->mmal.dma_buf);
++ if (ret)
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Failed to expbuf idx %d, ret %d\n",
++ __func__, vb->index, ret);
++ } else {
++ ret = 0;
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static void bcm2835_isp_node_buffer_queue(struct vb2_buffer *buf)
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(buf->vb2_queue);
++ struct vb2_v4l2_buffer *vbuf =
++ container_of(buf, struct vb2_v4l2_buffer, vb2_buf);
++ struct bcm2835_isp_buffer *buffer =
++ container_of(vbuf, struct bcm2835_isp_buffer, vb);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++
++ v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: node %s[%d], buffer %p\n",
++ __func__, node->name, node->id, buffer);
++
++ vb2_to_mmal_buffer(&buffer->mmal, &buffer->vb);
++ v4l2_dbg(3, debug, &dev->v4l2_dev,
++ "%s: node %s[%d] - submitting mmal dmabuf %p\n", __func__,
++ node->name, node->id, buffer->mmal.dma_buf);
++ vchiq_mmal_submit_buffer(dev->mmal_instance, get_port_data(node),
++ &buffer->mmal);
++}
++
++static void bcm2835_isp_buffer_cleanup(struct vb2_buffer *vb)
++{
++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb);
++ struct bcm2835_isp_buffer *buffer =
++ container_of(vb2, struct bcm2835_isp_buffer, vb);
++
++ bcm2835_isp_mmal_buf_cleanup(&buffer->mmal);
++}
++
++static int bcm2835_isp_node_start_streaming(struct vb2_queue *q,
++ unsigned int count)
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vchiq_mmal_port *port = get_port_data(node);
++ int ret;
++
++ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d] (count %u)\n",
++ __func__, node->name, node->id, count);
++
++ ret = vchiq_mmal_component_enable(dev->mmal_instance, dev->component);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev, "%s: Failed enabling component, ret %d\n",
++ __func__, ret);
++ return -EIO;
++ }
++
++ node->sequence = 0;
++ port->cb_ctx = node;
++ ret = vchiq_mmal_port_enable(dev->mmal_instance, port,
++ mmal_buffer_cb);
++ if (!ret)
++ atomic_inc(&dev->num_streaming);
++ else
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Failed enabling port, ret %d\n", __func__, ret);
++
++ return ret;
++}
++
++static void bcm2835_isp_node_stop_streaming(struct vb2_queue *q)
++{
++ struct bcm2835_isp_node *node = vb2_get_drv_priv(q);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vchiq_mmal_port *port = get_port_data(node);
++ unsigned int i;
++ int ret;
++
++ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d], mmal port %p\n",
++ __func__, node->name, node->id, port);
++
++ init_completion(&dev->frame_cmplt);
++
++ /* Disable MMAL port - this will flush buffers back */
++ ret = vchiq_mmal_port_disable(dev->mmal_instance, port);
++ if (ret)
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Failed disabling %s port, ret %d\n", __func__,
++ node_is_output(node) ? "i/p" : "o/p",
++ ret);
++
++ while (atomic_read(&port->buffers_with_vpu)) {
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: Waiting for buffers to be returned - %d outstanding\n",
++ __func__, atomic_read(&port->buffers_with_vpu));
++ ret = wait_for_completion_timeout(&dev->frame_cmplt, HZ);
++ if (ret <= 0) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Timeout waiting for buffers to be returned - %d outstanding\n",
++ __func__,
++ atomic_read(&port->buffers_with_vpu));
++ break;
++ }
++ }
++
++ /* Release the VCSM handle here to release the associated dmabuf */
++ for (i = 0; i < q->num_buffers; i++) {
++ struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(q->bufs[i]);
++ struct bcm2835_isp_buffer *buf =
++ container_of(vb2, struct bcm2835_isp_buffer, vb);
++ bcm2835_isp_mmal_buf_cleanup(&buf->mmal);
++ }
++
++ atomic_dec(&dev->num_streaming);
++ /* If all ports disabled, then disable the component */
++ if (atomic_read(&dev->num_streaming) == 0) {
++ ret = vchiq_mmal_component_disable(dev->mmal_instance,
++ dev->component);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Failed disabling component, ret %d\n",
++ __func__, ret);
++ }
++ }
++
++ /*
++ * Simply wait for any vb2 buffers to finish. We could take steps to
++ * make them complete more quickly if we care, or even return them
++ * ourselves.
++ */
++ vb2_wait_for_all_buffers(&node->queue);
++}
++
++static const struct vb2_ops bcm2835_isp_node_queue_ops = {
++ .queue_setup = bcm2835_isp_node_queue_setup,
++ .buf_init = bcm2835_isp_buf_init,
++ .buf_prepare = bcm2835_isp_buf_prepare,
++ .buf_queue = bcm2835_isp_node_buffer_queue,
++ .buf_cleanup = bcm2835_isp_buffer_cleanup,
++ .start_streaming = bcm2835_isp_node_start_streaming,
++ .stop_streaming = bcm2835_isp_node_stop_streaming,
++};
++
++static struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node)
++{
++ return &node->supported_fmts.list[0];
++}
++
++static inline unsigned int get_bytesperline(int width,
++ struct bcm2835_isp_fmt *fmt)
++{
++ return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align);
++}
++
++static inline unsigned int get_sizeimage(int bpl, int width, int height,
++ struct bcm2835_isp_fmt *fmt)
++{
++ return (bpl * height * fmt->size_multiplier_x2) >> 1;
++}
++
++static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct bcm2835_isp_dev *dev =
++ container_of(ctrl->handler, struct bcm2835_isp_dev, ctrl_handler);
++ struct bcm2835_isp_node *node = &dev->node[0];
++ int ret = 0;
++
++ /*
++ * The ISP firmware driver will ensure these settings are applied on
++ * a frame boundary, so we are safe to write them as they come in.
++ *
++ * Note that the bcm2835_isp_* param structures are identical to the
++ * mmal-parameters.h definitions. This avoids the need for unnecessary
++ * field-by-field copying between structures.
++ */
++ switch (ctrl->id) {
++ case V4L2_CID_RED_BALANCE:
++ dev->r_gain = ctrl->val;
++ ret = set_wb_gains(node);
++ break;
++ case V4L2_CID_BLUE_BALANCE:
++ dev->b_gain = ctrl->val;
++ ret = set_wb_gains(node);
++ break;
++ case V4L2_CID_DIGITAL_GAIN:
++ ret = set_digital_gain(node, ctrl->val);
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_CC_MATRIX:
++ ret = set_isp_param(node, MMAL_PARAMETER_CUSTOM_CCM,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_custom_ccm));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING:
++ ret = set_isp_param(node, MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_lens_shading));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL:
++ ret = set_isp_param(node, MMAL_PARAMETER_BLACK_LEVEL,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_black_level));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_GEQ:
++ ret = set_isp_param(node, MMAL_PARAMETER_GEQ,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_geq));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_GAMMA:
++ ret = set_isp_param(node, MMAL_PARAMETER_GAMMA,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_gamma));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_DENOISE:
++ ret = set_isp_param(node, MMAL_PARAMETER_DENOISE,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_denoise));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_SHARPEN:
++ ret = set_isp_param(node, MMAL_PARAMETER_SHARPEN,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_sharpen));
++ break;
++ case V4L2_CID_USER_BCM2835_ISP_DPC:
++ ret = set_isp_param(node, MMAL_PARAMETER_DPC,
++ ctrl->p_new.p_u8,
++ sizeof(struct bcm2835_isp_dpc));
++ break;
++ default:
++ v4l2_info(&dev->v4l2_dev, "Unrecognised control\n");
++ ret = -EINVAL;
++ }
++
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev, "%s: Failed setting ctrl \"%s\" (%08x), err %d\n",
++ __func__, ctrl->name, ctrl->id, ret);
++ ret = -EIO;
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops bcm2835_isp_ctrl_ops = {
++ .s_ctrl = bcm2835_isp_s_ctrl,
++};
++
++static const struct v4l2_file_operations bcm2835_isp_fops = {
++ .owner = THIS_MODULE,
++ .open = v4l2_fh_open,
++ .release = vb2_fop_release,
++ .poll = vb2_fop_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .mmap = vb2_fop_mmap
++};
++
++static int populate_qdata_fmt(struct v4l2_format *f,
++ struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct bcm2835_isp_q_data *q_data = &node->q_data;
++ struct vchiq_mmal_port *port;
++ int ret;
++
++ if (!node_is_stats(node)) {
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: Setting pix format for type %d, wxh: %ux%u, fmt: %08x, size %u\n",
++ __func__, f->type, f->fmt.pix.width, f->fmt.pix.height,
++ f->fmt.pix.pixelformat, f->fmt.pix.sizeimage);
++
++ q_data->fmt = find_format(f, node);
++ q_data->width = f->fmt.pix.width;
++ q_data->height = f->fmt.pix.height;
++ q_data->height = f->fmt.pix.height;
++
++ /* All parameters should have been set correctly by try_fmt */
++ q_data->bytesperline = f->fmt.pix.bytesperline;
++ q_data->sizeimage = f->fmt.pix.sizeimage;
++ } else {
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: Setting meta format for fmt: %08x, size %u\n",
++ __func__, f->fmt.meta.dataformat,
++ f->fmt.meta.buffersize);
++
++ q_data->fmt = find_format(f, node);
++ q_data->width = 0;
++ q_data->height = 0;
++ q_data->bytesperline = 0;
++ q_data->sizeimage = f->fmt.meta.buffersize;
++ }
++
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: Calculated bpl as %u, size %u\n", __func__,
++ q_data->bytesperline, q_data->sizeimage);
++
++ /* If we have a component then setup the port as well */
++ port = get_port_data(node);
++ setup_mmal_port_format(node, port);
++ ret = vchiq_mmal_port_set_format(dev->mmal_instance, port);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n",
++ __func__, ret);
++ ret = -EINVAL;
++ }
++
++ if (q_data->sizeimage < port->minimum_buffer.size) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n",
++ __func__,
++ q_data->sizeimage,
++ port->minimum_buffer.size);
++ }
++
++ v4l2_dbg(1, debug, &dev->v4l2_dev,
++ "%s: Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n",
++ __func__, f->type, q_data->width, q_data->height,
++ q_data->fmt->fourcc, q_data->sizeimage);
++
++ return ret;
++}
++
++static int bcm2835_isp_node_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ strscpy(cap->driver, BCM2835_ISP_NAME, sizeof(cap->driver));
++ strscpy(cap->card, BCM2835_ISP_NAME, sizeof(cap->card));
++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++ BCM2835_ISP_NAME);
++
++ return 0;
++}
++
++static int bcm2835_isp_node_g_fmt(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++
++ if (f->type != node->queue.type)
++ return -EINVAL;
++
++ if (node_is_stats(node)) {
++ f->fmt.meta.dataformat = V4L2_META_FMT_BCM2835_ISP_STATS;
++ f->fmt.meta.buffersize =
++ get_port_data(node)->minimum_buffer.size;
++ } else {
++ struct bcm2835_isp_q_data *q_data = &node->q_data;
++
++ f->fmt.pix.width = q_data->width;
++ f->fmt.pix.height = q_data->height;
++ f->fmt.pix.field = V4L2_FIELD_NONE;
++ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
++ f->fmt.pix.bytesperline = q_data->bytesperline;
++ f->fmt.pix.sizeimage = q_data->sizeimage;
++ f->fmt.pix.colorspace = q_data->fmt->colorspace;
++ }
++
++ return 0;
++}
++
++static int bcm2835_isp_node_enum_fmt(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts;
++
++ if (f->type != node->queue.type)
++ return -EINVAL;
++
++ if (f->index < fmts->num_entries) {
++ /* Format found */
++ f->pixelformat = fmts->list[f->index].fourcc;
++ f->flags = fmts->list[f->index].flags;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int bcm2835_isp_node_try_fmt(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ struct bcm2835_isp_fmt *fmt;
++
++ if (f->type != node->queue.type)
++ return -EINVAL;
++
++ fmt = find_format(f, node);
++ if (!fmt)
++ fmt = get_default_format(node);
++
++ if (!node_is_stats(node)) {
++ f->fmt.pix.width = max(min(f->fmt.pix.width, MAX_DIM),
++ MIN_DIM);
++ f->fmt.pix.height = max(min(f->fmt.pix.height, MAX_DIM),
++ MIN_DIM);
++
++ f->fmt.pix.pixelformat = fmt->fourcc;
++ f->fmt.pix.colorspace = fmt->colorspace;
++ f->fmt.pix.bytesperline = get_bytesperline(f->fmt.pix.width,
++ fmt);
++ f->fmt.pix.field = V4L2_FIELD_NONE;
++ f->fmt.pix.sizeimage =
++ get_sizeimage(f->fmt.pix.bytesperline, f->fmt.pix.width,
++ f->fmt.pix.height, fmt);
++ } else {
++ f->fmt.meta.dataformat = fmt->fourcc;
++ f->fmt.meta.buffersize =
++ get_port_data(node)->minimum_buffer.size;
++ }
++
++ return 0;
++}
++
++static int bcm2835_isp_node_s_fmt(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ int ret;
++
++ if (f->type != node->queue.type)
++ return -EINVAL;
++
++ ret = bcm2835_isp_node_try_fmt(file, priv, f);
++ if (ret)
++ return ret;
++
++ v4l2_dbg(1, debug, &node_get_dev(node)->v4l2_dev,
++ "%s: Set format for node %s[%d]\n",
++ __func__, node->name, node->id);
++
++ return populate_qdata_fmt(f, node);
++}
++
++static int bcm2835_isp_node_s_selection(struct file *file, void *fh,
++ struct v4l2_selection *s)
++{
++ struct mmal_parameter_crop crop;
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vchiq_mmal_port *port = get_port_data(node);
++
++ /* This return value is required fro V4L2 compliance. */
++ if (node_is_stats(node))
++ return -ENOTTY;
++
++ if (!s->r.width || !s->r.height)
++ return -EINVAL;
++
++ /* Adjust the crop window if goes outside the frame dimensions. */
++ s->r.left = min((unsigned int)max(s->r.left, 0),
++ node->q_data.width - MIN_DIM);
++ s->r.top = min((unsigned int)max(s->r.top, 0),
++ node->q_data.height - MIN_DIM);
++ s->r.width = max(min(s->r.width, node->q_data.width - s->r.left),
++ MIN_DIM);
++ s->r.height = max(min(s->r.height, node->q_data.height - s->r.top),
++ MIN_DIM);
++
++ crop.rect.x = s->r.left;
++ crop.rect.y = s->r.top;
++ crop.rect.width = s->r.width;
++ crop.rect.height = s->r.height;
++
++ return vchiq_mmal_port_parameter_set(dev->mmal_instance, port,
++ MMAL_PARAMETER_CROP,
++ &crop, sizeof(crop));
++}
++
++static int bcm2835_isp_node_g_selection(struct file *file, void *fh,
++ struct v4l2_selection *s)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct vchiq_mmal_port *port = get_port_data(node);
++ struct mmal_parameter_crop crop;
++ u32 crop_size = sizeof(crop);
++ int ret;
++
++ /* This return value is required for V4L2 compliance. */
++ if (node_is_stats(node))
++ return -ENOTTY;
++
++ /* We can only return out an input crop. */
++ if (s->target != V4L2_SEL_TGT_CROP)
++ return -EINVAL;
++
++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, port,
++ MMAL_PARAMETER_CROP,
++ &crop, &crop_size);
++ if (!ret)
++ return -EINVAL;
++
++ s->r.left = crop.rect.x;
++ s->r.top = crop.rect.y;
++ s->r.width = crop.rect.width;
++ s->r.height = crop.rect.height;
++
++ return 0;
++}
++
++static int bcm3285_isp_subscribe_event(struct v4l2_fh *fh,
++ const struct v4l2_event_subscription *s)
++{
++ switch (s->type) {
++ /* Cannot change source parameters dynamically at runtime. */
++ case V4L2_EVENT_SOURCE_CHANGE:
++ return -EINVAL;
++ case V4L2_EVENT_CTRL:
++ return v4l2_ctrl_subscribe_event(fh, s);
++ default:
++ return v4l2_event_subscribe(fh, s, 4, NULL);
++ }
++}
++
++static const struct v4l2_ioctl_ops bcm2835_isp_node_ioctl_ops = {
++ .vidioc_querycap = bcm2835_isp_node_querycap,
++ .vidioc_g_fmt_vid_cap = bcm2835_isp_node_g_fmt,
++ .vidioc_g_fmt_vid_out = bcm2835_isp_node_g_fmt,
++ .vidioc_g_fmt_meta_cap = bcm2835_isp_node_g_fmt,
++ .vidioc_s_fmt_vid_cap = bcm2835_isp_node_s_fmt,
++ .vidioc_s_fmt_vid_out = bcm2835_isp_node_s_fmt,
++ .vidioc_s_fmt_meta_cap = bcm2835_isp_node_s_fmt,
++ .vidioc_try_fmt_vid_cap = bcm2835_isp_node_try_fmt,
++ .vidioc_try_fmt_vid_out = bcm2835_isp_node_try_fmt,
++ .vidioc_try_fmt_meta_cap = bcm2835_isp_node_try_fmt,
++ .vidioc_s_selection = bcm2835_isp_node_s_selection,
++ .vidioc_g_selection = bcm2835_isp_node_g_selection,
++
++ .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt,
++ .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt,
++ .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt,
++
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++
++ .vidioc_subscribe_event = bcm3285_isp_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++};
++
++/*
++ * Size of the array to provide to the VPU when asking for the list of supported
++ * formats.
++ *
++ * The ISP component currently advertises 33 input formats, so add a small
++ * overhead on that.
++ */
++#define MAX_SUPPORTED_ENCODINGS 40
++
++/* Populate node->supported_fmts with the formats supported by those ports. */
++static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct bcm2835_isp_fmt *list;
++ unsigned int i, j, num_encodings;
++ u32 fourccs[MAX_SUPPORTED_ENCODINGS];
++ u32 param_size = sizeof(fourccs);
++ int ret;
++
++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance,
++ get_port_data(node),
++ MMAL_PARAMETER_SUPPORTED_ENCODINGS,
++ &fourccs, &param_size);
++
++ if (ret) {
++ if (ret == MMAL_MSG_STATUS_ENOSPC) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: port has more encoding than we provided space for. Some are dropped.\n",
++ __func__);
++ num_encodings = MAX_SUPPORTED_ENCODINGS;
++ } else {
++ v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n",
++ __func__, ret);
++ return -EINVAL;
++ }
++ } else {
++ num_encodings = param_size / sizeof(u32);
++ }
++
++ /*
++ * Assume at this stage that all encodings will be supported in V4L2.
++ * Any that aren't supported will waste a very small amount of memory.
++ */
++ list = devm_kzalloc(dev->dev,
++ sizeof(struct bcm2835_isp_fmt) * num_encodings,
++ GFP_KERNEL);
++ if (!list)
++ return -ENOMEM;
++ node->supported_fmts.list = list;
++
++ for (i = 0, j = 0; i < num_encodings; i++) {
++ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]);
++
++ if (fmt) {
++ list[j] = *fmt;
++ j++;
++ }
++ }
++ node->supported_fmts.num_entries = j;
++
++ param_size = sizeof(fourccs);
++ ret = vchiq_mmal_port_parameter_get(dev->mmal_instance,
++ get_port_data(node),
++ MMAL_PARAMETER_SUPPORTED_ENCODINGS,
++ &fourccs, &param_size);
++
++ if (ret) {
++ if (ret == MMAL_MSG_STATUS_ENOSPC) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: port has more encoding than we provided space for. Some are dropped.\n",
++ __func__);
++ num_encodings = MAX_SUPPORTED_ENCODINGS;
++ } else {
++ return -EINVAL;
++ }
++ } else {
++ num_encodings = param_size / sizeof(u32);
++ }
++ /* Assume at this stage that all encodings will be supported in V4L2. */
++ list = devm_kzalloc(dev->dev,
++ sizeof(struct bcm2835_isp_fmt) * num_encodings,
++ GFP_KERNEL);
++ if (!list)
++ return -ENOMEM;
++ node->supported_fmts.list = list;
++
++ for (i = 0, j = 0; i < num_encodings; i++) {
++ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]);
++
++ if (fmt) {
++ list[j] = *fmt;
++ j++;
++ }
++ }
++ node->supported_fmts.num_entries = j;
++ return 0;
++}
++
++/*
++ * Register a device node /dev/video<N> to go along with one of the ISP's input
++ * or output nodes.
++ */
++static int register_node(struct bcm2835_isp_dev *dev,
++ struct bcm2835_isp_node *node,
++ int index)
++{
++ struct video_device *vfd;
++ struct vb2_queue *queue;
++ int ret;
++
++ mutex_init(&node->lock);
++
++ node->dev = dev;
++ vfd = &node->vfd;
++ queue = &node->queue;
++ queue->type = index_to_queue_type(index);
++ /*
++ * Setup the node type-specific params.
++ *
++ * Only the OUTPUT node can set controls and crop windows. However,
++ * we must allow the s/g_selection ioctl on the stats node as v4l2
++ * compliance expects it to return a -ENOTTY, and the framework
++ * does not handle it if the ioctl is disabled.
++ */
++ switch (queue->type) {
++ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
++ vfd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
++ node->id = index;
++ node->vfl_dir = VFL_DIR_TX;
++ node->name = "output";
++ break;
++ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
++ vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
++ /* First Capture node starts at id 0, etc. */
++ node->id = index - BCM2835_ISP_NUM_OUTPUTS;
++ node->vfl_dir = VFL_DIR_RX;
++ node->name = "capture";
++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL);
++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION);
++ v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION);
++ break;
++ case V4L2_BUF_TYPE_META_CAPTURE:
++ vfd->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
++ node->id = index - BCM2835_ISP_NUM_OUTPUTS;
++ node->vfl_dir = VFL_DIR_RX;
++ node->name = "stats";
++ v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL);
++ break;
++ }
++
++ /* We use the selection API instead of the old crop API. */
++ v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP);
++ v4l2_disable_ioctl(vfd, VIDIOC_G_CROP);
++ v4l2_disable_ioctl(vfd, VIDIOC_S_CROP);
++
++ ret = bcm2835_isp_get_supported_fmts(node);
++ if (ret)
++ return ret;
++
++ /* Initialise the the video node. */
++ vfd->vfl_type = VFL_TYPE_GRABBER;
++ vfd->fops = &bcm2835_isp_fops,
++ vfd->ioctl_ops = &bcm2835_isp_node_ioctl_ops,
++ vfd->minor = -1,
++ vfd->release = video_device_release_empty,
++ vfd->queue = &node->queue;
++ vfd->lock = &node->lock;
++ vfd->v4l2_dev = &dev->v4l2_dev;
++ vfd->vfl_dir = node->vfl_dir;
++
++ node->q_data.fmt = get_default_format(node);
++ node->q_data.width = DEFAULT_DIM;
++ node->q_data.height = DEFAULT_DIM;
++ node->q_data.bytesperline =
++ get_bytesperline(DEFAULT_DIM, node->q_data.fmt);
++ node->q_data.sizeimage = node_is_stats(node) ?
++ get_port_data(node)->recommended_buffer.size :
++ get_sizeimage(node->q_data.bytesperline,
++ node->q_data.width,
++ node->q_data.height,
++ node->q_data.fmt);
++
++ queue->io_modes = VB2_MMAP | VB2_DMABUF;
++ queue->drv_priv = node;
++ queue->ops = &bcm2835_isp_node_queue_ops;
++ queue->mem_ops = &vb2_dma_contig_memops;
++ queue->buf_struct_size = sizeof(struct bcm2835_isp_buffer);
++ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++ queue->dev = dev->dev;
++ queue->lock = &node->queue_lock;
++
++ ret = vb2_queue_init(queue);
++ if (ret < 0) {
++ v4l2_info(&dev->v4l2_dev, "vb2_queue_init failed\n");
++ return ret;
++ }
++ node->queue_init = true;
++
++ /* Define the device names */
++ snprintf(vfd->name, sizeof(node->vfd.name), "%s-%s%d", BCM2835_ISP_NAME,
++ node->name, node->id);
++
++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr + index);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to register video %s[%d] device node\n",
++ node->name, node->id);
++ return ret;
++ }
++
++ node->registered = true;
++ video_set_drvdata(vfd, node);
++
++ /* Set some controls and defaults, but only on the VIDEO_OUTPUT node. */
++ if (node_is_output(node)) {
++ unsigned int i;
++
++ /* Use this ctrl template to assign all out ISP custom ctrls. */
++ struct v4l2_ctrl_config ctrl_template = {
++ .ops = &bcm2835_isp_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U8,
++ .def = 0,
++ .min = 0x00,
++ .max = 0xff,
++ .step = 1,
++ };
++
++ v4l2_ctrl_handler_init(&dev->ctrl_handler, 4);
++
++ dev->r_gain = 1000;
++ dev->b_gain = 1000;
++
++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops,
++ V4L2_CID_RED_BALANCE, 1, 0xffff, 1,
++ dev->r_gain);
++
++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops,
++ V4L2_CID_BLUE_BALANCE, 1, 0xffff, 1,
++ dev->b_gain);
++
++ v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops,
++ V4L2_CID_DIGITAL_GAIN, 1, 0xffff, 1, 1000);
++
++ for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) {
++ ctrl_template.name = custom_ctrls[i].name;
++ ctrl_template.id = custom_ctrls[i].id;
++ ctrl_template.dims[0] = custom_ctrls[i].size;
++ ctrl_template.flags = custom_ctrls[i].flags;
++ v4l2_ctrl_new_custom(&dev->ctrl_handler,
++ &ctrl_template, NULL);
++ }
++
++ node->vfd.ctrl_handler = &dev->ctrl_handler;
++ }
++
++ v4l2_info(&dev->v4l2_dev,
++ "Device node %s[%d] registered as /dev/video%d\n",
++ node->name, node->id, vfd->num);
++
++ return 0;
++}
++
++/* Unregister one of the /dev/video<N> nodes associated with the ISP. */
++static void unregister_node(struct bcm2835_isp_node *node)
++{
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++
++ v4l2_info(&dev->v4l2_dev,
++ "Unregistering node %s[%d] device node /dev/video%d\n",
++ node->name, node->id, node->vfd.num);
++
++ if (node->queue_init)
++ vb2_queue_release(&node->queue);
++
++ if (node->registered) {
++ video_unregister_device(&node->vfd);
++ if (node_is_output(node))
++ v4l2_ctrl_handler_free(&dev->ctrl_handler);
++ }
++
++ /*
++ * node->supported_fmts.list is free'd automatically
++ * as a managed resource.
++ */
++ node->supported_fmts.list = NULL;
++ node->supported_fmts.num_entries = 0;
++ node->vfd.ctrl_handler = NULL;
++ node->registered = false;
++ node->queue_init = false;
++}
++
++static void media_controller_unregister(struct bcm2835_isp_dev *dev)
++{
++ unsigned int i;
++
++ v4l2_info(&dev->v4l2_dev, "Unregister from media controller\n");
++
++ if (dev->media_device_registered) {
++ media_device_unregister(&dev->mdev);
++ media_device_cleanup(&dev->mdev);
++ dev->media_device_registered = false;
++ }
++
++ kfree(dev->entity.name);
++ dev->entity.name = NULL;
++
++ if (dev->media_entity_registered) {
++ media_device_unregister_entity(&dev->entity);
++ dev->media_entity_registered = false;
++ }
++
++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
++ struct bcm2835_isp_node *node = &dev->node[i];
++
++ if (node->media_node_registered) {
++ media_remove_intf_links(node->intf_link->intf);
++ media_entity_remove_links(&dev->node[i].vfd.entity);
++ media_devnode_remove(node->intf_devnode);
++ media_device_unregister_entity(&node->vfd.entity);
++ kfree(node->vfd.entity.name);
++ }
++ node->media_node_registered = false;
++ }
++
++ dev->v4l2_dev.mdev = NULL;
++}
++
++static int media_controller_register_node(struct bcm2835_isp_dev *dev, int num)
++{
++ struct bcm2835_isp_node *node = &dev->node[num];
++ struct media_entity *entity = &node->vfd.entity;
++ int output = node_is_output(node);
++ char *name;
++ int ret;
++
++ v4l2_info(&dev->v4l2_dev,
++ "Register %s node %d with media controller\n",
++ output ? "output" : "capture", num);
++ entity->obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
++ entity->function = MEDIA_ENT_F_IO_V4L;
++ entity->info.dev.major = VIDEO_MAJOR;
++ entity->info.dev.minor = node->vfd.minor;
++ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL);
++ if (!name) {
++ ret = -ENOMEM;
++ goto error_no_mem;
++ }
++ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "%s0-%s%d",
++ BCM2835_ISP_NAME, output ? "output" : "capture", num);
++ entity->name = name;
++ node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
++ ret = media_entity_pads_init(entity, 1, &node->pad);
++ if (ret)
++ goto error_pads_init;
++ ret = media_device_register_entity(&dev->mdev, entity);
++ if (ret)
++ goto error_register_entity;
++
++ node->intf_devnode = media_devnode_create(&dev->mdev,
++ MEDIA_INTF_T_V4L_VIDEO, 0,
++ VIDEO_MAJOR, node->vfd.minor);
++ if (!node->intf_devnode) {
++ ret = -ENOMEM;
++ goto error_devnode_create;
++ }
++
++ node->intf_link = media_create_intf_link(entity,
++ &node->intf_devnode->intf,
++ MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ if (!node->intf_link) {
++ ret = -ENOMEM;
++ goto error_create_intf_link;
++ }
++
++ if (output)
++ ret = media_create_pad_link(entity, 0, &dev->entity, num,
++ MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ else
++ ret = media_create_pad_link(&dev->entity, num, entity, 0,
++ MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ if (ret)
++ goto error_create_pad_link;
++
++ dev->node[num].media_node_registered = true;
++ return 0;
++
++error_create_pad_link:
++ media_remove_intf_links(&node->intf_devnode->intf);
++error_create_intf_link:
++ media_devnode_remove(node->intf_devnode);
++error_devnode_create:
++ media_device_unregister_entity(&node->vfd.entity);
++error_register_entity:
++error_pads_init:
++ kfree(entity->name);
++ entity->name = NULL;
++error_no_mem:
++ if (ret)
++ v4l2_info(&dev->v4l2_dev, "Error registering node\n");
++
++ return ret;
++}
++
++static int media_controller_register(struct bcm2835_isp_dev *dev)
++{
++ char *name;
++ unsigned int i;
++ int ret;
++
++ v4l2_dbg(2, debug, &dev->v4l2_dev, "Registering with media controller\n");
++ dev->mdev.dev = dev->dev;
++ strscpy(dev->mdev.model, "bcm2835-isp",
++ sizeof(dev->mdev.model));
++ strscpy(dev->mdev.bus_info, "platform:bcm2835-isp",
++ sizeof(dev->mdev.bus_info));
++ media_device_init(&dev->mdev);
++ dev->v4l2_dev.mdev = &dev->mdev;
++
++ v4l2_dbg(2, debug, &dev->v4l2_dev, "Register entity for nodes\n");
++
++ name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL);
++ if (!name) {
++ ret = -ENOMEM;
++ goto done;
++ }
++ snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "bcm2835_isp0");
++ dev->entity.name = name;
++ dev->entity.obj_type = MEDIA_ENTITY_TYPE_BASE;
++ dev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
++
++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
++ dev->pad[i].flags = node_is_output(&dev->node[i]) ?
++ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
++ }
++
++ ret = media_entity_pads_init(&dev->entity, BCM2835_ISP_NUM_NODES,
++ dev->pad);
++ if (ret)
++ goto done;
++
++ ret = media_device_register_entity(&dev->mdev, &dev->entity);
++ if (ret)
++ goto done;
++
++ dev->media_entity_registered = true;
++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
++ ret = media_controller_register_node(dev, i);
++ if (ret)
++ goto done;
++ }
++
++ ret = media_device_register(&dev->mdev);
++ if (!ret)
++ dev->media_device_registered = true;
++done:
++ return ret;
++}
++
++static int bcm2835_isp_remove(struct platform_device *pdev)
++{
++ struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev);
++ unsigned int i;
++
++ media_controller_unregister(dev);
++
++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++)
++ unregister_node(&dev->node[i]);
++
++ v4l2_device_unregister(&dev->v4l2_dev);
++
++ if (dev->component)
++ vchiq_mmal_component_finalise(dev->mmal_instance,
++ dev->component);
++
++ vchiq_mmal_finalise(dev->mmal_instance);
++
++ return 0;
++}
++
++static int bcm2835_isp_probe(struct platform_device *pdev)
++{
++ struct bcm2835_isp_dev *dev;
++ unsigned int i;
++ int ret;
++
++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++
++ dev->dev = &pdev->dev;
++
++ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
++ if (ret)
++ return ret;
++
++ ret = vchiq_mmal_init(&dev->mmal_instance);
++ if (ret) {
++ v4l2_device_unregister(&dev->v4l2_dev);
++ return ret;
++ }
++
++ ret = vchiq_mmal_component_init(dev->mmal_instance, "ril.isp",
++ &dev->component);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: failed to create ril.isp component\n", __func__);
++ goto error;
++ }
++
++ if ((dev->component->inputs != BCM2835_ISP_NUM_OUTPUTS) ||
++ (dev->component->outputs != BCM2835_ISP_NUM_CAPTURES +
++ BCM2835_ISP_NUM_METADATA)) {
++ v4l2_err(&dev->v4l2_dev,
++ "%s: ril.isp returned %d i/p (%d expected), %d o/p (%d expected) ports\n",
++ __func__, dev->component->inputs,
++ BCM2835_ISP_NUM_OUTPUTS,
++ dev->component->outputs,
++ BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA);
++ goto error;
++ }
++
++ atomic_set(&dev->num_streaming, 0);
++
++ for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) {
++ struct bcm2835_isp_node *node = &dev->node[i];
++
++ ret = register_node(dev, node, i);
++ if (ret)
++ goto error;
++ }
++
++ ret = media_controller_register(dev);
++ if (ret)
++ goto error;
++
++ platform_set_drvdata(pdev, dev);
++ v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME);
++ return 0;
++
++error:
++ bcm2835_isp_remove(pdev);
++
++ return ret;
++}
++
++static struct platform_driver bcm2835_isp_pdrv = {
++ .probe = bcm2835_isp_probe,
++ .remove = bcm2835_isp_remove,
++ .driver = {
++ .name = BCM2835_ISP_NAME,
++ },
++};
++
++module_platform_driver(bcm2835_isp_pdrv);
++
++MODULE_DESCRIPTION("BCM2835 ISP driver");
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
++MODULE_LICENSE("GPL");
++MODULE_VERSION("1.0");
++MODULE_ALIAS("platform:bcm2835-isp");
+--- /dev/null
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h
+@@ -0,0 +1,67 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Broadcom BCM2835 ISP driver
++ *
++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
++ *
++ * Author: Naushir Patuck (naush@raspberrypi.com)
++ *
++ */
++
++#ifndef BCM2835_ISP_CTRLS
++#define BCM2835_ISP_CTRLS
++
++#include <linux/bcm2835-isp.h>
++
++struct bcm2835_isp_custom_ctrl {
++ const char *name;
++ u32 id;
++ u32 size;
++ u32 flags;
++};
++
++static const struct bcm2835_isp_custom_ctrl custom_ctrls[] = {
++ {
++ .name = "Colour Correction Matrix",
++ .id = V4L2_CID_USER_BCM2835_ISP_CC_MATRIX,
++ .size = sizeof(struct bcm2835_isp_custom_ccm),
++ .flags = 0
++ }, {
++ .name = "Lens Shading",
++ .id = V4L2_CID_USER_BCM2835_ISP_LENS_SHADING,
++ .size = sizeof(struct bcm2835_isp_lens_shading),
++ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE
++ }, {
++ .name = "Black Level",
++ .id = V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL,
++ .size = sizeof(struct bcm2835_isp_black_level),
++ .flags = 0
++ }, {
++ .name = "Green Equalisation",
++ .id = V4L2_CID_USER_BCM2835_ISP_GEQ,
++ .size = sizeof(struct bcm2835_isp_geq),
++ .flags = 0
++ }, {
++ .name = "Gamma",
++ .id = V4L2_CID_USER_BCM2835_ISP_GAMMA,
++ .size = sizeof(struct bcm2835_isp_gamma),
++ .flags = 0
++ }, {
++ .name = "Sharpen",
++ .id = V4L2_CID_USER_BCM2835_ISP_SHARPEN,
++ .size = sizeof(struct bcm2835_isp_sharpen),
++ .flags = 0
++ }, {
++ .name = "Denoise",
++ .id = V4L2_CID_USER_BCM2835_ISP_DENOISE,
++ .size = sizeof(struct bcm2835_isp_denoise),
++ .flags = 0
++ }, {
++ .name = "Defective Pixel Correction",
++ .id = V4L2_CID_USER_BCM2835_ISP_DPC,
++ .size = sizeof(struct bcm2835_isp_dpc),
++ .flags = 0
++ }
++};
++
++#endif
+--- /dev/null
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
+@@ -0,0 +1,272 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Broadcom BCM2835 ISP driver
++ *
++ * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd.
++ *
++ * Author: Naushir Patuck (naush@raspberrypi.com)
++ *
++ */
++
++#ifndef BCM2835_ISP_FMTS
++#define BCM2835_ISP_FMTS
++
++#include <linux/videodev2.h>
++#include "vchiq-mmal/mmal-encodings.h"
++
++struct bcm2835_isp_fmt {
++ u32 fourcc;
++ int depth;
++ int bytesperline_align;
++ u32 flags;
++ u32 mmal_fmt;
++ int size_multiplier_x2;
++ enum v4l2_colorspace colorspace;
++};
++
++struct bcm2835_isp_fmt_list {
++ struct bcm2835_isp_fmt *list;
++ unsigned int num_entries;
++};
++
++static const struct bcm2835_isp_fmt supported_formats[] = {
++ {
++ /* YUV formats */
++ .fourcc = V4L2_PIX_FMT_YUV420,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_I420,
++ .size_multiplier_x2 = 3,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YVU420,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_YV12,
++ .size_multiplier_x2 = 3,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_NV12,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_NV12,
++ .size_multiplier_x2 = 3,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_NV21,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_NV21,
++ .size_multiplier_x2 = 3,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_YUYV,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_UYVY,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_YVYU,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_YVYU,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ .fourcc = V4L2_PIX_FMT_VYUY,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_VYUY,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ }, {
++ /* RGB formats */
++ .fourcc = V4L2_PIX_FMT_RGB24,
++ .depth = 24,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_RGB24,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ }, {
++ .fourcc = V4L2_PIX_FMT_RGB565,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_RGB16,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ }, {
++ .fourcc = V4L2_PIX_FMT_BGR24,
++ .depth = 24,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BGR24,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ }, {
++ .fourcc = V4L2_PIX_FMT_ABGR32,
++ .depth = 32,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BGRA,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ }, {
++ /* Bayer formats */
++ /* 8 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB8,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR8,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG8,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG8,
++ .depth = 8,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ /* 10 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB10P,
++ .depth = 10,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR10P,
++ .depth = 10,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG10P,
++ .depth = 10,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG10P,
++ .depth = 10,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ /* 12 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB12P,
++ .depth = 12,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR12P,
++ .depth = 12,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG12P,
++ .depth = 12,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG12P,
++ .depth = 12,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ /* 16 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB16,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SBGGR16,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGRBG16,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ .fourcc = V4L2_PIX_FMT_SGBRG16,
++ .depth = 16,
++ .bytesperline_align = 32,
++ .flags = 0,
++ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16,
++ .size_multiplier_x2 = 2,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ }, {
++ /* ISP statistics format */
++ .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS,
++ .mmal_fmt = MMAL_ENCODING_BRCM_STATS,
++ /* The rest are not valid fields for stats. */
++ }
++};
++
++#endif
+--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h
++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h
+@@ -100,6 +100,10 @@
+ */
+ #define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I')
+
++/** ISP image statistics format
++ */
++#define MMAL_ENCODING_BRCM_STATS MMAL_FOURCC('S', 'T', 'A', 'T')
++
+ /* }@ */
+
+ /** \name Pre-defined audio encodings */
+--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
+@@ -221,6 +221,62 @@ enum mmal_parameter_camera_type {
+ MMAL_PARAMETER_SHUTTER_SPEED,
+ /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */
+ MMAL_PARAMETER_CUSTOM_AWB_GAINS,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_SETTINGS_T */
++ MMAL_PARAMETER_CAMERA_SETTINGS,
++ /**< Takes a @ref MMAL_PARAMETER_PRIVACY_INDICATOR_T */
++ MMAL_PARAMETER_PRIVACY_INDICATOR,
++ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
++ MMAL_PARAMETER_VIDEO_DENOISE,
++ /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */
++ MMAL_PARAMETER_STILLS_DENOISE,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_ANNOTATE_T */
++ MMAL_PARAMETER_ANNOTATE,
++ /**< Takes a @ref MMAL_PARAMETER_STEREOSCOPIC_MODE_T */
++ MMAL_PARAMETER_STEREOSCOPIC_MODE,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_INTERFACE_T */
++ MMAL_PARAMETER_CAMERA_INTERFACE,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T */
++ MMAL_PARAMETER_CAMERA_CLOCKING_MODE,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_CONFIG_T */
++ MMAL_PARAMETER_CAMERA_RX_CONFIG,
++ /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_TIMING_T */
++ MMAL_PARAMETER_CAMERA_RX_TIMING,
++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */
++ MMAL_PARAMETER_DPF_CONFIG,
++
++ /* 0x50 */
++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */
++ MMAL_PARAMETER_JPEG_RESTART_INTERVAL,
++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */
++ MMAL_PARAMETER_CAMERA_ISP_BLOCK_OVERRIDE,
++ /**< Takes a @ref MMAL_PARAMETER_LENS_SHADING_T */
++ MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
++ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */
++ MMAL_PARAMETER_BLACK_LEVEL,
++ /**< Takes a @ref MMAL_PARAMETER_RESIZE_T */
++ MMAL_PARAMETER_RESIZE_PARAMS,
++ /**< Takes a @ref MMAL_PARAMETER_CROP_T */
++ MMAL_PARAMETER_CROP,
++ /**< Takes a @ref MMAL_PARAMETER_INT32_T */
++ MMAL_PARAMETER_OUTPUT_SHIFT,
++ /**< Takes a @ref MMAL_PARAMETER_INT32_T */
++ MMAL_PARAMETER_CCM_SHIFT,
++ /**< Takes a @ref MMAL_PARAMETER_CUSTOM_CCM_T */
++ MMAL_PARAMETER_CUSTOM_CCM,
++ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */
++ MMAL_PARAMETER_ANALOG_GAIN,
++ /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */
++ MMAL_PARAMETER_DIGITAL_GAIN,
++ /**< Takes a @ref MMAL_PARAMETER_DENOISE_T */
++ MMAL_PARAMETER_DENOISE,
++ /**< Takes a @ref MMAL_PARAMETER_SHARPEN_T */
++ MMAL_PARAMETER_SHARPEN,
++ /**< Takes a @ref MMAL_PARAMETER_GEQ_T */
++ MMAL_PARAMETER_GEQ,
++ /**< Tales a @ref MMAP_PARAMETER_DPC_T */
++ MMAL_PARAMETER_DPC,
++ /**< Tales a @ref MMAP_PARAMETER_GAMMA_T */
++ MMAL_PARAMETER_GAMMA,
+ };
+
+ struct mmal_parameter_rational {
+@@ -780,7 +836,102 @@ struct mmal_parameter_camera_info {
+ struct mmal_parameter_camera_info_camera
+ cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS];
+ struct mmal_parameter_camera_info_flash
+- flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES];
++ flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES];
++};
++
++struct mmal_parameter_ccm {
++ struct mmal_parameter_rational ccm[3][3];
++ s32 offsets[3];
++};
++
++struct mmal_parameter_custom_ccm {
++ u32 enabled; /**< Enable the custom CCM. */
++ struct mmal_parameter_ccm ccm; /**< CCM to be used. */
++};
++
++struct mmal_parameter_lens_shading {
++ u32 enabled;
++ u32 grid_cell_size;
++ u32 grid_width;
++ u32 grid_stride;
++ u32 grid_height;
++ u32 mem_handle_table;
++ u32 ref_transform;
++};
++
++enum mmal_parameter_ls_gain_format_type {
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U0P8_1 = 0,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_0 = 1,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_1 = 2,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_0 = 3,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_1 = 4,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_0 = 5,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_1 = 6,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U4P10 = 7,
++ MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_DUMMY = 0x7FFFFFFF
++};
++
++struct mmal_parameter_lens_shading_v2 {
++ u32 enabled;
++ u32 grid_cell_size;
++ u32 grid_width;
++ u32 grid_stride;
++ u32 grid_height;
++ u32 mem_handle_table;
++ u32 ref_transform;
++ u32 corner_sampled;
++ enum mmal_parameter_ls_gain_format_type gain_format;
++};
++
++struct mmal_parameter_black_level {
++ u32 enabled;
++ u16 black_level_r;
++ u16 black_level_g;
++ u16 black_level_b;
++ u8 pad_[2]; /* Unused */
++};
++
++struct mmal_parameter_geq {
++ u32 enabled;
++ u32 offset;
++ struct mmal_parameter_rational slope;
++};
++
++#define MMAL_NUM_GAMMA_PTS 33
++struct mmal_parameter_gamma {
++ u32 enabled;
++ u16 x[MMAL_NUM_GAMMA_PTS];
++ u16 y[MMAL_NUM_GAMMA_PTS];
++};
++
++struct mmal_parameter_denoise {
++ u32 enabled;
++ u32 constant;
++ struct mmal_parameter_rational slope;
++ struct mmal_parameter_rational strength;
++};
++
++struct mmal_parameter_sharpen {
++ u32 enabled;
++ struct mmal_parameter_rational threshold;
++ struct mmal_parameter_rational strength;
++ struct mmal_parameter_rational limit;
++};
++
++enum mmal_dpc_mode {
++ MMAL_DPC_MODE_OFF = 0,
++ MMAL_DPC_MODE_NORMAL = 1,
++ MMAL_DPC_MODE_STRONG = 2,
++ MMAL_DPC_MODE_MAX = 0x7FFFFFFF,
++};
++
++struct mmal_parameter_dpc {
++ u32 enabled;
++ u32 strength;
++};
++
++struct mmal_parameter_crop {
++ struct vchiq_mmal_rect rect;
+ };
+
+ #endif
diff --git a/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch b/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch
new file mode 100644
index 0000000000..70fe392a3b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch
@@ -0,0 +1,39 @@
+From 7f2f9b54862f7df5cdef95b85234fad83b6b3480 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 22 Apr 2020 08:32:32 +0100
+Subject: [PATCH] staging: vchiq: Load bcm2835_isp driver from vchiq
+
+bcmn2835_isp is a platform driver dependent on vchiq,
+therefore add the load/unload functions for it to vchiq.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+@@ -142,6 +142,7 @@ static struct platform_device *bcm2835_c
+ static struct platform_device *bcm2835_audio;
+ static struct platform_device *bcm2835_codec;
+ static struct platform_device *vcsm_cma;
++static struct platform_device *bcm2835_isp;
+
+ static struct vchiq_drvdata bcm2835_drvdata = {
+ .cache_line_size = 32,
+@@ -3281,6 +3282,7 @@ static int vchiq_probe(struct platform_d
+ bcm2835_codec = vchiq_register_child(pdev, "bcm2835-codec");
+ bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera");
+ bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio");
++ bcm2835_isp = vchiq_register_child(pdev, "bcm2835-isp");
+
+ return 0;
+
+@@ -3293,6 +3295,7 @@ failed_platform_init:
+
+ static int vchiq_remove(struct platform_device *pdev)
+ {
++ platform_device_unregister(bcm2835_isp);
+ platform_device_unregister(bcm2835_audio);
+ platform_device_unregister(bcm2835_camera);
+ platform_device_unregister(bcm2835_codec);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch b/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch
new file mode 100644
index 0000000000..816ca3c589
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch
@@ -0,0 +1,23 @@
+From 41b2f1242ff3f90c88de2de93dbec1f5734b45fd Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Tue, 28 Apr 2020 17:35:07 +0100
+Subject: [PATCH] vc4_hvs: Mark core clock as optional
+
+This isn't required on Pi3, so don't treat as an error
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -240,7 +240,7 @@ static int vc4_hvs_bind(struct device *d
+ hvs->regset.regs = hvs_regs;
+ hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+
+- hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++ hvs->core_clk = devm_clk_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(hvs->core_clk)) {
+ dev_err(&pdev->dev, "Couldn't get core clock\n");
+ return PTR_ERR(hvs->regs);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch b/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch
new file mode 100644
index 0000000000..0ea80ae6c8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch
@@ -0,0 +1,93 @@
+From af3f381a59c10f6bd49d86a5ff2325b6ebeb79e9 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 27 Apr 2020 19:07:50 +0100
+Subject: [PATCH] vc4_hdmi: BCM2835 requires a fixed hsm clock for CEC
+ to work
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 32 ++++++++++++++++++++++++++------
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ 2 files changed, 29 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -580,12 +580,7 @@ static void vc4_hdmi_encoder_enable(stru
+ return;
+ }
+
+- /*
+- * The HSM rate needs to be slightly greater than the pixel clock, with
+- * a minimum of 108MHz.
+- * Use 101% as this is what the firmware uses.
+- */
+- hsm_rate = max_t(unsigned long, 108000000, (pixel_rate / 100) * 101);
++ hsm_rate = vc4_hdmi->variant->calc_hsm_clock(vc4_hdmi, pixel_rate);
+ ret = clk_set_rate(vc4_hdmi->hsm_clock, hsm_rate);
+ if (ret) {
+ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+@@ -753,6 +748,28 @@ static u32 vc5_hdmi_get_hsm_clock(struct
+ return 108000000;
+ }
+
++static u32 vc4_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate)
++{
++ /*
++ * This is the rate that is set by the firmware. The number
++ * needs to be a bit higher than the pixel clock rate
++ * (generally 148.5Mhz).
++ */
++
++ return 163682864;
++}
++
++static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate)
++{
++ /*
++ * The HSM rate needs to be slightly greater than the pixel clock, with
++ * a minimum of 108MHz.
++ * Use 101% as this is what the firmware uses.
++ */
++
++ return max_t(unsigned long, 108000000, (pixel_rate / 100) * 101);
++}
++
+ static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
+ {
+ int i;
+@@ -1748,6 +1765,7 @@ static const struct vc4_hdmi_variant bcm
+ .phy_rng_enable = vc4_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc4_hdmi_phy_rng_disable,
+ .get_hsm_clock = vc4_hdmi_get_hsm_clock,
++ .calc_hsm_clock = vc4_hdmi_calc_hsm_clock,
+ .channel_map = vc4_hdmi_channel_map,
+ };
+
+@@ -1772,6 +1790,7 @@ static const struct vc4_hdmi_variant bcm
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
+ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
++ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock,
+ .channel_map = vc5_hdmi_channel_map,
+ };
+
+@@ -1796,6 +1815,7 @@ static const struct vc4_hdmi_variant bcm
+ .phy_rng_enable = vc5_hdmi_phy_rng_enable,
+ .phy_rng_disable = vc5_hdmi_phy_rng_disable,
+ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
++ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock,
+ .channel_map = vc5_hdmi_channel_map,
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -92,6 +92,9 @@ struct vc4_hdmi_variant {
+ /* Callback to get hsm clock */
+ u32 (*get_hsm_clock)(struct vc4_hdmi *vc4_hdmi);
+
++ /* Callback to get hsm clock */
++ u32 (*calc_hsm_clock)(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate);
++
+ /* Callback to get channel map */
+ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch b/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch
new file mode 100644
index 0000000000..cb7a0af605
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch
@@ -0,0 +1,181 @@
+From f479cf37ccda2be7204a964fe2747dfcb4b56bf6 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo@jmondi.org>
+Date: Wed, 29 Apr 2020 11:50:38 +0200
+Subject: [PATCH] media: i2c: imx219: Implement get_selection
+
+Implement the get_selection pad operation for the IMX219 sensor driver.
+The supported targets report the sensor's native size, the crop default
+rectangle and the crop rectangle.
+
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
+---
+ drivers/media/i2c/imx219.c | 94 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 94 insertions(+)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -122,6 +122,14 @@ enum pad_types {
+ NUM_PADS
+ };
+
++/* IMX219 native and active pixel array size. */
++#define IMX219_NATIVE_WIDTH 3296U
++#define IMX219_NATIVE_HEIGHT 2480U
++#define IMX219_PIXEL_ARRAY_LEFT 8U
++#define IMX219_PIXEL_ARRAY_TOP 8U
++#define IMX219_PIXEL_ARRAY_WIDTH 3280U
++#define IMX219_PIXEL_ARRAY_HEIGHT 2464U
++
+ struct imx219_reg {
+ u16 address;
+ u8 val;
+@@ -139,6 +147,9 @@ struct imx219_mode {
+ /* Frame height */
+ unsigned int height;
+
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
+ /* V-timing */
+ unsigned int vts_def;
+
+@@ -473,6 +484,12 @@ static const struct imx219_mode supporte
+ /* 8MPix 15fps mode */
+ .width = 3280,
+ .height = 2464,
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 3280,
++ .height = 2464
++ },
+ .vts_def = IMX219_VTS_15FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
+@@ -483,6 +500,12 @@ static const struct imx219_mode supporte
+ /* 1080P 30fps cropped */
+ .width = 1920,
+ .height = 1080,
++ .crop = {
++ .left = 680,
++ .top = 692,
++ .width = 1920,
++ .height = 1080
++ },
+ .vts_def = IMX219_VTS_30FPS_1080P,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
+@@ -493,6 +516,12 @@ static const struct imx219_mode supporte
+ /* 2x2 binned 30fps mode */
+ .width = 1640,
+ .height = 1232,
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 3280,
++ .height = 2464
++ },
+ .vts_def = IMX219_VTS_30FPS_BINNED,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
+@@ -503,6 +532,12 @@ static const struct imx219_mode supporte
+ /* 640x480 30fps mode */
+ .width = 640,
+ .height = 480,
++ .crop = {
++ .left = 1000,
++ .top = 752,
++ .width = 1280,
++ .height = 960
++ },
+ .vts_def = IMX219_VTS_30FPS_640x480,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
+@@ -666,6 +701,7 @@ static int imx219_open(struct v4l2_subde
+ v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD);
+ struct v4l2_mbus_framefmt *try_fmt_meta =
+ v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD);
++ struct v4l2_rect *try_crop;
+
+ mutex_lock(&imx219->mutex);
+
+@@ -682,6 +718,13 @@ static int imx219_open(struct v4l2_subde
+ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA;
+ try_fmt_meta->field = V4L2_FIELD_NONE;
+
++ /* Initialize try_crop rectangle. */
++ try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0);
++ try_crop->top = IMX219_PIXEL_ARRAY_TOP;
++ try_crop->left = IMX219_PIXEL_ARRAY_LEFT;
++ try_crop->width = IMX219_PIXEL_ARRAY_WIDTH;
++ try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT;
++
+ mutex_unlock(&imx219->mutex);
+
+ return 0;
+@@ -1011,6 +1054,56 @@ static int imx219_set_framefmt(struct im
+ return -EINVAL;
+ }
+
++static const struct v4l2_rect *
++__imx219_get_pad_crop(struct imx219 *imx219, struct v4l2_subdev_pad_config *cfg,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&imx219->sd, cfg, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &imx219->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int imx219_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct imx219 *imx219 = to_imx219(sd);
++
++ mutex_lock(&imx219->mutex);
++ sel->r = *__imx219_get_pad_crop(imx219, cfg, sel->pad,
++ sel->which);
++ mutex_unlock(&imx219->mutex);
++
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.top = 0;
++ sel->r.left = 0;
++ sel->r.width = IMX219_NATIVE_WIDTH;
++ sel->r.height = IMX219_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ sel->r.top = IMX219_PIXEL_ARRAY_TOP;
++ sel->r.left = IMX219_PIXEL_ARRAY_LEFT;
++ sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
++ sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
+ static int imx219_start_streaming(struct imx219 *imx219)
+ {
+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+@@ -1235,6 +1328,7 @@ static const struct v4l2_subdev_pad_ops
+ .enum_mbus_code = imx219_enum_mbus_code,
+ .get_fmt = imx219_get_pad_format,
+ .set_fmt = imx219_set_pad_format,
++ .get_selection = imx219_get_selection,
+ .enum_frame_size = imx219_enum_frame_size,
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch b/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch
new file mode 100644
index 0000000000..66715a108e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch
@@ -0,0 +1,206 @@
+From 940cac315aaeca33483bffcf09a235195e3f5272 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 11:46:07 +0100
+Subject: [PATCH] media: i2c: ov5647: Add support for g_selection to
+ reflect cropping/binning
+
+In order to apply lens shading correctly the client needs to know how
+each mode crops or scales the image compared to the full sensor array.
+Implement this (based on the imx219 equivalent).
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 119 ++++++++++++++++++++++++++++++-------
+ 1 file changed, 96 insertions(+), 23 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -70,25 +70,14 @@
+ #define VAL_TERM 0xfe
+ #define REG_DLY 0xffff
+
+-#define OV5647_ROW_START 0x01
+-#define OV5647_ROW_START_MIN 0
+-#define OV5647_ROW_START_MAX 2004
+-#define OV5647_ROW_START_DEF 54
+-
+-#define OV5647_COLUMN_START 0x02
+-#define OV5647_COLUMN_START_MIN 0
+-#define OV5647_COLUMN_START_MAX 2750
+-#define OV5647_COLUMN_START_DEF 16
+-
+-#define OV5647_WINDOW_HEIGHT 0x03
+-#define OV5647_WINDOW_HEIGHT_MIN 2
+-#define OV5647_WINDOW_HEIGHT_MAX 2006
+-#define OV5647_WINDOW_HEIGHT_DEF 1944
+-
+-#define OV5647_WINDOW_WIDTH 0x04
+-#define OV5647_WINDOW_WIDTH_MIN 2
+-#define OV5647_WINDOW_WIDTH_MAX 2752
+-#define OV5647_WINDOW_WIDTH_DEF 2592
++/* OV5647 native and active pixel array size */
++#define OV5647_NATIVE_WIDTH 2624U
++#define OV5647_NATIVE_HEIGHT 1956U
++
++#define OV5647_PIXEL_ARRAY_LEFT 16U
++#define OV5647_PIXEL_ARRAY_TOP 16U
++#define OV5647_PIXEL_ARRAY_WIDTH 2592U
++#define OV5647_PIXEL_ARRAY_HEIGHT 1944U
+
+ struct regval_list {
+ u16 addr;
+@@ -97,6 +86,9 @@ struct regval_list {
+
+ struct ov5647_mode {
+ struct v4l2_mbus_framefmt format;
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
+ struct regval_list *reg_list;
+ unsigned int num_regs;
+ };
+@@ -603,6 +595,12 @@ static struct ov5647_mode supported_mode
+ .width = 640,
+ .height = 480
+ },
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 1280,
++ .height = 960,
++ },
+ ov5647_640x480_8bit,
+ ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+@@ -620,6 +618,12 @@ static struct ov5647_mode supported_mode
+ .width = 2592,
+ .height = 1944
+ },
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 2592,
++ .height = 1944
++ },
+ ov5647_2592x1944_10bit,
+ ARRAY_SIZE(ov5647_2592x1944_10bit)
+ },
+@@ -635,6 +639,12 @@ static struct ov5647_mode supported_mode
+ .width = 1920,
+ .height = 1080
+ },
++ .crop = {
++ .left = 348,
++ .top = 434,
++ .width = 1928,
++ .height = 1080,
++ },
+ ov5647_1080p30_10bit,
+ ARRAY_SIZE(ov5647_1080p30_10bit)
+ },
+@@ -649,6 +659,12 @@ static struct ov5647_mode supported_mode
+ .width = 1296,
+ .height = 972
+ },
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 2592,
++ .height = 1944,
++ },
+ ov5647_2x2binned_10bit,
+ ARRAY_SIZE(ov5647_2x2binned_10bit)
+ },
+@@ -664,6 +680,12 @@ static struct ov5647_mode supported_mode
+ .width = 640,
+ .height = 480
+ },
++ .crop = {
++ .left = 16,
++ .top = 0,
++ .width = 2560,
++ .height = 1920,
++ },
+ ov5647_640x480_10bit,
+ ARRAY_SIZE(ov5647_640x480_10bit)
+ },
+@@ -971,6 +993,56 @@ static const struct v4l2_subdev_core_ops
+ #endif
+ };
+
++static const struct v4l2_rect *
++__ov5647_get_pad_crop(struct ov5647 *ov5647, struct v4l2_subdev_pad_config *cfg,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&ov5647->sd, cfg, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &ov5647->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int ov5647_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct ov5647 *state = to_state(sd);
++
++ mutex_lock(&state->lock);
++ sel->r = *__ov5647_get_pad_crop(state, cfg, sel->pad,
++ sel->which);
++ mutex_unlock(&state->lock);
++
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.top = 0;
++ sel->r.left = 0;
++ sel->r.width = OV5647_NATIVE_WIDTH;
++ sel->r.height = OV5647_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ sel->r.top = OV5647_PIXEL_ARRAY_TOP;
++ sel->r.left = OV5647_PIXEL_ARRAY_LEFT;
++ sel->r.width = OV5647_PIXEL_ARRAY_WIDTH;
++ sel->r.height = OV5647_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
+ static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
+ {
+ struct ov5647 *state = to_state(sd);
+@@ -1122,6 +1194,7 @@ static const struct v4l2_subdev_pad_ops
+ .enum_mbus_code = ov5647_enum_mbus_code,
+ .set_fmt = ov5647_set_fmt,
+ .get_fmt = ov5647_get_fmt,
++ .get_selection = ov5647_get_selection,
+ .enum_frame_size = ov5647_enum_frame_size,
+ };
+
+@@ -1170,10 +1243,10 @@ static int ov5647_open(struct v4l2_subde
+ v4l2_subdev_get_try_crop(sd, fh->pad, 0);
+ struct ov5647 *state = to_state(sd);
+
+- crop->left = OV5647_COLUMN_START_DEF;
+- crop->top = OV5647_ROW_START_DEF;
+- crop->width = OV5647_WINDOW_WIDTH_DEF;
+- crop->height = OV5647_WINDOW_HEIGHT_DEF;
++ crop->left = OV5647_PIXEL_ARRAY_LEFT;
++ crop->top = OV5647_PIXEL_ARRAY_TOP;
++ crop->width = OV5647_PIXEL_ARRAY_WIDTH;
++ crop->height = OV5647_PIXEL_ARRAY_HEIGHT;
+
+ /* Set the default format to the same as the sensor. */
+ *format = state->mode->format;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch b/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch
new file mode 100644
index 0000000000..d8a0104739
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch
@@ -0,0 +1,29 @@
+From 0c447a38b9d561a80d78ffd7f4533fef75cd1393 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 11:50:52 +0100
+Subject: [PATCH] media: i2c: ov5467: Fixup error path to release mutex
+
+"87f3ab9 media: ov5647: Add basic support for multiple sensor modes."
+added a return path ov5647_set_fmt that didn't release the device
+mutex that it had claimed.
+Release the mutex.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -1146,8 +1146,10 @@ static int ov5647_set_fmt(struct v4l2_su
+ else
+ mode = mode_8bit;
+
+- if (!mode)
++ if (!mode) {
++ mutex_unlock(&state->lock);
+ return -EINVAL;
++ }
+
+ *fmt = mode->format;
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch b/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch
new file mode 100644
index 0000000000..ce16832d35
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch
@@ -0,0 +1,131 @@
+From e947a531a5c6a61fc568dc6a502543e1145efc29 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 12:25:13 +0100
+Subject: [PATCH] media: i2c: ov5647: Support V4L2_CID_PIXEL_RATE
+
+Clients need to know the pixel rate in order to compute exposure
+and frame rate values.
+Advertise it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 31 +++++++++++++++++++++++++++----
+ 1 file changed, 27 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -89,6 +89,8 @@ struct ov5647_mode {
+ /* Analog crop rectangle. */
+ struct v4l2_rect crop;
+
++ u64 pixel_rate;
++
+ struct regval_list *reg_list;
+ unsigned int num_regs;
+ };
+@@ -103,6 +105,7 @@ struct ov5647 {
+ struct gpio_desc *pwdn;
+ unsigned int flags;
+ struct v4l2_ctrl_handler ctrls;
++ struct v4l2_ctrl *pixel_rate;
+ bool write_mode_regs;
+ };
+
+@@ -601,6 +604,7 @@ static struct ov5647_mode supported_mode
+ .width = 1280,
+ .height = 960,
+ },
++ .pixel_rate = 77291670,
+ ov5647_640x480_8bit,
+ ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+@@ -624,6 +628,7 @@ static struct ov5647_mode supported_mode
+ .width = 2592,
+ .height = 1944
+ },
++ .pixel_rate = 87500000,
+ ov5647_2592x1944_10bit,
+ ARRAY_SIZE(ov5647_2592x1944_10bit)
+ },
+@@ -645,6 +650,7 @@ static struct ov5647_mode supported_mode
+ .width = 1928,
+ .height = 1080,
+ },
++ .pixel_rate = 81666700,
+ ov5647_1080p30_10bit,
+ ARRAY_SIZE(ov5647_1080p30_10bit)
+ },
+@@ -665,6 +671,7 @@ static struct ov5647_mode supported_mode
+ .width = 2592,
+ .height = 1944,
+ },
++ .pixel_rate = 81666700,
+ ov5647_2x2binned_10bit,
+ ARRAY_SIZE(ov5647_2x2binned_10bit)
+ },
+@@ -686,6 +693,7 @@ static struct ov5647_mode supported_mode
+ .width = 2560,
+ .height = 1920,
+ },
++ .pixel_rate = 55000000,
+ ov5647_640x480_10bit,
+ ARRAY_SIZE(ov5647_640x480_10bit)
+ },
+@@ -1163,6 +1171,11 @@ static int ov5647_set_fmt(struct v4l2_su
+ if (state->mode != mode)
+ state->write_mode_regs = true;
+ state->mode = mode;
++
++ __v4l2_ctrl_modify_range(state->pixel_rate,
++ mode->pixel_rate,
++ mode->pixel_rate, 1,
++ mode->pixel_rate);
+ }
+
+ mutex_unlock(&state->lock);
+@@ -1379,6 +1392,9 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+ case V4L2_CID_EXPOSURE:
+ ret = ov5647_s_exposure(sd, ctrl->val);
+ break;
++ case V4L2_CID_PIXEL_RATE:
++ /* Read-only, but we adjust it based on mode. */
++ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+@@ -1436,7 +1452,7 @@ static int ov5647_probe(struct i2c_clien
+ mutex_init(&sensor->lock);
+
+ /* Initialise controls. */
+- v4l2_ctrl_handler_init(&sensor->ctrls, 3);
++ v4l2_ctrl_handler_init(&sensor->ctrls, 6);
+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_AUTOGAIN,
+ 0, /* min */
+@@ -1469,6 +1485,16 @@ static int ov5647_probe(struct i2c_clien
+ 32); /* default, 32 = 2.0x */
+ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
++ /* Set the default mode before we init the subdev */
++ sensor->mode = OV5647_DEFAULT_MODE;
++
++ /* By default, PIXEL_RATE is read only, but it does change per mode */
++ sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_PIXEL_RATE,
++ sensor->mode->pixel_rate,
++ sensor->mode->pixel_rate, 1,
++ sensor->mode->pixel_rate);
++
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "%s control init failed (%d)\n",
+@@ -1477,9 +1503,6 @@ static int ov5647_probe(struct i2c_clien
+ }
+ sensor->sd.ctrl_handler = &sensor->ctrls;
+
+- /* Set the default mode before we init the subdev */
+- sensor->mode = OV5647_DEFAULT_MODE;
+-
+ /* Write out the register set over I2C on stream-on. */
+ sensor->write_mode_regs = true;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch b/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch
new file mode 100644
index 0000000000..b7a3f19753
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch
@@ -0,0 +1,143 @@
+From 0e864ac98ffc97d0bb5fc343ca62d860fbe8da09 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 17:25:56 +0100
+Subject: [PATCH] media: i2c: ov5647: Set V4L2_SUBDEV_FL_HAS_EVENTS
+ flag
+
+The ov5647 subdev can generate control events, therefore set
+the V4L2_SUBDEV_FL_HAS_EVENTS flag.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 29 +++++++++++++++++++++++++++--
+ 1 file changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -90,6 +90,8 @@ struct ov5647_mode {
+ struct v4l2_rect crop;
+
+ u64 pixel_rate;
++ /* HTS as defined in the register set (0x380C/0x380D) */
++ int hts;
+
+ struct regval_list *reg_list;
+ unsigned int num_regs;
+@@ -106,6 +108,7 @@ struct ov5647 {
+ unsigned int flags;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *hblank;
+ bool write_mode_regs;
+ };
+
+@@ -605,6 +608,7 @@ static struct ov5647_mode supported_mode
+ .height = 960,
+ },
+ .pixel_rate = 77291670,
++ .hts = 1896,
+ ov5647_640x480_8bit,
+ ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+@@ -629,6 +633,7 @@ static struct ov5647_mode supported_mode
+ .height = 1944
+ },
+ .pixel_rate = 87500000,
++ .hts = 2844,
+ ov5647_2592x1944_10bit,
+ ARRAY_SIZE(ov5647_2592x1944_10bit)
+ },
+@@ -651,6 +656,7 @@ static struct ov5647_mode supported_mode
+ .height = 1080,
+ },
+ .pixel_rate = 81666700,
++ .hts = 2416,
+ ov5647_1080p30_10bit,
+ ARRAY_SIZE(ov5647_1080p30_10bit)
+ },
+@@ -672,6 +678,7 @@ static struct ov5647_mode supported_mode
+ .height = 1944,
+ },
+ .pixel_rate = 81666700,
++ .hts = 1896,
+ ov5647_2x2binned_10bit,
+ ARRAY_SIZE(ov5647_2x2binned_10bit)
+ },
+@@ -694,6 +701,7 @@ static struct ov5647_mode supported_mode
+ .height = 1920,
+ },
+ .pixel_rate = 55000000,
++ .hts = 1852,
+ ov5647_640x480_10bit,
+ ARRAY_SIZE(ov5647_640x480_10bit)
+ },
+@@ -1168,6 +1176,8 @@ static int ov5647_set_fmt(struct v4l2_su
+ * If we have changed modes, write the I2C register list on
+ * a stream_on().
+ */
++ int hblank;
++
+ if (state->mode != mode)
+ state->write_mode_regs = true;
+ state->mode = mode;
+@@ -1176,6 +1186,9 @@ static int ov5647_set_fmt(struct v4l2_su
+ mode->pixel_rate,
+ mode->pixel_rate, 1,
+ mode->pixel_rate);
++ hblank = mode->hts - mode->format.width;
++ __v4l2_ctrl_modify_range(state->hblank, hblank, hblank, 1,
++ hblank);
+ }
+
+ mutex_unlock(&state->lock);
+@@ -1395,6 +1408,9 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+ case V4L2_CID_PIXEL_RATE:
+ /* Read-only, but we adjust it based on mode. */
+ break;
++ case V4L2_CID_HBLANK:
++ /* Read-only, but we adjust it based on mode. */
++ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+@@ -1419,6 +1435,7 @@ static int ov5647_probe(struct i2c_clien
+ struct device_node *np = client->dev.of_node;
+ u32 xclk_freq;
+ struct v4l2_ctrl *ctrl;
++ int hblank;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+@@ -1452,7 +1469,7 @@ static int ov5647_probe(struct i2c_clien
+ mutex_init(&sensor->lock);
+
+ /* Initialise controls. */
+- v4l2_ctrl_handler_init(&sensor->ctrls, 6);
++ v4l2_ctrl_handler_init(&sensor->ctrls, 7);
+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_AUTOGAIN,
+ 0, /* min */
+@@ -1495,6 +1512,13 @@ static int ov5647_probe(struct i2c_clien
+ sensor->mode->pixel_rate, 1,
+ sensor->mode->pixel_rate);
+
++ /* By default, HBLANK is read only, but it does change per mode */
++ hblank = sensor->mode->hts - sensor->mode->format.width;
++ sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_HBLANK, hblank, hblank, 1,
++ hblank);
++ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "%s control init failed (%d)\n",
+@@ -1509,7 +1533,8 @@ static int ov5647_probe(struct i2c_clien
+ sd = &sensor->sd;
+ v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+ sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
+- sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
+
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch b/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch
new file mode 100644
index 0000000000..85d63cf1a3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch
@@ -0,0 +1,205 @@
+From fbb943e35b519549eac8ee17bf20d651388a27dd Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 21:39:58 +0100
+Subject: [PATCH] media: i2c: ov5647: Add support for V4L2_CID_VBLANK
+
+Adds vblank control to allow for frame rate control.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 65 ++++++++++++++++++++++++++++++++------
+ 1 file changed, 55 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -61,6 +61,8 @@
+ #define OV5647_REG_AEC_AGC 0x3503
+ #define OV5647_REG_GAIN_HI 0x350A
+ #define OV5647_REG_GAIN_LO 0x350B
++#define OV5647_REG_VTS_HI 0x380e
++#define OV5647_REG_VTS_LO 0x380f
+ #define OV5647_REG_FRAME_OFF_NUMBER 0x4202
+ #define OV5647_REG_MIPI_CTRL00 0x4800
+ #define OV5647_REG_MIPI_CTRL14 0x4814
+@@ -79,6 +81,9 @@
+ #define OV5647_PIXEL_ARRAY_WIDTH 2592U
+ #define OV5647_PIXEL_ARRAY_HEIGHT 1944U
+
++#define OV5647_VBLANK_MIN 4
++#define OV5647_VTS_MAX 32767
++
+ struct regval_list {
+ u16 addr;
+ u8 data;
+@@ -92,6 +97,8 @@ struct ov5647_mode {
+ u64 pixel_rate;
+ /* HTS as defined in the register set (0x380C/0x380D) */
+ int hts;
++ /* Default VTS value for this mode */
++ int vts_def;
+
+ struct regval_list *reg_list;
+ unsigned int num_regs;
+@@ -109,6 +116,7 @@ struct ov5647 {
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
++ struct v4l2_ctrl *vblank;
+ bool write_mode_regs;
+ };
+
+@@ -161,8 +169,6 @@ static struct regval_list ov5647_640x480
+ {0x3b07, 0x0c},
+ {0x380c, 0x07},
+ {0x380d, 0x68},
+- {0x380e, 0x03},
+- {0x380f, 0xd8},
+ {0x3814, 0x31},
+ {0x3815, 0x31},
+ {0x3708, 0x64},
+@@ -251,8 +257,6 @@ static struct regval_list ov5647_2592x19
+ {0x3b07, 0x0c},
+ {0x380c, 0x0b},
+ {0x380d, 0x1c},
+- {0x380e, 0x07},
+- {0x380f, 0xb0},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+@@ -342,8 +346,6 @@ static struct regval_list ov5647_1080p30
+ {0x3b07, 0x0c},
+ {0x380c, 0x09},
+ {0x380d, 0x70},
+- {0x380e, 0x04},
+- {0x380f, 0x50},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+@@ -485,8 +487,6 @@ static struct regval_list ov5647_2x2binn
+ {0x3503, 0x03},
+ {0x3820, 0x41},
+ {0x3821, 0x07},
+- {0x380E, 0x05},
+- {0x380F, 0x9B},
+ {0x350A, 0x00},
+ {0x350B, 0x10},
+ {0x3500, 0x00},
+@@ -520,8 +520,6 @@ static struct regval_list ov5647_640x480
+ {0x3b07, 0x0c},
+ {0x380c, 0x07},
+ {0x380d, 0x3c},
+- {0x380e, 0x01},
+- {0x380f, 0xf8},
+ {0x3814, 0x35},
+ {0x3815, 0x35},
+ {0x3708, 0x64},
+@@ -609,6 +607,7 @@ static struct ov5647_mode supported_mode
+ },
+ .pixel_rate = 77291670,
+ .hts = 1896,
++ .vts_def = 0x3d8,
+ ov5647_640x480_8bit,
+ ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+@@ -634,6 +633,7 @@ static struct ov5647_mode supported_mode
+ },
+ .pixel_rate = 87500000,
+ .hts = 2844,
++ .vts_def = 0x7b0,
+ ov5647_2592x1944_10bit,
+ ARRAY_SIZE(ov5647_2592x1944_10bit)
+ },
+@@ -657,6 +657,7 @@ static struct ov5647_mode supported_mode
+ },
+ .pixel_rate = 81666700,
+ .hts = 2416,
++ .vts_def = 0x450,
+ ov5647_1080p30_10bit,
+ ARRAY_SIZE(ov5647_1080p30_10bit)
+ },
+@@ -679,6 +680,7 @@ static struct ov5647_mode supported_mode
+ },
+ .pixel_rate = 81666700,
+ .hts = 1896,
++ .vts_def = 0x59b,
+ ov5647_2x2binned_10bit,
+ ARRAY_SIZE(ov5647_2x2binned_10bit)
+ },
+@@ -702,6 +704,7 @@ static struct ov5647_mode supported_mode
+ },
+ .pixel_rate = 55000000,
+ .hts = 1852,
++ .vts_def = 0x1f8,
+ ov5647_640x480_10bit,
+ ARRAY_SIZE(ov5647_640x480_10bit)
+ },
+@@ -710,6 +713,29 @@ static struct ov5647_mode supported_mode
+ /* Use 2x2 binned 10-bit mode as default. */
+ #define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
+
++static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
++{
++ int ret;
++ unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ ret = i2c_master_send(client, data, 4);
++ /*
++ * Writing the wrong number of bytes also needs to be flagged as an
++ * error. Success needs to produce a 0 return code.
++ */
++ if (ret == 4) {
++ ret = 0;
++ } else {
++ dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
++ __func__, reg);
++ if (ret >= 0)
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
+ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+ {
+ int ret;
+@@ -1189,6 +1215,14 @@ static int ov5647_set_fmt(struct v4l2_su
+ hblank = mode->hts - mode->format.width;
+ __v4l2_ctrl_modify_range(state->hblank, hblank, hblank, 1,
+ hblank);
++
++ __v4l2_ctrl_modify_range(state->vblank,
++ OV5647_VBLANK_MIN,
++ OV5647_VTS_MAX - mode->format.height,
++ 1,
++ mode->vts_def - mode->format.height);
++ __v4l2_ctrl_s_ctrl(state->vblank,
++ mode->vts_def - mode->format.height);
+ }
+
+ mutex_unlock(&state->lock);
+@@ -1411,6 +1445,10 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+ case V4L2_CID_HBLANK:
+ /* Read-only, but we adjust it based on mode. */
+ break;
++ case V4L2_CID_VBLANK:
++ ret = ov5647_write16(sd, OV5647_REG_VTS_HI,
++ state->mode->format.height + ctrl->val);
++ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+@@ -1519,6 +1557,13 @@ static int ov5647_probe(struct i2c_clien
+ hblank);
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
++ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_VBLANK, OV5647_VBLANK_MIN,
++ OV5647_VTS_MAX -
++ sensor->mode->format.height, 1,
++ sensor->mode->vts_def -
++ sensor->mode->format.height);
++
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "%s control init failed (%d)\n",
diff --git a/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch b/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch
new file mode 100644
index 0000000000..039809e94e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch
@@ -0,0 +1,58 @@
+From ea0b801a818e837e657c53687f03d62805e2e586 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 21:47:25 +0100
+Subject: [PATCH] media: i2c: ov5647: Neither analogue gain nor
+ exposure need EXECUTE_ON_WRITE
+
+The controls for analogue gain and exposure were defined with
+V4L2_CTRL_FLAG_EXECUTE_ON_WRITE. This is not required as we only need
+to send changes to the sensor.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 27 ++++++++++++---------------
+ 1 file changed, 12 insertions(+), 15 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -1472,7 +1472,6 @@ static int ov5647_probe(struct i2c_clien
+ struct v4l2_subdev *sd;
+ struct device_node *np = client->dev.of_node;
+ u32 xclk_freq;
+- struct v4l2_ctrl *ctrl;
+ int hblank;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+@@ -1525,20 +1524,18 @@ static int ov5647_probe(struct i2c_clien
+ V4L2_EXPOSURE_MANUAL, /* max */
+ 0, /* skip_mask */
+ V4L2_EXPOSURE_MANUAL); /* default */
+- ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+- V4L2_CID_EXPOSURE,
+- 4, /* min lines */
+- 65535, /* max lines (4+8+4 bits)*/
+- 1, /* step */
+- 1000); /* default number of lines */
+- ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+- ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+- V4L2_CID_ANALOGUE_GAIN,
+- 16, /* min, 16 = 1.0x */
+- 1023, /* max (10 bits) */
+- 1, /* step */
+- 32); /* default, 32 = 2.0x */
+- ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
++ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ 4, /* min lines */
++ 65535, /* max lines (4+8+4 bits)*/
++ 1, /* step */
++ 1000); /* default number of lines */
++ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_ANALOGUE_GAIN,
++ 16, /* min, 16 = 1.0x */
++ 1023, /* max (10 bits) */
++ 1, /* step */
++ 32); /* default, 32 = 2.0x */
+
+ /* Set the default mode before we init the subdev */
+ sensor->mode = OV5647_DEFAULT_MODE;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch b/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch
new file mode 100644
index 0000000000..d8ef700a59
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch
@@ -0,0 +1,111 @@
+From 95b6a6fea10497ca8f583768522d80317f8d700e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 22:11:01 +0100
+Subject: [PATCH] media: i2c: ov5647: Use member names in mode tables
+
+To make adding new members to the mode structures easier, use
+the member names in the initialisers.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -592,7 +592,7 @@ static struct ov5647_mode supported_mode
+ * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
+ */
+ {
+- {
++ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+@@ -608,8 +608,8 @@ static struct ov5647_mode supported_mode
+ .pixel_rate = 77291670,
+ .hts = 1896,
+ .vts_def = 0x3d8,
+- ov5647_640x480_8bit,
+- ARRAY_SIZE(ov5647_640x480_8bit)
++ .reg_list = ov5647_640x480_8bit,
++ .num_regs = ARRAY_SIZE(ov5647_640x480_8bit)
+ },
+ };
+
+@@ -618,7 +618,7 @@ static struct ov5647_mode supported_mode
+ * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
+ */
+ {
+- {
++ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+@@ -634,15 +634,15 @@ static struct ov5647_mode supported_mode
+ .pixel_rate = 87500000,
+ .hts = 2844,
+ .vts_def = 0x7b0,
+- ov5647_2592x1944_10bit,
+- ARRAY_SIZE(ov5647_2592x1944_10bit)
++ .reg_list = ov5647_2592x1944_10bit,
++ .num_regs = ARRAY_SIZE(ov5647_2592x1944_10bit)
+ },
+ /*
+ * MODE 1: 1080p30 10-bit mode.
+ * Full resolution centre-cropped down to 1080p.
+ */
+ {
+- {
++ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+@@ -658,14 +658,14 @@ static struct ov5647_mode supported_mode
+ .pixel_rate = 81666700,
+ .hts = 2416,
+ .vts_def = 0x450,
+- ov5647_1080p30_10bit,
+- ARRAY_SIZE(ov5647_1080p30_10bit)
++ .reg_list = ov5647_1080p30_10bit,
++ .num_regs = ARRAY_SIZE(ov5647_1080p30_10bit)
+ },
+ /*
+ * MODE 2: 2x2 binned full FOV 10-bit mode.
+ */
+ {
+- {
++ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+@@ -681,15 +681,15 @@ static struct ov5647_mode supported_mode
+ .pixel_rate = 81666700,
+ .hts = 1896,
+ .vts_def = 0x59b,
+- ov5647_2x2binned_10bit,
+- ARRAY_SIZE(ov5647_2x2binned_10bit)
++ .reg_list = ov5647_2x2binned_10bit,
++ .num_regs = ARRAY_SIZE(ov5647_2x2binned_10bit)
+ },
+ /*
+ * MODE 3: 10-bit VGA full FOV mode 60fps.
+ * 2x2 binned and subsampled down to VGA.
+ */
+ {
+- {
++ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+@@ -705,8 +705,8 @@ static struct ov5647_mode supported_mode
+ .pixel_rate = 55000000,
+ .hts = 1852,
+ .vts_def = 0x1f8,
+- ov5647_640x480_10bit,
+- ARRAY_SIZE(ov5647_640x480_10bit)
++ .reg_list = ov5647_640x480_10bit,
++ .num_regs = ARRAY_SIZE(ov5647_640x480_10bit)
+ },
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch b/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch
new file mode 100644
index 0000000000..9b874485a8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch
@@ -0,0 +1,119 @@
+From c92b64465d1f6dbfaf189dbf68128ec52fcb0521 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 30 Apr 2020 11:03:00 +0100
+Subject: [PATCH] media: i2c: ov5647: Advertise the correct exposure
+ range
+
+Exposure is clipped by the VTS of the mode, so needs to be updated as
+and when this is changed.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 47 +++++++++++++++++++++++++++++++-------
+ 1 file changed, 39 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -84,6 +84,11 @@
+ #define OV5647_VBLANK_MIN 4
+ #define OV5647_VTS_MAX 32767
+
++#define OV5647_EXPOSURE_MIN 4
++#define OV5647_EXPOSURE_STEP 1
++#define OV5647_EXPOSURE_DEFAULT 1000
++#define OV5647_EXPOSURE_MAX 65535
++
+ struct regval_list {
+ u16 addr;
+ u8 data;
+@@ -117,6 +122,7 @@ struct ov5647 {
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *exposure;
+ bool write_mode_regs;
+ };
+
+@@ -1202,7 +1208,7 @@ static int ov5647_set_fmt(struct v4l2_su
+ * If we have changed modes, write the I2C register list on
+ * a stream_on().
+ */
+- int hblank;
++ int exposure_max, exposure_def, hblank;
+
+ if (state->mode != mode)
+ state->write_mode_regs = true;
+@@ -1223,6 +1229,15 @@ static int ov5647_set_fmt(struct v4l2_su
+ mode->vts_def - mode->format.height);
+ __v4l2_ctrl_s_ctrl(state->vblank,
+ mode->vts_def - mode->format.height);
++
++ exposure_max = mode->vts_def - 4;
++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
++ exposure_max : OV5647_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(state->exposure,
++ state->exposure->minimum,
++ exposure_max,
++ state->exposure->step,
++ exposure_def);
+ }
+
+ mutex_unlock(&state->lock);
+@@ -1415,6 +1430,19 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+
+ /* v4l2_ctrl_lock() locks our own mutex */
+
++ if (ctrl->id == V4L2_CID_VBLANK) {
++ int exposure_max, exposure_def;
++
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = state->mode->format.height + ctrl->val - 4;
++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
++ exposure_max : OV5647_EXPOSURE_DEFAULT;
++ __v4l2_ctrl_modify_range(state->exposure,
++ state->exposure->minimum,
++ exposure_max, state->exposure->step,
++ exposure_def);
++ }
++
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+@@ -1472,7 +1500,7 @@ static int ov5647_probe(struct i2c_clien
+ struct v4l2_subdev *sd;
+ struct device_node *np = client->dev.of_node;
+ u32 xclk_freq;
+- int hblank;
++ int hblank, exposure_max, exposure_def;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+@@ -1525,12 +1553,6 @@ static int ov5647_probe(struct i2c_clien
+ 0, /* skip_mask */
+ V4L2_EXPOSURE_MANUAL); /* default */
+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+- V4L2_CID_EXPOSURE,
+- 4, /* min lines */
+- 65535, /* max lines (4+8+4 bits)*/
+- 1, /* step */
+- 1000); /* default number of lines */
+- v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ 16, /* min, 16 = 1.0x */
+ 1023, /* max (10 bits) */
+@@ -1540,6 +1562,15 @@ static int ov5647_probe(struct i2c_clien
+ /* Set the default mode before we init the subdev */
+ sensor->mode = OV5647_DEFAULT_MODE;
+
++ exposure_max = sensor->mode->vts_def - 4;
++ exposure_def = (exposure_max < OV5647_EXPOSURE_DEFAULT) ?
++ exposure_max : OV5647_EXPOSURE_DEFAULT;
++ sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ OV5647_EXPOSURE_MIN, exposure_max,
++ OV5647_EXPOSURE_STEP,
++ exposure_def);
++
+ /* By default, PIXEL_RATE is read only, but it does change per mode */
+ sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch b/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch
new file mode 100644
index 0000000000..fa700a81a3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch
@@ -0,0 +1,27 @@
+From 58694d6b23e4138640042fd779df95c425be825b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 20 Apr 2020 11:01:21 +0100
+Subject: [PATCH] media: i2c: imx219: Declare that the driver can
+ create events
+
+The flag V4L2_SUBDEV_FL_HAS_EVENTS is required if the subdev can
+generate events. It can create events from the ctrl handler, therefore
+this is required.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/imx219.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -1573,7 +1573,8 @@ static int imx219_probe(struct i2c_clien
+
+ /* Initialize subdev */
+ imx219->sd.internal_ops = &imx219_internal_ops;
+- imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
+ imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pads */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch b/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch
new file mode 100644
index 0000000000..b30d106f6e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch
@@ -0,0 +1,82 @@
+From 40aaca6ed160e67e518c512908cf49efb4cbed8b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 16:45:02 +0100
+Subject: [PATCH] media: bcm2835-unicam: Add support for
+ VIDIOC_[S|G]_SELECTION
+
+Sensors are now reflecting cropping and scaling parameters through
+the selection API, therefore Unicam needs to forward the requests
+through to the subdev.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 44 +++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -1898,6 +1898,39 @@ static int unicam_g_edid(struct file *fi
+ return v4l2_subdev_call(dev->sensor, pad, get_edid, edid);
+ }
+
++static int unicam_s_selection(struct file *file, void *priv,
++ struct v4l2_selection *sel)
++{
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ struct v4l2_subdev_selection sdsel = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .target = sel->target,
++ .flags = sel->flags,
++ .r = sel->r,
++ };
++
++ return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel);
++}
++
++static int unicam_g_selection(struct file *file, void *priv,
++ struct v4l2_selection *sel)
++{
++ struct unicam_node *node = video_drvdata(file);
++ struct unicam_device *dev = node->dev;
++ struct v4l2_subdev_selection sdsel = {
++ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
++ .target = sel->target,
++ };
++ int ret;
++
++ ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel);
++ if (!ret)
++ sel->r = sdsel.r;
++
++ return ret;
++}
++
+ static int unicam_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+ {
+@@ -2218,6 +2251,9 @@ static const struct v4l2_ioctl_ops unica
+ .vidioc_enum_framesizes = unicam_enum_framesizes,
+ .vidioc_enum_frameintervals = unicam_enum_frameintervals,
+
++ .vidioc_g_selection = unicam_g_selection,
++ .vidioc_s_selection = unicam_s_selection,
++
+ .vidioc_g_parm = unicam_g_parm,
+ .vidioc_s_parm = unicam_s_parm,
+
+@@ -2446,6 +2482,14 @@ static int register_node(struct unicam_d
+ !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size))
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
+
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, pad, set_selection))
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_SELECTION);
++
++ if (node->pad_id == METADATA_PAD ||
++ !v4l2_subdev_has_op(unicam->sensor, pad, get_selection))
++ v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_SELECTION);
++
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ unicam_err(unicam, "Unable to register video device.\n");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch b/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch
new file mode 100644
index 0000000000..74abba4ac9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch
@@ -0,0 +1,28 @@
+From 37e9677654a48cfa748c1d22fbf782920ab2cb23 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 29 Apr 2020 22:05:09 +0100
+Subject: [PATCH] media: bcm2835-unicam: Do not stop streaming in
+ unicam_release
+
+unicam_release calls _vb2_fop_release, which will call stop_streaming
+if that particular node was streaming. Calling it unconditionally (as
+the code was) means that if a second handle was opened eg to alter
+a setting, on closing that connection it also stopped Unicam.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -2204,9 +2204,6 @@ static int unicam_release(struct file *f
+ if (fh_singular)
+ v4l2_subdev_call(sd, core, s_power, 0);
+
+- if (node->streaming)
+- unicam_stop_streaming(&node->buffer_queue);
+-
+ node->open--;
+ mutex_unlock(&node->lock);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch b/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch
new file mode 100644
index 0000000000..ba979632ae
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch
@@ -0,0 +1,38 @@
+From 3681c556d79797f132e18973611a14681ed47f76 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 30 Apr 2020 09:52:50 +0100
+Subject: [PATCH] media: bcm2835-unicam: Fix reference counting in
+ unicam_open
+
+The reference counting of node->open was only incremented after
+a check that the node was v4l2_fh_is_singular_file, which resulted
+in the counting going wrong and s_power not being called at an
+appropriate time.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -2170,16 +2170,18 @@ static int unicam_open(struct file *file
+ goto unlock;
+ }
+
++ node->open++;
++
+ if (!v4l2_fh_is_singular_file(file))
+ goto unlock;
+
+ ret = v4l2_subdev_call(dev->sensor, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ v4l2_fh_release(file);
++ node->open--;
+ goto unlock;
+ }
+
+- node->open++;
+ ret = 0;
+
+ unlock:
diff --git a/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch b/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch
new file mode 100644
index 0000000000..1673bde514
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch
@@ -0,0 +1,333 @@
+From 01a1601512893ce9a3353e1686a95a9861f3d685 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 1 May 2020 14:15:24 +0100
+Subject: [PATCH] staging: vc04_services: ISP: Add enum_framesizes
+ ioctl
+
+This is used to enumerate available frame sizes on all nodes
+apart from statistics output.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../bcm2835-isp/bcm2835-v4l2-isp.c | 48 +++++++++++++++++--
+ .../bcm2835-isp/bcm2835_isp_fmts.h | 29 +++++++++++
+ 2 files changed, 72 insertions(+), 5 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -227,8 +227,9 @@ static const struct bcm2835_isp_fmt *get
+ return NULL;
+ }
+
+-static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
+- struct bcm2835_isp_node *node)
++static const
++struct bcm2835_isp_fmt *find_format_by_fourcc(unsigned int fourcc,
++ struct bcm2835_isp_node *node)
+ {
+ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts;
+ struct bcm2835_isp_fmt *fmt;
+@@ -236,15 +237,22 @@ static struct bcm2835_isp_fmt *find_form
+
+ for (i = 0; i < fmts->num_entries; i++) {
+ fmt = &fmts->list[i];
+- if (fmt->fourcc == (node_is_stats(node) ?
+- f->fmt.meta.dataformat :
+- f->fmt.pix.pixelformat))
++ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+
+ return NULL;
+ }
+
++static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
++ struct bcm2835_isp_node *node)
++{
++ return find_format_by_fourcc(node_is_stats(node) ?
++ f->fmt.meta.dataformat :
++ f->fmt.pix.pixelformat,
++ node);
++}
++
+ /* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL
+ *
+ * Copies all the required fields from a VB2 buffer to the MMAL buffer header,
+@@ -892,6 +900,35 @@ static int bcm2835_isp_node_enum_fmt(str
+ return -EINVAL;
+ }
+
++static int bcm2835_isp_enum_framesizes(struct file *file, void *priv,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct bcm2835_isp_node *node = video_drvdata(file);
++ struct bcm2835_isp_dev *dev = node_get_dev(node);
++ struct bcm2835_isp_fmt *fmt;
++
++ if (node_is_stats(node) || fsize->index)
++ return -EINVAL;
++
++ fmt = find_format_by_fourcc(fsize->pixel_format, node);
++ if (!fmt) {
++ v4l2_err(&dev->v4l2_dev, "Invalid pixel code: %x\n",
++ fsize->pixel_format);
++ return -EINVAL;
++ }
++
++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++ fsize->stepwise.min_width = MIN_DIM;
++ fsize->stepwise.max_width = MAX_DIM;
++ fsize->stepwise.step_width = fmt->step_size;
++
++ fsize->stepwise.min_height = MIN_DIM;
++ fsize->stepwise.max_height = MAX_DIM;
++ fsize->stepwise.step_height = fmt->step_size;
++
++ return 0;
++}
++
+ static int bcm2835_isp_node_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+ {
+@@ -1046,6 +1083,7 @@ static const struct v4l2_ioctl_ops bcm28
+ .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt,
+ .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt,
+ .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt,
++ .vidioc_enum_framesizes = bcm2835_isp_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
+@@ -22,6 +22,7 @@ struct bcm2835_isp_fmt {
+ u32 mmal_fmt;
+ int size_multiplier_x2;
+ enum v4l2_colorspace colorspace;
++ unsigned int step_size;
+ };
+
+ struct bcm2835_isp_fmt_list {
+@@ -39,6 +40,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_I420,
+ .size_multiplier_x2 = 3,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 8,
+@@ -47,6 +49,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_YV12,
+ .size_multiplier_x2 = 3,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 8,
+@@ -55,6 +58,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_NV12,
+ .size_multiplier_x2 = 3,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .depth = 8,
+@@ -63,6 +67,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_NV21,
+ .size_multiplier_x2 = 3,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+@@ -71,6 +76,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_YUYV,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+@@ -79,6 +85,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_UYVY,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+@@ -87,6 +94,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_YVYU,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+@@ -95,6 +103,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_VYUY,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
++ .step_size = 2,
+ }, {
+ /* RGB formats */
+ .fourcc = V4L2_PIX_FMT_RGB24,
+@@ -104,6 +113,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_RGB24,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
++ .step_size = 1,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+@@ -112,6 +122,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_RGB16,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
++ .step_size = 1,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+@@ -120,6 +131,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BGR24,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
++ .step_size = 1,
+ }, {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .depth = 32,
+@@ -128,6 +140,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BGRA,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
++ .step_size = 1,
+ }, {
+ /* Bayer formats */
+ /* 8 bit */
+@@ -138,6 +151,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+@@ -146,6 +160,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+@@ -154,6 +169,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+@@ -162,6 +178,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ /* 10 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
+@@ -171,6 +188,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
+ .depth = 10,
+@@ -179,6 +197,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
+ .depth = 10,
+@@ -187,6 +206,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
+ .depth = 10,
+@@ -195,6 +215,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ /* 12 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
+@@ -204,6 +225,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
+ .depth = 12,
+@@ -212,6 +234,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
+ .depth = 12,
+@@ -220,6 +243,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
+ .depth = 12,
+@@ -228,6 +252,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ /* 16 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+@@ -237,6 +262,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .depth = 16,
+@@ -245,6 +271,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .depth = 16,
+@@ -253,6 +280,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .depth = 16,
+@@ -261,6 +289,7 @@ static const struct bcm2835_isp_fmt supp
+ .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16,
+ .size_multiplier_x2 = 2,
+ .colorspace = V4L2_COLORSPACE_RAW,
++ .step_size = 2,
+ }, {
+ /* ISP statistics format */
+ .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch b/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch
new file mode 100644
index 0000000000..2be5524a07
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch
@@ -0,0 +1,28 @@
+From 4172a6bd7e4afada99911947a335d47e94802be5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 1 May 2020 14:58:23 +0100
+Subject: [PATCH] SQUASH: spi: Demote SPI_CS_HIGH warning to KERN_DEBUG
+
+This warning is unavoidable from a client's perspective and
+doesn't indicate anything wrong (just surprising).
+
+SQUASH with "spi: use_gpio_descriptor fixup moved to spi_setup"
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi.c
++++ b/drivers/spi/spi.c
+@@ -3044,8 +3044,8 @@ int spi_setup(struct spi_device *spi)
+
+ if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
+ ctlr->cs_gpiods[spi->chip_select] && !(spi->mode & SPI_CS_HIGH)) {
+- dev_warn(&spi->dev,
+- "setup: forcing CS_HIGH (use_gpio_descriptors)\n");
++ dev_dbg(&spi->dev,
++ "setup: forcing CS_HIGH (use_gpio_descriptors)\n");
+ spi->mode |= SPI_CS_HIGH;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch
new file mode 100644
index 0000000000..3fb47589ee
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch
@@ -0,0 +1,801 @@
+From a2f673d1aa39752608e5f4838ed9656b38cbc4b9 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.org>
+Date: Thu, 4 Apr 2019 13:33:47 +0100
+Subject: [PATCH] bcm2835-dma: Add proper 40-bit DMA support
+
+BCM2711 has 4 DMA channels with a 40-bit address range, allowing them
+to access the full 4GB of memory on a Pi 4.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.org>
+---
+ drivers/dma/bcm2835-dma.c | 485 ++++++++++++++++++++++++++++++++------
+ 1 file changed, 412 insertions(+), 73 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -38,6 +38,11 @@
+ #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
+ #define BCM2835_DMA_CHAN_NAME_SIZE 8
+ #define BCM2835_DMA_BULK_MASK BIT(0)
++#define BCM2711_DMA_MEMCPY_CHAN 14
++
++struct bcm2835_dma_cfg_data {
++ u32 chan_40bit_mask;
++};
+
+ /**
+ * struct bcm2835_dmadev - BCM2835 DMA controller
+@@ -52,6 +57,7 @@ struct bcm2835_dmadev {
+ void __iomem *base;
+ struct device_dma_parameters dma_parms;
+ dma_addr_t zero_page;
++ const struct bcm2835_dma_cfg_data *cfg_data;
+ };
+
+ struct bcm2835_dma_cb {
+@@ -64,6 +70,17 @@ struct bcm2835_dma_cb {
+ uint32_t pad[2];
+ };
+
++struct bcm2711_dma40_scb {
++ uint32_t ti;
++ uint32_t src;
++ uint32_t srci;
++ uint32_t dst;
++ uint32_t dsti;
++ uint32_t len;
++ uint32_t next_cb;
++ uint32_t rsvd;
++};
++
+ struct bcm2835_cb_entry {
+ struct bcm2835_dma_cb *cb;
+ dma_addr_t paddr;
+@@ -84,6 +101,7 @@ struct bcm2835_chan {
+ unsigned int irq_flags;
+
+ bool is_lite_channel;
++ bool is_40bit_channel;
+ };
+
+ struct bcm2835_desc {
+@@ -173,13 +191,118 @@ struct bcm2835_desc {
+ #define BCM2835_DMA_DATA_TYPE_S128 16
+
+ /* Valid only for channels 0 - 14, 15 has its own base address */
+-#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */
++#define BCM2835_DMA_CHAN_SIZE 0x100
++#define BCM2835_DMA_CHAN(n) ((n) * BCM2835_DMA_CHAN_SIZE) /* Base address */
+ #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
+
+ /* the max dma length for different channels */
+ #define MAX_DMA_LEN SZ_1G
+ #define MAX_LITE_DMA_LEN (SZ_64K - 4)
+
++/* 40-bit DMA support */
++#define BCM2711_DMA40_CS 0x00
++#define BCM2711_DMA40_CB 0x04
++#define BCM2711_DMA40_DEBUG 0x0c
++#define BCM2711_DMA40_TI 0x10
++#define BCM2711_DMA40_SRC 0x14
++#define BCM2711_DMA40_SRCI 0x18
++#define BCM2711_DMA40_DEST 0x1c
++#define BCM2711_DMA40_DESTI 0x20
++#define BCM2711_DMA40_LEN 0x24
++#define BCM2711_DMA40_NEXT_CB 0x28
++#define BCM2711_DMA40_DEBUG2 0x2c
++
++#define BCM2711_DMA40_ACTIVE BIT(0)
++#define BCM2711_DMA40_END BIT(1)
++#define BCM2711_DMA40_INT BIT(2)
++#define BCM2711_DMA40_DREQ BIT(3) /* DREQ state */
++#define BCM2711_DMA40_RD_PAUSED BIT(4) /* Reading is paused */
++#define BCM2711_DMA40_WR_PAUSED BIT(5) /* Writing is paused */
++#define BCM2711_DMA40_DREQ_PAUSED BIT(6) /* Is paused by DREQ flow control */
++#define BCM2711_DMA40_WAITING_FOR_WRITES BIT(7) /* Waiting for last write */
++#define BCM2711_DMA40_ERR BIT(10)
++#define BCM2711_DMA40_QOS(x) (((x) & 0x1f) << 16)
++#define BCM2711_DMA40_PANIC_QOS(x) (((x) & 0x1f) << 20)
++#define BCM2711_DMA40_WAIT_FOR_WRITES BIT(28)
++#define BCM2711_DMA40_DISDEBUG BIT(29)
++#define BCM2711_DMA40_ABORT BIT(30)
++#define BCM2711_DMA40_HALT BIT(31)
++#define BCM2711_DMA40_CS_FLAGS(x) (x & (BCM2711_DMA40_QOS(15) | \
++ BCM2711_DMA40_PANIC_QOS(15) | \
++ BCM2711_DMA40_WAIT_FOR_WRITES | \
++ BCM2711_DMA40_DISDEBUG))
++
++/* Transfer information bits */
++#define BCM2711_DMA40_INTEN BIT(0)
++#define BCM2711_DMA40_TDMODE BIT(1) /* 2D-Mode */
++#define BCM2711_DMA40_WAIT_RESP BIT(2) /* wait for AXI write to be acked */
++#define BCM2711_DMA40_WAIT_RD_RESP BIT(3) /* wait for AXI read to complete */
++#define BCM2711_DMA40_PER_MAP(x) ((x & 31) << 9) /* REQ source */
++#define BCM2711_DMA40_S_DREQ BIT(14) /* enable SREQ for source */
++#define BCM2711_DMA40_D_DREQ BIT(15) /* enable DREQ for destination */
++#define BCM2711_DMA40_S_WAIT(x) ((x & 0xff) << 16) /* add DMA read-wait cycles */
++#define BCM2711_DMA40_D_WAIT(x) ((x & 0xff) << 24) /* add DMA write-wait cycles */
++
++/* debug register bits */
++#define BCM2711_DMA40_DEBUG_WRITE_ERR BIT(0)
++#define BCM2711_DMA40_DEBUG_FIFO_ERR BIT(1)
++#define BCM2711_DMA40_DEBUG_READ_ERR BIT(2)
++#define BCM2711_DMA40_DEBUG_READ_CB_ERR BIT(3)
++#define BCM2711_DMA40_DEBUG_IN_ON_ERR BIT(8)
++#define BCM2711_DMA40_DEBUG_ABORT_ON_ERR BIT(9)
++#define BCM2711_DMA40_DEBUG_HALT_ON_ERR BIT(10)
++#define BCM2711_DMA40_DEBUG_DISABLE_CLK_GATE BIT(11)
++#define BCM2711_DMA40_DEBUG_RSTATE_SHIFT 14
++#define BCM2711_DMA40_DEBUG_RSTATE_BITS 4
++#define BCM2711_DMA40_DEBUG_WSTATE_SHIFT 18
++#define BCM2711_DMA40_DEBUG_WSTATE_BITS 4
++#define BCM2711_DMA40_DEBUG_RESET BIT(23)
++#define BCM2711_DMA40_DEBUG_ID_SHIFT 24
++#define BCM2711_DMA40_DEBUG_ID_BITS 4
++#define BCM2711_DMA40_DEBUG_VERSION_SHIFT 28
++#define BCM2711_DMA40_DEBUG_VERSION_BITS 4
++
++/* Valid only for channels 0 - 3 (11 - 14) */
++#define BCM2711_DMA40_CHAN(n) (((n) + 11) << 8) /* Base address */
++#define BCM2711_DMA40_CHANIO(base, n) ((base) + BCM2711_DMA_CHAN(n))
++
++/* the max dma length for different channels */
++#define MAX_DMA40_LEN SZ_1G
++
++#define BCM2711_DMA40_BURST_LEN(x) ((min(x,16) - 1) << 8)
++#define BCM2711_DMA40_INC BIT(12)
++#define BCM2711_DMA40_SIZE_32 (0 << 13)
++#define BCM2711_DMA40_SIZE_64 (1 << 13)
++#define BCM2711_DMA40_SIZE_128 (2 << 13)
++#define BCM2711_DMA40_SIZE_256 (3 << 13)
++#define BCM2711_DMA40_IGNORE BIT(15)
++#define BCM2711_DMA40_STRIDE(x) ((x) << 16) /* For 2D mode */
++
++#define BCM2711_DMA40_MEMCPY_FLAGS \
++ (BCM2711_DMA40_QOS(0) | \
++ BCM2711_DMA40_PANIC_QOS(0) | \
++ BCM2711_DMA40_WAIT_FOR_WRITES | \
++ BCM2711_DMA40_DISDEBUG)
++
++#define BCM2711_DMA40_MEMCPY_XFER_INFO \
++ (BCM2711_DMA40_SIZE_128 | \
++ BCM2711_DMA40_INC | \
++ BCM2711_DMA40_BURST_LEN(16))
++
++struct bcm2835_dmadev *memcpy_parent;
++static void __iomem *memcpy_chan;
++static struct bcm2711_dma40_scb *memcpy_scb;
++static dma_addr_t memcpy_scb_dma;
++DEFINE_SPINLOCK(memcpy_lock);
++
++static const struct bcm2835_dma_cfg_data bcm2835_dma_cfg = {
++ .chan_40bit_mask = 0,
++};
++
++static const struct bcm2835_dma_cfg_data bcm2711_dma_cfg = {
++ .chan_40bit_mask = BIT(11) | BIT(12) | BIT(13) | BIT(14),
++};
++
+ static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
+ {
+ /* lite and normal channels have different max frame length */
+@@ -209,6 +332,32 @@ static inline struct bcm2835_desc *to_bc
+ return container_of(t, struct bcm2835_desc, vd.tx);
+ }
+
++static inline uint32_t to_bcm2711_ti(uint32_t info)
++{
++ return ((info & BCM2835_DMA_INT_EN) ? BCM2711_DMA40_INTEN : 0) |
++ ((info & BCM2835_DMA_WAIT_RESP) ? BCM2711_DMA40_WAIT_RESP : 0) |
++ ((info & BCM2835_DMA_S_DREQ) ?
++ (BCM2711_DMA40_S_DREQ | BCM2711_DMA40_WAIT_RD_RESP) : 0) |
++ ((info & BCM2835_DMA_D_DREQ) ? BCM2711_DMA40_D_DREQ : 0) |
++ BCM2711_DMA40_PER_MAP((info >> 16) & 0x1f);
++}
++
++static inline uint32_t to_bcm2711_srci(uint32_t info)
++{
++ return ((info & BCM2835_DMA_S_INC) ? BCM2711_DMA40_INC : 0);
++}
++
++static inline uint32_t to_bcm2711_dsti(uint32_t info)
++{
++ return ((info & BCM2835_DMA_D_INC) ? BCM2711_DMA40_INC : 0);
++}
++
++static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr)
++{
++ BUG_ON(addr & 0x1f);
++ return (addr >> 5);
++}
++
+ static void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
+ {
+ size_t i;
+@@ -227,45 +376,53 @@ static void bcm2835_dma_desc_free(struct
+ }
+
+ static void bcm2835_dma_create_cb_set_length(
+- struct bcm2835_chan *chan,
++ struct bcm2835_chan *c,
+ struct bcm2835_dma_cb *control_block,
+ size_t len,
+ size_t period_len,
+ size_t *total_len,
+ u32 finalextrainfo)
+ {
+- size_t max_len = bcm2835_dma_max_frame_length(chan);
++ size_t max_len = bcm2835_dma_max_frame_length(c);
++ uint32_t cb_len;
+
+ /* set the length taking lite-channel limitations into account */
+- control_block->length = min_t(u32, len, max_len);
++ cb_len = min_t(u32, len, max_len);
+
+- /* finished if we have no period_length */
+- if (!period_len)
+- return;
++ if (period_len) {
++ /*
++ * period_len means: that we need to generate
++ * transfers that are terminating at every
++ * multiple of period_len - this is typically
++ * used to set the interrupt flag in info
++ * which is required during cyclic transfers
++ */
+
+- /*
+- * period_len means: that we need to generate
+- * transfers that are terminating at every
+- * multiple of period_len - this is typically
+- * used to set the interrupt flag in info
+- * which is required during cyclic transfers
+- */
++ /* have we filled in period_length yet? */
++ if (*total_len + cb_len < period_len) {
++ /* update number of bytes in this period so far */
++ *total_len += cb_len;
++ } else {
++ /* calculate the length that remains to reach period_len */
++ cb_len = period_len - *total_len;
+
+- /* have we filled in period_length yet? */
+- if (*total_len + control_block->length < period_len) {
+- /* update number of bytes in this period so far */
+- *total_len += control_block->length;
+- return;
++ /* reset total_length for next period */
++ *total_len = 0;
++ }
+ }
+
+- /* calculate the length that remains to reach period_length */
+- control_block->length = period_len - *total_len;
+-
+- /* reset total_length for next period */
+- *total_len = 0;
+-
+- /* add extrainfo bits in info */
+- control_block->info |= finalextrainfo;
++ if (c->is_40bit_channel) {
++ struct bcm2711_dma40_scb *scb =
++ (struct bcm2711_dma40_scb *)control_block;
++
++ scb->len = cb_len;
++ /* add extrainfo bits to ti */
++ scb->ti |= to_bcm2711_ti(finalextrainfo);
++ } else {
++ control_block->length = cb_len;
++ /* add extrainfo bits to info */
++ control_block->info |= finalextrainfo;
++ }
+ }
+
+ static inline size_t bcm2835_dma_count_frames_for_sg(
+@@ -288,7 +445,7 @@ static inline size_t bcm2835_dma_count_f
+ /**
+ * bcm2835_dma_create_cb_chain - create a control block and fills data in
+ *
+- * @chan: the @dma_chan for which we run this
++ * @c: the @bcm2835_chan for which we run this
+ * @direction: the direction in which we transfer
+ * @cyclic: it is a cyclic transfer
+ * @info: the default info bits to apply per controlblock
+@@ -306,12 +463,11 @@ static inline size_t bcm2835_dma_count_f
+ * @gfp: the GFP flag to use for allocation
+ */
+ static struct bcm2835_desc *bcm2835_dma_create_cb_chain(
+- struct dma_chan *chan, enum dma_transfer_direction direction,
++ struct bcm2835_chan *c, enum dma_transfer_direction direction,
+ bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
+ dma_addr_t src, dma_addr_t dst, size_t buf_len,
+ size_t period_len, gfp_t gfp)
+ {
+- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ size_t len = buf_len, total_len;
+ size_t frame;
+ struct bcm2835_desc *d;
+@@ -343,11 +499,23 @@ static struct bcm2835_desc *bcm2835_dma_
+
+ /* fill in the control block */
+ control_block = cb_entry->cb;
+- control_block->info = info;
+- control_block->src = src;
+- control_block->dst = dst;
+- control_block->stride = 0;
+- control_block->next = 0;
++ if (c->is_40bit_channel) {
++ struct bcm2711_dma40_scb *scb =
++ (struct bcm2711_dma40_scb *)control_block;
++ scb->ti = to_bcm2711_ti(info);
++ scb->src = lower_32_bits(src);
++ scb->srci= upper_32_bits(src) | to_bcm2711_srci(info);
++ scb->dst = lower_32_bits(dst);
++ scb->dsti = upper_32_bits(dst) | to_bcm2711_dsti(info);
++ scb->next_cb = 0;
++ } else {
++ control_block->info = info;
++ control_block->src = src;
++ control_block->dst = dst;
++ control_block->stride = 0;
++ control_block->next = 0;
++ }
++
+ /* set up length in control_block if requested */
+ if (buf_len) {
+ /* calculate length honoring period_length */
+@@ -361,7 +529,11 @@ static struct bcm2835_desc *bcm2835_dma_
+ }
+
+ /* link this the last controlblock */
+- if (frame)
++ if (frame && c->is_40bit_channel)
++ ((struct bcm2711_dma40_scb *)
++ d->cb_list[frame - 1].cb)->next_cb =
++ to_bcm2711_cbaddr(cb_entry->paddr);
++ if (frame && !c->is_40bit_channel)
+ d->cb_list[frame - 1].cb->next = cb_entry->paddr;
+
+ /* update src and dst and length */
+@@ -371,11 +543,21 @@ static struct bcm2835_desc *bcm2835_dma_
+ dst += control_block->length;
+
+ /* Length of total transfer */
+- d->size += control_block->length;
++ if (c->is_40bit_channel)
++ d->size += ((struct bcm2711_dma40_scb *)control_block)->len;
++ else
++ d->size += control_block->length;
+ }
+
+ /* the last frame requires extra flags */
+- d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
++ if (c->is_40bit_channel) {
++ struct bcm2711_dma40_scb *scb =
++ (struct bcm2711_dma40_scb *)d->cb_list[d->frames-1].cb;
++
++ scb->ti |= to_bcm2711_ti(finalextrainfo);
++ } else {
++ d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
++ }
+
+ /* detect a size missmatch */
+ if (buf_len && (d->size != buf_len))
+@@ -389,13 +571,12 @@ error_cb:
+ }
+
+ static void bcm2835_dma_fill_cb_chain_with_sg(
+- struct dma_chan *chan,
++ struct bcm2835_chan *c,
+ enum dma_transfer_direction direction,
+ struct bcm2835_cb_entry *cb,
+ struct scatterlist *sgl,
+ unsigned int sg_len)
+ {
+- struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ size_t len, max_len;
+ unsigned int i;
+ dma_addr_t addr;
+@@ -403,14 +584,35 @@ static void bcm2835_dma_fill_cb_chain_wi
+
+ max_len = bcm2835_dma_max_frame_length(c);
+ for_each_sg(sgl, sgent, sg_len, i) {
+- for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
+- len > 0;
+- addr += cb->cb->length, len -= cb->cb->length, cb++) {
+- if (direction == DMA_DEV_TO_MEM)
+- cb->cb->dst = addr;
+- else
+- cb->cb->src = addr;
+- cb->cb->length = min(len, max_len);
++ if (c->is_40bit_channel) {
++ struct bcm2711_dma40_scb *scb;
++
++ for (addr = sg_dma_address(sgent),
++ len = sg_dma_len(sgent);
++ len > 0;
++ addr += scb->len, len -= scb->len, cb++) {
++ scb = (struct bcm2711_dma40_scb *)cb->cb;
++ if (direction == DMA_DEV_TO_MEM) {
++ scb->dst = lower_32_bits(addr);
++ scb->dsti = upper_32_bits(addr) | BCM2711_DMA40_INC;
++ } else {
++ scb->src = lower_32_bits(addr);
++ scb->srci = upper_32_bits(addr) | BCM2711_DMA40_INC;
++ }
++ scb->len = min(len, max_len);
++ }
++ } else {
++ for (addr = sg_dma_address(sgent),
++ len = sg_dma_len(sgent);
++ len > 0;
++ addr += cb->cb->length, len -= cb->cb->length,
++ cb++) {
++ if (direction == DMA_DEV_TO_MEM)
++ cb->cb->dst = addr;
++ else
++ cb->cb->src = addr;
++ cb->cb->length = min(len, max_len);
++ }
+ }
+ }
+ }
+@@ -419,6 +621,10 @@ static void bcm2835_dma_abort(struct bcm
+ {
+ void __iomem *chan_base = c->chan_base;
+ long int timeout = 10000;
++ u32 wait_mask = BCM2835_DMA_WAITING_FOR_WRITES;
++
++ if (c->is_40bit_channel)
++ wait_mask = BCM2711_DMA40_WAITING_FOR_WRITES;
+
+ /*
+ * A zero control block address means the channel is idle.
+@@ -431,8 +637,7 @@ static void bcm2835_dma_abort(struct bcm
+ writel(0, chan_base + BCM2835_DMA_CS);
+
+ /* Wait for any current AXI transfer to complete */
+- while ((readl(chan_base + BCM2835_DMA_CS) &
+- BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
++ while ((readl(chan_base + BCM2835_DMA_CS) & wait_mask) && --timeout)
+ cpu_relax();
+
+ /* Peripheral might be stuck and fail to signal AXI write responses */
+@@ -457,9 +662,16 @@ static void bcm2835_dma_start_desc(struc
+
+ c->desc = d = to_bcm2835_dma_desc(&vd->tx);
+
+- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
+- writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+- c->chan_base + BCM2835_DMA_CS);
++ if (c->is_40bit_channel) {
++ writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++ c->chan_base + BCM2711_DMA40_CB);
++ writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_CS_FLAGS(c->dreq),
++ c->chan_base + BCM2711_DMA40_CS);
++ } else {
++ writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
++ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
++ c->chan_base + BCM2835_DMA_CS);
++ }
+ }
+
+ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
+@@ -486,8 +698,7 @@ static irqreturn_t bcm2835_dma_callback(
+ * if this IRQ handler is threaded.) If the channel is finished, it
+ * will remain idle despite the ACTIVE flag being set.
+ */
+- writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE |
+- BCM2835_DMA_CS_FLAGS(c->dreq),
++ writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
+ c->chan_base + BCM2835_DMA_CS);
+
+ d = c->desc;
+@@ -590,9 +801,17 @@ static enum dma_status bcm2835_dma_tx_st
+ struct bcm2835_desc *d = c->desc;
+ dma_addr_t pos;
+
+- if (d->dir == DMA_MEM_TO_DEV)
++ if (d->dir == DMA_MEM_TO_DEV && c->is_40bit_channel)
++ pos = readl(c->chan_base + BCM2711_DMA40_SRC) +
++ ((readl(c->chan_base + BCM2711_DMA40_SRCI) &
++ 0xff) << 8);
++ else if (d->dir == DMA_MEM_TO_DEV && !c->is_40bit_channel)
+ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
+- else if (d->dir == DMA_DEV_TO_MEM)
++ else if (d->dir == DMA_DEV_TO_MEM && c->is_40bit_channel)
++ pos = readl(c->chan_base + BCM2711_DMA40_DEST) +
++ ((readl(c->chan_base + BCM2711_DMA40_DESTI) &
++ 0xff) << 8);
++ else if (d->dir == DMA_DEV_TO_MEM && !c->is_40bit_channel)
+ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD);
+ else
+ pos = 0;
+@@ -638,7 +857,7 @@ static struct dma_async_tx_descriptor *b
+ frames = bcm2835_dma_frames_for_length(len, max_len);
+
+ /* allocate the CB chain - this also fills in the pointers */
+- d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
++ d = bcm2835_dma_create_cb_chain(c, DMA_MEM_TO_MEM, false,
+ info, extra, frames,
+ src, dst, len, 0, GFP_KERNEL);
+ if (!d)
+@@ -673,11 +892,21 @@ static struct dma_async_tx_descriptor *b
+ if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ return NULL;
+ src = c->cfg.src_addr;
++ /*
++ * One would think it ought to be possible to get the physical
++ * to dma address mapping information from the dma-ranges DT
++ * property, but I've not found a way yet that doesn't involve
++ * open-coding the whole thing.
++ */
++ if (c->is_40bit_channel)
++ src |= 0x400000000ull;
+ info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
+ } else {
+ if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ return NULL;
+ dst = c->cfg.dst_addr;
++ if (c->is_40bit_channel)
++ dst |= 0x400000000ull;
+ info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
+ }
+
+@@ -685,7 +914,7 @@ static struct dma_async_tx_descriptor *b
+ frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
+
+ /* allocate the CB chain */
+- d = bcm2835_dma_create_cb_chain(chan, direction, false,
++ d = bcm2835_dma_create_cb_chain(c, direction, false,
+ info, extra,
+ frames, src, dst, 0, 0,
+ GFP_NOWAIT);
+@@ -693,7 +922,7 @@ static struct dma_async_tx_descriptor *b
+ return NULL;
+
+ /* fill in frames with scatterlist pointers */
+- bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list,
++ bcm2835_dma_fill_cb_chain_with_sg(c, direction, d->cb_list,
+ sgl, sg_len);
+
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
+@@ -747,12 +976,16 @@ static struct dma_async_tx_descriptor *b
+ if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ return NULL;
+ src = c->cfg.src_addr;
++ if (c->is_40bit_channel)
++ src |= 0x400000000ull;
+ dst = buf_addr;
+ info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
+ } else {
+ if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
+ return NULL;
+ dst = c->cfg.dst_addr;
++ if (c->is_40bit_channel)
++ dst |= 0x400000000ull;
+ src = buf_addr;
+ info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
+
+@@ -772,7 +1005,7 @@ static struct dma_async_tx_descriptor *b
+ * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
+ * implementation calls prep_dma_cyclic with interrupts disabled.
+ */
+- d = bcm2835_dma_create_cb_chain(chan, direction, true,
++ d = bcm2835_dma_create_cb_chain(c, direction, true,
+ info, extra,
+ frames, src, dst, buf_len,
+ period_len, GFP_NOWAIT);
+@@ -780,7 +1013,12 @@ static struct dma_async_tx_descriptor *b
+ return NULL;
+
+ /* wrap around into a loop */
+- d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
++ if (c->is_40bit_channel)
++ ((struct bcm2711_dma40_scb *)
++ d->cb_list[frames - 1].cb)->next_cb =
++ to_bcm2711_cbaddr(d->cb_list[0].paddr);
++ else
++ d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
+
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
+@@ -844,9 +1082,11 @@ static int bcm2835_dma_chan_init(struct
+ c->irq_number = irq;
+ c->irq_flags = irq_flags;
+
+- /* check in DEBUG register if this is a LITE channel */
+- if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
+- BCM2835_DMA_DEBUG_LITE)
++ /* check for 40bit and lite channels */
++ if (d->cfg_data->chan_40bit_mask & BIT(chan_id))
++ c->is_40bit_channel = true;
++ else if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
++ BCM2835_DMA_DEBUG_LITE)
+ c->is_lite_channel = true;
+
+ return 0;
+@@ -866,8 +1106,58 @@ static void bcm2835_dma_free(struct bcm2
+ DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
+ }
+
++int bcm2711_dma40_memcpy_init(void)
++{
++ if (!memcpy_parent)
++ return -EPROBE_DEFER;
++
++ if (!memcpy_chan)
++ return -EINVAL;
++
++ if (!memcpy_scb)
++ return -ENOMEM;
++
++ return 0;
++}
++EXPORT_SYMBOL(bcm2711_dma40_memcpy_init);
++
++void bcm2711_dma40_memcpy(dma_addr_t dst, dma_addr_t src, size_t size)
++{
++ struct bcm2711_dma40_scb *scb = memcpy_scb;
++ unsigned long flags;
++
++ if (!scb) {
++ pr_err("bcm2711_dma40_memcpy not initialised!\n");
++ return;
++ }
++
++ spin_lock_irqsave(&memcpy_lock, flags);
++
++ scb->ti = 0;
++ scb->src = lower_32_bits(src);
++ scb->srci = upper_32_bits(src) | BCM2711_DMA40_MEMCPY_XFER_INFO;
++ scb->dst = lower_32_bits(dst);
++ scb->dsti = upper_32_bits(dst) | BCM2711_DMA40_MEMCPY_XFER_INFO;
++ scb->len = size;
++ scb->next_cb = 0;
++
++ writel((u32)(memcpy_scb_dma >> 5), memcpy_chan + BCM2711_DMA40_CB);
++ writel(BCM2711_DMA40_MEMCPY_FLAGS + BCM2711_DMA40_ACTIVE,
++ memcpy_chan + BCM2711_DMA40_CS);
++
++ /* Poll for completion */
++ while (!(readl(memcpy_chan + BCM2711_DMA40_CS) & BCM2711_DMA40_END))
++ cpu_relax();
++
++ writel(BCM2711_DMA40_END, memcpy_chan + BCM2711_DMA40_CS);
++
++ spin_unlock_irqrestore(&memcpy_lock, flags);
++}
++EXPORT_SYMBOL(bcm2711_dma40_memcpy);
++
+ static const struct of_device_id bcm2835_dma_of_match[] = {
+- { .compatible = "brcm,bcm2835-dma", },
++ { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
++ { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
+@@ -899,6 +1189,8 @@ static int bcm2835_dma_probe(struct plat
+ int irq_flags;
+ uint32_t chans_available;
+ char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
++ const struct of_device_id *of_id;
++ int chan_count, chan_start, chan_end;
+
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+@@ -920,9 +1212,13 @@ static int bcm2835_dma_probe(struct plat
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+- rc = bcm_dmaman_probe(pdev, base, BCM2835_DMA_BULK_MASK);
+- if (rc)
+- dev_err(&pdev->dev, "Failed to initialize the legacy API\n");
++
++ /* The set of channels can be split across multiple instances. */
++ chan_start = ((u32)(uintptr_t)base / BCM2835_DMA_CHAN_SIZE) & 0xf;
++ base -= BCM2835_DMA_CHAN(chan_start);
++ chan_count = resource_size(res) / BCM2835_DMA_CHAN_SIZE;
++ chan_end = min(chan_start + chan_count,
++ BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1);
+
+ od->base = base;
+
+@@ -959,6 +1255,14 @@ static int bcm2835_dma_probe(struct plat
+ return -ENOMEM;
+ }
+
++ of_id = of_match_node(bcm2835_dma_of_match, pdev->dev.of_node);
++ if (!of_id) {
++ dev_err(&pdev->dev, "Failed to match compatible string\n");
++ return -EINVAL;
++ }
++
++ od->cfg_data = of_id->data;
++
+ /* Request DMA channel mask from device tree */
+ if (of_property_read_u32(pdev->dev.of_node,
+ "brcm,dma-channel-mask",
+@@ -968,11 +1272,34 @@ static int bcm2835_dma_probe(struct plat
+ goto err_no_dma;
+ }
+
+- /* Channel 0 is used by the legacy API */
+- chans_available &= ~BCM2835_DMA_BULK_MASK;
++ /* One channel is reserved for the legacy API */
++ if (chans_available & BCM2835_DMA_BULK_MASK) {
++ rc = bcm_dmaman_probe(pdev, base,
++ chans_available & BCM2835_DMA_BULK_MASK);
++ if (rc)
++ dev_err(&pdev->dev,
++ "Failed to initialize the legacy API\n");
++
++ chans_available &= ~BCM2835_DMA_BULK_MASK;
++ }
++
++ /* And possibly one for the 40-bit DMA memcpy API */
++ if (chans_available & od->cfg_data->chan_40bit_mask &
++ BIT(BCM2711_DMA_MEMCPY_CHAN)) {
++ memcpy_parent = od;
++ memcpy_chan = BCM2835_DMA_CHANIO(base, BCM2711_DMA_MEMCPY_CHAN);
++ memcpy_scb = dma_alloc_coherent(memcpy_parent->ddev.dev,
++ sizeof(*memcpy_scb),
++ &memcpy_scb_dma, GFP_KERNEL);
++ if (!memcpy_scb)
++ dev_warn(&pdev->dev,
++ "Failed to allocated memcpy scb\n");
++
++ chans_available &= ~BIT(BCM2711_DMA_MEMCPY_CHAN);
++ }
+
+ /* get irqs for each channel that we support */
+- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
++ for (i = chan_start; i < chan_end; i++) {
+ /* skip masked out channels */
+ if (!(chans_available & (1 << i))) {
+ irq[i] = -1;
+@@ -995,13 +1322,17 @@ static int bcm2835_dma_probe(struct plat
+ irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
+ }
+
++ chan_count = 0;
++
+ /* get irqs for each channel */
+- for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
++ for (i = chan_start; i < chan_end; i++) {
+ /* skip channels without irq */
+ if (irq[i] < 0)
+ continue;
+
+ /* check if there are other channels that also use this irq */
++ /* FIXME: This will fail if interrupts are shared across
++ instances */
+ irq_flags = 0;
+ for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
+ if ((i != j) && (irq[j] == irq[i])) {
+@@ -1013,9 +1344,10 @@ static int bcm2835_dma_probe(struct plat
+ rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags);
+ if (rc)
+ goto err_no_dma;
++ chan_count++;
+ }
+
+- dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i);
++ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", chan_count);
+
+ /* Device-tree DMA controller registration */
+ rc = of_dma_controller_register(pdev->dev.of_node,
+@@ -1047,6 +1379,13 @@ static int bcm2835_dma_remove(struct pla
+
+ bcm_dmaman_remove(pdev);
+ dma_async_device_unregister(&od->ddev);
++ if (memcpy_parent == od) {
++ dma_free_coherent(&pdev->dev, sizeof(*memcpy_scb), memcpy_scb,
++ memcpy_scb_dma);
++ memcpy_parent = NULL;
++ memcpy_scb = NULL;
++ memcpy_chan = NULL;
++ }
+ bcm2835_dma_free(od);
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch b/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch
new file mode 100644
index 0000000000..ac6543fe4f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch
@@ -0,0 +1,40 @@
+From 0dcafa8ab800d1f534bb0cb51cd02a082cf34be6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 30 Apr 2020 12:43:05 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Allow 40-bit DMA for SPI
+
+Add the spi_dma4 DT parameter to enable use of the 40-bit DMA channels
+to drive SPI. Note that there are only 3-4 40-bit channels available,
+and using this parameter claims 2 of them.
+
+Usage: dtparam=spi_dma4
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 ++
+ arch/arm/boot/dts/overlays/README | 4 ++++
+ 2 files changed, 6 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -554,5 +554,7 @@
+ eth_led0 = <&phy1>,"led-modes:0";
+ eth_led1 = <&phy1>,"led-modes:4";
+
++ spi_dma4 = <&spi0>, "dmas:0=", <&dma40>,
++ <&spi0>, "dmas:8=", <&dma40>;
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -159,6 +159,10 @@ Params:
+ spi Set to "on" to enable the spi interfaces
+ (default "off")
+
++ spi_dma4 Use to enable 40-bit DMA on spi interfaces
++ (the assigned value doesn't matter)
++ (2711 only)
++
+ random Set to "on" to enable the hardware random
+ number generator (default "on")
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch b/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch
new file mode 100644
index 0000000000..537ca5dc55
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch
@@ -0,0 +1,32 @@
+From 3c0cbb59e068f13b2a12a98a5dac42afa8ccc639 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 1 May 2020 17:56:13 +0100
+Subject: [PATCH] overlays: Make the i2c-gpio overlay safe again
+
+Like many overlays, the i2c-gpio overlay goes to efforts to avoid
+generating warnings about #address-cells and #size-cells not
+being defined, which it does by defining them. Unfortunately this
+is fatal if they don't match what the system requires, and the
+recent switch to #size-cells = 2 on 2711 made i2c-gpio very
+dangerous.
+
+In the absence of the knowledge of a clean way to fix this, just delete
+the declarations and suffer the warnings.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
+@@ -9,9 +9,6 @@
+ target-path = "/";
+
+ __overlay__ {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+ i2c_gpio: i2c@0 {
+ reg = <0xffffffff>;
+ compatible = "i2c-gpio";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch b/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch
new file mode 100644
index 0000000000..d2b0bf1f2f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch
@@ -0,0 +1,62 @@
+From 6a9cc90467f4b14d596a819c87d48764a4ba5282 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 May 2020 17:49:08 +0100
+Subject: [PATCH] staging: vc04_services: isp: Remove duplicated
+ initialisation
+
+With the codec code from which this was derived, the driver had to
+get the supported formats for both input and output ports.
+This had been copied across, however here we have independent nodes
+for each port, but the code had been left in to do the same thing
+twice.
+Remove the duplicate.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../bcm2835-isp/bcm2835-v4l2-isp.c | 35 -------------------
+ 1 file changed, 35 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -1160,41 +1160,6 @@ static int bcm2835_isp_get_supported_fmt
+ }
+ node->supported_fmts.num_entries = j;
+
+- param_size = sizeof(fourccs);
+- ret = vchiq_mmal_port_parameter_get(dev->mmal_instance,
+- get_port_data(node),
+- MMAL_PARAMETER_SUPPORTED_ENCODINGS,
+- &fourccs, &param_size);
+-
+- if (ret) {
+- if (ret == MMAL_MSG_STATUS_ENOSPC) {
+- v4l2_err(&dev->v4l2_dev,
+- "%s: port has more encoding than we provided space for. Some are dropped.\n",
+- __func__);
+- num_encodings = MAX_SUPPORTED_ENCODINGS;
+- } else {
+- return -EINVAL;
+- }
+- } else {
+- num_encodings = param_size / sizeof(u32);
+- }
+- /* Assume at this stage that all encodings will be supported in V4L2. */
+- list = devm_kzalloc(dev->dev,
+- sizeof(struct bcm2835_isp_fmt) * num_encodings,
+- GFP_KERNEL);
+- if (!list)
+- return -ENOMEM;
+- node->supported_fmts.list = list;
+-
+- for (i = 0, j = 0; i < num_encodings; i++) {
+- const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]);
+-
+- if (fmt) {
+- list[j] = *fmt;
+- j++;
+- }
+- }
+- node->supported_fmts.num_entries = j;
+ return 0;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch b/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch
new file mode 100644
index 0000000000..97ca47bcd0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch
@@ -0,0 +1,148 @@
+From 9ecf10bfe3e501b10cc72d710a70150640a0b266 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 May 2020 16:54:20 +0100
+Subject: [PATCH] staging: vc04_services: isp: Make all references to
+ bcm2835_isp_fmt const
+
+The array of potential formats and their configuration should be const.
+Rework all accesses so that this is possible.
+
+The list of supported formats was taking a copy of entries from this table.
+This is unnecessary, therefore allocate an array of pointers instead of
+an array of entries.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../bcm2835-isp/bcm2835-v4l2-isp.c | 34 ++++++++++---------
+ .../bcm2835-isp/bcm2835_isp_fmts.h | 2 +-
+ 2 files changed, 19 insertions(+), 17 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -67,7 +67,7 @@ struct bcm2835_isp_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+- struct bcm2835_isp_fmt *fmt;
++ const struct bcm2835_isp_fmt *fmt;
+ };
+
+ /*
+@@ -232,11 +232,11 @@ struct bcm2835_isp_fmt *find_format_by_f
+ struct bcm2835_isp_node *node)
+ {
+ struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts;
+- struct bcm2835_isp_fmt *fmt;
++ const struct bcm2835_isp_fmt *fmt;
+ unsigned int i;
+
+ for (i = 0; i < fmts->num_entries; i++) {
+- fmt = &fmts->list[i];
++ fmt = fmts->list[i];
+ if (fmt->fourcc == fourcc)
+ return fmt;
+ }
+@@ -244,8 +244,9 @@ struct bcm2835_isp_fmt *find_format_by_f
+ return NULL;
+ }
+
+-static struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
+- struct bcm2835_isp_node *node)
++static const
++struct bcm2835_isp_fmt *find_format(struct v4l2_format *f,
++ struct bcm2835_isp_node *node)
+ {
+ return find_format_by_fourcc(node_is_stats(node) ?
+ f->fmt.meta.dataformat :
+@@ -666,19 +667,20 @@ static const struct vb2_ops bcm2835_isp_
+ .stop_streaming = bcm2835_isp_node_stop_streaming,
+ };
+
+-static struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node)
++static const
++struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node)
+ {
+- return &node->supported_fmts.list[0];
++ return node->supported_fmts.list[0];
+ }
+
+ static inline unsigned int get_bytesperline(int width,
+- struct bcm2835_isp_fmt *fmt)
++ const struct bcm2835_isp_fmt *fmt)
+ {
+ return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align);
+ }
+
+ static inline unsigned int get_sizeimage(int bpl, int width, int height,
+- struct bcm2835_isp_fmt *fmt)
++ const struct bcm2835_isp_fmt *fmt)
+ {
+ return (bpl * height * fmt->size_multiplier_x2) >> 1;
+ }
+@@ -892,8 +894,8 @@ static int bcm2835_isp_node_enum_fmt(str
+
+ if (f->index < fmts->num_entries) {
+ /* Format found */
+- f->pixelformat = fmts->list[f->index].fourcc;
+- f->flags = fmts->list[f->index].flags;
++ f->pixelformat = fmts->list[f->index]->fourcc;
++ f->flags = fmts->list[f->index]->flags;
+ return 0;
+ }
+
+@@ -905,7 +907,7 @@ static int bcm2835_isp_enum_framesizes(s
+ {
+ struct bcm2835_isp_node *node = video_drvdata(file);
+ struct bcm2835_isp_dev *dev = node_get_dev(node);
+- struct bcm2835_isp_fmt *fmt;
++ const struct bcm2835_isp_fmt *fmt;
+
+ if (node_is_stats(node) || fsize->index)
+ return -EINVAL;
+@@ -933,7 +935,7 @@ static int bcm2835_isp_node_try_fmt(stru
+ struct v4l2_format *f)
+ {
+ struct bcm2835_isp_node *node = video_drvdata(file);
+- struct bcm2835_isp_fmt *fmt;
++ const struct bcm2835_isp_fmt *fmt;
+
+ if (f->type != node->queue.type)
+ return -EINVAL;
+@@ -1113,7 +1115,7 @@ static const struct v4l2_ioctl_ops bcm28
+ static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node)
+ {
+ struct bcm2835_isp_dev *dev = node_get_dev(node);
+- struct bcm2835_isp_fmt *list;
++ struct bcm2835_isp_fmt const **list;
+ unsigned int i, j, num_encodings;
+ u32 fourccs[MAX_SUPPORTED_ENCODINGS];
+ u32 param_size = sizeof(fourccs);
+@@ -1144,7 +1146,7 @@ static int bcm2835_isp_get_supported_fmt
+ * Any that aren't supported will waste a very small amount of memory.
+ */
+ list = devm_kzalloc(dev->dev,
+- sizeof(struct bcm2835_isp_fmt) * num_encodings,
++ sizeof(struct bcm2835_isp_fmt *) * num_encodings,
+ GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+@@ -1154,7 +1156,7 @@ static int bcm2835_isp_get_supported_fmt
+ const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]);
+
+ if (fmt) {
+- list[j] = *fmt;
++ list[j] = fmt;
+ j++;
+ }
+ }
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h
+@@ -26,7 +26,7 @@ struct bcm2835_isp_fmt {
+ };
+
+ struct bcm2835_isp_fmt_list {
+- struct bcm2835_isp_fmt *list;
++ struct bcm2835_isp_fmt const **list;
+ unsigned int num_entries;
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch b/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch
new file mode 100644
index 0000000000..579b3b61b6
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch
@@ -0,0 +1,29 @@
+From ae10c15867d9a5b85eefaf11333bd30473af4b2b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Sat, 2 May 2020 13:43:06 +0100
+Subject: [PATCH] overlays: gpio-keys: Avoid open-drain warnings
+
+The i2c-gpio driver expects to use a GPIO in open-drain mode. Failure
+to configure it in that way causes alarming warnings in the kernel log.
+The BCM283x and BCM2711 GPIO blocks don't support open-drain mode,
+but i2c-gpio works anyway. Silence the warning by declaring that
+open-drain mode has been enabled by other means.
+
+See: https://github.com/raspberrypi/firmware/issues/1381
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-gpio-overlay.dts
+@@ -16,6 +16,8 @@
+ &gpio 24 0 /* scl */
+ >;
+ i2c-gpio,delay-us = <2>; /* ~100 kHz */
++ i2c-gpio,sda-open-drain;
++ i2c-gpio,scl-open-drain;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch b/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch
new file mode 100644
index 0000000000..45429d506d
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch
@@ -0,0 +1,23 @@
+From 6dc415bb238d7c515f40305248d03234be88cadf Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 6 Apr 2020 17:07:31 +0100
+Subject: [PATCH] vc4_hdmi_phy: Fix typo in phy_get_cp_current
+
+This is stored in a 6-bit register field which causes a WARN
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -182,7 +182,7 @@ static u8 phy_get_cp_current(unsigned lo
+ if (vco_freq < 3700000000ULL)
+ return 0x1c;
+
+- return 0xc8;
++ return 0x18;
+ }
+
+ static u32 phy_get_rm_offset(unsigned long long vco_freq)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch b/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch
new file mode 100644
index 0000000000..628bec3f33
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch
@@ -0,0 +1,92 @@
+From 31bebbef0cce2ae68c25a2b0cbfc040f938791e9 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 4 May 2020 15:13:24 +0100
+Subject: [PATCH] overlays: Make use of intra-overlay fragments
+
+The firmware and runtime overlay support has recently been updated to
+correctly process fragments that target other fragments within the
+overlay. Make use of that ability and avoid the use of the awkward
+target-path = "<alias>/..." workaround and for better readability.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ads1015-overlay.dts | 8 ++++----
+ arch/arm/boot/dts/overlays/ads1115-overlay.dts | 8 ++++----
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/ads1015-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1015-overlay.dts
+@@ -24,7 +24,7 @@
+ };
+
+ fragment@1 {
+- target-path = "i2c_arm/ads1015";
++ target = <&ads1015>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -37,7 +37,7 @@
+ };
+
+ fragment@2 {
+- target-path = "i2c_arm/ads1015";
++ target = <&ads1015>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -50,7 +50,7 @@
+ };
+
+ fragment@3 {
+- target-path = "i2c_arm/ads1015";
++ target = <&ads1015>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -63,7 +63,7 @@
+ };
+
+ fragment@4 {
+- target-path = "i2c_arm/ads1015";
++ target = <&ads1015>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts
+@@ -26,7 +26,7 @@
+ };
+
+ fragment@1 {
+- target-path = "i2c_arm/ads1115";
++ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -40,7 +40,7 @@
+ };
+
+ fragment@2 {
+- target-path = "i2c_arm/ads1115";
++ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -54,7 +54,7 @@
+ };
+
+ fragment@3 {
+- target-path = "i2c_arm/ads1115";
++ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -68,7 +68,7 @@
+ };
+
+ fragment@4 {
+- target-path = "i2c_arm/ads1115";
++ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch b/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch
new file mode 100644
index 0000000000..b0e74e5e1b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch
@@ -0,0 +1,20 @@
+From e09aaa8b2074bfa1656f1af09357b26c87e39988 Mon Sep 17 00:00:00 2001
+From: Jacko Dirks <jdirks.linuxdev@gmail.com>
+Date: Tue, 5 May 2020 14:28:14 +0200
+Subject: [PATCH] media: i2c: tc358743: Fix fallthrough warning
+
+Signed-off-by: Jacko Dirks <jdirks.linuxdev@gmail.com>
+---
+ drivers/media/i2c/tc358743.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/media/i2c/tc358743.c
++++ b/drivers/media/i2c/tc358743.c
+@@ -2002,6 +2002,7 @@ static int tc358743_probe_of(struct tc35
+ switch (bps_pr_lane) {
+ default:
+ dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane);
++ /* fall through */
+ case 594000000U:
+ state->pdata.lineinitcnt = 0xe80;
+ state->pdata.lptxtimecnt = 0x003;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch b/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch
new file mode 100644
index 0000000000..2186d7b802
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch
@@ -0,0 +1,21 @@
+From f73609479c8542bd974a6e9aaf8bffc8ed09eaca Mon Sep 17 00:00:00 2001
+From: Jacko Dirks <jdirks.linuxdev@gmail.com>
+Date: Tue, 5 May 2020 14:33:31 +0200
+Subject: [PATCH] media: bcm2835: unicam: Fix uninitialized warning
+
+Signed-off-by: Jacko Dirks <jdirks.linuxdev@gmail.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -1001,7 +1001,7 @@ const struct unicam_fmt *get_first_suppo
+ {
+ struct v4l2_subdev_mbus_code_enum mbus_code;
+ const struct unicam_fmt *fmt = NULL;
+- int ret;
++ int ret = 0;
+ int j;
+
+ for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch b/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch
new file mode 100644
index 0000000000..90cb5e276e
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch
@@ -0,0 +1,34 @@
+From e005a4db95a48e8b14a2017bf56a0e3f3dccfa6d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 5 May 2020 19:45:41 +0100
+Subject: [PATCH] video: bcm2708_fb: Disable FB if no displays found
+
+If the firmware hasn't detected a display, the driver would assume
+one display was available, but because it had failed to retrieve the
+display size it would try to allocate a zero-sized buffer.
+
+Avoid the allocation failure by bailing out early if no display is
+found.
+
+See: https://github.com/raspberrypi/linux/issues/3598
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/video/fbdev/bcm2708_fb.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/video/fbdev/bcm2708_fb.c
++++ b/drivers/video/fbdev/bcm2708_fb.c
+@@ -1104,10 +1104,9 @@ static int bcm2708_fb_probe(struct platf
+ * set one display
+ */
+ if (ret || num_displays == 0) {
+- num_displays = 1;
+ dev_err(&dev->dev,
+- "Unable to determine number of FB's. Assuming 1\n");
+- ret = 0;
++ "Unable to determine number of FBs. Disabling driver.\n");
++ return -ENOENT;
+ } else {
+ fbdev->firmware_supports_multifb = 1;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch
new file mode 100644
index 0000000000..582c32aa90
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch
@@ -0,0 +1,38 @@
+From 48bf88ebb1abf55168f636d7a6edf2ca79be13c6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 6 May 2020 14:25:20 +0100
+Subject: [PATCH] overlays: sc16is752-spi1: Add xtal parameter
+
+The other sc16is75x overlays have an xtal parameter to allow a
+different crystal frequency to be specified, but sc16is752-spi1
+doesn't. Fix this omission.
+
+See: https://www.raspberrypi.org/forums/viewtopic.php?f=107&t=273234
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 1 +
+ arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts | 3 ++-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2114,6 +2114,7 @@ Info: Overlay for the NXP SC16IS752 Du
+
+ Load: dtoverlay=sc16is752-spi1,<param>=<val>
+ Params: int_pin GPIO used for IRQ (default 24)
++ xtal On-board crystal frequency (default 14745600)
+
+
+ Name: sdhost
+--- a/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts
+@@ -56,6 +56,7 @@
+ };
+
+ __overrides__ {
+- int_pin = <&sc16is752>,"interrupts:0";
++ int_pin = <&sc16is752>,"interrupts:0";
++ xtal = <&sc16is752_clk>,"clock-frequency:0";
+ };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch b/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch
new file mode 100644
index 0000000000..d6aa46a4f4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch
@@ -0,0 +1,42 @@
+From 602ec343e69479dbec368f67d09c9f3e3e5ac248 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:07 +0100
+Subject: [PATCH] vc4_hdmi: Fix register offset when sending longer CEC
+ messages
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1264,8 +1264,13 @@ static void vc4_cec_read_msg(struct vc4_
+
+ msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >>
+ VC4_HDMI_CEC_REC_WRD_CNT_SHIFT);
++
++ if (msg->len > 16) {
++ DRM_ERROR("Attempting to read too much data (%d)\n", msg->len);
++ return;
++ }
+ for (i = 0; i < msg->len; i += 4) {
+- u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i);
++ u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + (i>>2));
+
+ msg->msg[i] = val & 0xff;
+ msg->msg[i + 1] = (val >> 8) & 0xff;
+@@ -1361,8 +1366,12 @@ static int vc4_hdmi_cec_adap_transmit(st
+ u32 val;
+ unsigned int i;
+
++ if (msg->len > 16) {
++ DRM_ERROR("Attempting to transmit too much data (%d)\n", msg->len);
++ return -ENOMEM;
++ }
+ for (i = 0; i < msg->len; i += 4)
+- HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i,
++ HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i>>2),
+ (msg->msg[i]) |
+ (msg->msg[i + 1] << 8) |
+ (msg->msg[i + 2] << 16) |
diff --git a/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch b/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch
new file mode 100644
index 0000000000..a00aee15bd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch
@@ -0,0 +1,43 @@
+From 25402f4978434949e5ea550c9b1b4a192e95fd83 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:07 +0100
+Subject: [PATCH] vc4_hdmi: Fix up CEC registers
+
+Fix an incorrect register address, add a
+missing one and reorder into address order
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -33,11 +33,12 @@ enum vc4_hdmi_field {
+ HDMI_CEC_CNTRL_3,
+ HDMI_CEC_CNTRL_4,
+ HDMI_CEC_CNTRL_5,
++ HDMI_CEC_CPU_STATUS,
++ HDMI_CEC_CPU_SET,
+ HDMI_CEC_CPU_CLEAR,
+- HDMI_CEC_CPU_MASK_CLEAR,
+- HDMI_CEC_CPU_MASK_SET,
+ HDMI_CEC_CPU_MASK_STATUS,
+- HDMI_CEC_CPU_STATUS,
++ HDMI_CEC_CPU_MASK_SET,
++ HDMI_CEC_CPU_MASK_CLEAR,
+
+ /*
+ * Transmit data, first byte is low byte of the 32-bit reg.
+@@ -205,9 +206,10 @@ static const struct vc4_hdmi_register vc
+ VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0),
+ VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4),
+ VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340),
++ VC4_HDMI_REG(HDMI_CEC_CPU_SET, 0x0344),
+ VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348),
+ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c),
+- VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c),
++ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x0350),
+ VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354),
+ VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400),
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch b/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch
new file mode 100644
index 0000000000..d750e55abb
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch
@@ -0,0 +1,151 @@
+From 2aa3a92e409ed4ad416eceacef998f0027016a81 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:07 +0100
+Subject: [PATCH] vc4_hdmi_regs: Add Intr2 register block
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 14 ++++++++++----
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 8 ++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 2 ++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 17 +++++++++++++++++
+ 4 files changed, 37 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -316,7 +316,8 @@
+ <0x7ef01f00 0x400>,
+ <0x7ef00200 0x80>,
+ <0x7ef04300 0x100>,
+- <0x7ef20000 0x100>;
++ <0x7ef20000 0x100>,
++ <0x7ef00100 0x30>;
+ reg-names = "hdmi",
+ "dvp",
+ "phy",
+@@ -325,13 +326,15 @@
+ "metadata",
+ "csc",
+ "cec",
+- "hd";
++ "hd",
++ "intr2";
+ clocks = <&firmware_clocks 13>;
+ clock-names = "hdmi";
+ resets = <&dvp 0>;
+ ddc = <&ddc0>;
+ dmas = <&dma 10>;
+ dma-names = "audio-rx";
++ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+@@ -353,7 +356,8 @@
+ <0x7ef06f00 0x400>,
+ <0x7ef00280 0x80>,
+ <0x7ef09300 0x100>,
+- <0x7ef20000 0x100>;
++ <0x7ef20000 0x100>,
++ <0x7ef00100 0x30>;
+ reg-names = "hdmi",
+ "dvp",
+ "phy",
+@@ -362,13 +366,15 @@
+ "metadata",
+ "csc",
+ "cec",
+- "hd";
++ "hd",
++ "intr2";
+ ddc = <&ddc1>;
+ clocks = <&firmware_clocks 13>;
+ clock-names = "hdmi";
+ resets = <&dvp 1>;
+ dmas = <&dma 17>;
+ dma-names = "audio-rx";
++ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1581,6 +1581,14 @@ static int vc5_hdmi_init_resources(struc
+ if (IS_ERR(vc4_hdmi->dvp_regs))
+ return PTR_ERR(vc4_hdmi->dvp_regs);
+
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr2");
++ if (!res)
++ return -ENODEV;
++
++ vc4_hdmi->intr2_regs = devm_ioremap(dev, res->start, resource_size(res));
++ if (IS_ERR(vc4_hdmi->intr2_regs))
++ return PTR_ERR(vc4_hdmi->intr2_regs);
++
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
+ if (!res)
+ return -ENODEV;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -140,6 +140,8 @@ struct vc4_hdmi {
+ void __iomem *ram_regs;
+ /* VC5 Only */
+ void __iomem *rm_regs;
++ /* VC5 Only */
++ void __iomem *intr2_regs;
+
+ int hpd_gpio;
+ bool hpd_active_low;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -24,6 +24,7 @@ enum vc4_hdmi_regs {
+ VC5_PHY,
+ VC5_RAM,
+ VC5_RM,
++ VC5_INTR2,
+ };
+
+ enum vc4_hdmi_field {
+@@ -148,6 +149,7 @@ struct vc4_hdmi_register {
+ #define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset)
+ #define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset)
+ #define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset)
++#define VC5_INTR2_REG(reg, offset) _VC4_REG(VC5_INTR2, reg, offset)
+ #define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset)
+ #define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset)
+ #define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset)
+@@ -280,6 +282,12 @@ static const struct vc4_hdmi_register vc
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++ VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000),
++ VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004),
++ VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014),
+
+ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
+ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
+@@ -356,6 +364,12 @@ static const struct vc4_hdmi_register vc
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
+ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++ VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000),
++ VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004),
++ VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010),
++ VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014),
+
+ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
+ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
+@@ -386,6 +400,9 @@ void __iomem *__vc4_hdmi_get_field_base(
+ case VC5_DVP:
+ return hdmi->dvp_regs;
+
++ case VC5_INTR2:
++ return hdmi->intr2_regs;
++
+ case VC5_PHY:
+ return hdmi->phy_regs;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch b/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch
new file mode 100644
index 0000000000..b92d1dbac9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch
@@ -0,0 +1,101 @@
+From 9a9f4303c95f18cc062569c9c5d5240d06ddd69b Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:08 +0100
+Subject: [PATCH] vc4_hdmi_regs: Make interrupt mask variant specific
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 14 ++++++++++----
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ drivers/gpu/drm/vc4/vc4_regs.h | 9 +++++++++
+ 3 files changed, 22 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1285,7 +1285,7 @@ static irqreturn_t vc4_cec_irq_handler(i
+ u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS);
+ u32 cntrl1, cntrl5;
+
+- if (!(stat & VC4_HDMI_CPU_CEC))
++ if (!(stat & vc4_hdmi->variant->cec_mask))
+ return IRQ_NONE;
+ vc4_hdmi->cec_rx_msg.len = 0;
+ cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
+@@ -1301,7 +1301,7 @@ static irqreturn_t vc4_cec_irq_handler(i
+ cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
+ }
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1);
+- HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CPU_CLEAR, vc4_hdmi->variant->cec_mask);
+
+ return IRQ_WAKE_THREAD;
+ }
+@@ -1340,9 +1340,9 @@ static int vc4_hdmi_cec_adap_enable(stru
+ ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) |
+ ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT));
+
+- HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, vc4_hdmi->variant->cec_mask);
+ } else {
+- HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
++ HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, vc4_hdmi->variant->cec_mask);
+ HDMI_WRITE(HDMI_CEC_CNTRL_5, val |
+ VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET);
+ }
+@@ -1784,6 +1784,8 @@ static const struct vc4_hdmi_variant bcm
+ .get_hsm_clock = vc4_hdmi_get_hsm_clock,
+ .calc_hsm_clock = vc4_hdmi_calc_hsm_clock,
+ .channel_map = vc4_hdmi_channel_map,
++
++ .cec_mask = VC4_HDMI_CPU_CEC,
+ };
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = {
+@@ -1809,6 +1811,8 @@ static const struct vc4_hdmi_variant bcm
+ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
+ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock,
+ .channel_map = vc5_hdmi_channel_map,
++
++ .cec_mask = VC5_HDMI0_CPU_CEC_RX | VC5_HDMI0_CPU_CEC_TX,
+ };
+
+ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = {
+@@ -1834,6 +1838,8 @@ static const struct vc4_hdmi_variant bcm
+ .get_hsm_clock = vc5_hdmi_get_hsm_clock,
+ .calc_hsm_clock = vc5_hdmi_calc_hsm_clock,
+ .channel_map = vc5_hdmi_channel_map,
++
++ .cec_mask = VC5_HDMI1_CPU_CEC_RX | VC5_HDMI1_CPU_CEC_TX,
+ };
+
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -97,6 +97,9 @@ struct vc4_hdmi_variant {
+
+ /* Callback to get channel map */
+ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask);
++
++ /* Bitmask for CEC events */
++ u32 cec_mask;
+ };
+
+ /* HDMI audio information */
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -668,6 +668,15 @@
+ # define VC4_HDMI_CPU_CEC BIT(6)
+ # define VC4_HDMI_CPU_HOTPLUG BIT(0)
+
++# define VC5_HDMI0_CPU_CEC_RX BIT(1)
++# define VC5_HDMI0_CPU_CEC_TX BIT(0)
++# define VC5_HDMI0_CPU_HOTPLUG_CONN BIT(4)
++# define VC5_HDMI0_CPU_HOTPLUG_REM BIT(5)
++# define VC5_HDMI1_CPU_CEC_RX BIT(7)
++# define VC5_HDMI1_CPU_CEC_TX BIT(6)
++# define VC5_HDMI1_CPU_HOTPLUG_CONN BIT(10)
++# define VC5_HDMI1_CPU_HOTPLUG_REM BIT(11)
++
+ /* Debug: Current receive value on the CEC pad. */
+ # define VC4_HD_CECRXD BIT(9)
+ /* Debug: Override CEC output to 0. */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch b/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch
new file mode 100644
index 0000000000..dc0fec6c7a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch
@@ -0,0 +1,22 @@
+From 45129a4714234396b4725d8898b14875add1874e Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:08 +0100
+Subject: [PATCH] vc4_hdmi: Make irq shared
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1428,7 +1428,8 @@ static int vc4_hdmi_cec_init(struct vc4_
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+ ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+- vc4_cec_irq_handler_thread, 0,
++ vc4_cec_irq_handler_thread,
++ IRQF_SHARED,
+ "vc4 hdmi cec", vc4_hdmi);
+ if (ret)
+ goto err_delete_cec_adap;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch b/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch
new file mode 100644
index 0000000000..da99772696
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch
@@ -0,0 +1,89 @@
+From 32e84f4f525e2a0d7dc021b5795df34407096b0e Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:08 +0100
+Subject: [PATCH] vc4_hdmi: Adjust CEC ref clock based on its input
+ clock
+
+2711 uses a fixed 27MHz input, earlier models use the HSM clock
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 11 ++++++++---
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +++
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -79,6 +79,7 @@
+ # define VC4_HD_M_ENABLE BIT(0)
+
+ #define CEC_CLOCK_FREQ 40000
++#define VC4_HSM_CLOCK 163682864
+
+ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+ {
+@@ -755,8 +756,7 @@ static u32 vc4_hdmi_calc_hsm_clock(struc
+ * needs to be a bit higher than the pixel clock rate
+ * (generally 148.5Mhz).
+ */
+-
+- return 163682864;
++ return VC4_HSM_CLOCK;
+ }
+
+ static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate)
+@@ -1399,6 +1399,7 @@ static int vc4_hdmi_cec_init(struct vc4_
+ struct cec_connector_info conn_info;
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ u32 value;
++ u32 clk_cnt;
+ int ret;
+
+ if (!vc4_hdmi->variant->cec_available)
+@@ -1423,8 +1424,9 @@ static int vc4_hdmi_cec_init(struct vc4_
+ * divider: the hsm_clock rate and this divider setting will
+ * give a 40 kHz CEC clock.
+ */
++ clk_cnt = vc4_hdmi->variant->cec_input_clock / CEC_CLOCK_FREQ;
+ value |= VC4_HDMI_CEC_ADDR_MASK |
+- (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
++ ((clk_cnt-1) << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
+ HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
+ ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0),
+ vc4_cec_irq_handler,
+@@ -1769,6 +1771,7 @@ static int vc4_hdmi_dev_remove(struct pl
+
+ static const struct vc4_hdmi_variant bcm2835_variant = {
+ .max_pixel_clock = 162000000,
++ .cec_input_clock = VC4_HSM_CLOCK,
+ .audio_available = true,
+ .cec_available = true,
+ .registers = vc4_hdmi_fields,
+@@ -1793,6 +1796,7 @@ static const struct vc4_hdmi_variant bcm
+ .id = 0,
+ .audio_available = true,
+ .max_pixel_clock = 297000000,
++ .cec_input_clock = 27000000,
+ .registers = vc5_hdmi_hdmi0_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields),
+ .phy_lane_mapping = {
+@@ -1820,6 +1824,7 @@ static const struct vc4_hdmi_variant bcm
+ .id = 1,
+ .audio_available = true,
+ .max_pixel_clock = 297000000,
++ .cec_input_clock = 27000000,
+ .registers = vc5_hdmi_hdmi1_fields,
+ .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields),
+ .phy_lane_mapping = {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -48,6 +48,9 @@ struct vc4_hdmi_variant {
+ /* Maximum pixel clock supported by the controller (in Hz) */
+ unsigned long long max_pixel_clock;
+
++ /* Input clock frequency of CEC block (in Hz) */
++ unsigned long cec_input_clock;
++
+ /* List of the registers available on that variant */
+ const struct vc4_hdmi_register *registers;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch b/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch
new file mode 100644
index 0000000000..25d5fd9507
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch
@@ -0,0 +1,44 @@
+From c214fa3d1bc1142cb8f185f71deb3f14915fe55d Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 7 May 2020 18:16:09 +0100
+Subject: [PATCH] vc4_hdmi: Remove cec_available flag as always
+ supported
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ----
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ---
+ 2 files changed, 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1402,9 +1402,6 @@ static int vc4_hdmi_cec_init(struct vc4_
+ u32 clk_cnt;
+ int ret;
+
+- if (!vc4_hdmi->variant->cec_available)
+- return 0;
+-
+ vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
+ vc4_hdmi, "vc4",
+ CEC_CAP_DEFAULTS |
+@@ -1773,7 +1770,6 @@ static const struct vc4_hdmi_variant bcm
+ .max_pixel_clock = 162000000,
+ .cec_input_clock = VC4_HSM_CLOCK,
+ .audio_available = true,
+- .cec_available = true,
+ .registers = vc4_hdmi_fields,
+ .num_registers = ARRAY_SIZE(vc4_hdmi_fields),
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -42,9 +42,6 @@ struct vc4_hdmi_variant {
+ /* Set to true when the audio support is available */
+ bool audio_available;
+
+- /* Set to true when the CEC support is available */
+- bool cec_available;
+-
+ /* Maximum pixel clock supported by the controller (in Hz) */
+ unsigned long long max_pixel_clock;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch b/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch
new file mode 100644
index 0000000000..5bfa6bc60a
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch
@@ -0,0 +1,55 @@
+From adf5f2833517758152cbc9032dd93934a1e16ca1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 May 2020 11:55:45 +0100
+Subject: [PATCH] overlays: tc358743: Use intra-overlay fragments
+
+The tc358743 overlay was written using a workaround to a problem with
+fragments that target other fragments, but this had the unfortunate
+side-effect of preventing the overlay from being applied at runtime
+(the kernel doesn't allow nodes to be overwritten by an overlay, only
+properties).
+
+The current firmware and dtoverlay/dtparam utilities include support
+for these "intra-overlay" fragments, so remove the workaround and do
+it properly.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../boot/dts/overlays/tc358743-overlay.dts | 20 ++++---------------
+ 1 file changed, 4 insertions(+), 16 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
+@@ -54,28 +54,16 @@
+ };
+
+ fragment@2 {
+- target = <&i2c_csi_dsi>;
++ target = <&tc358743>;
+ __overlay__ {
+- tc358743@0f {
+- port {
+- endpoint {
+- data-lanes = <1 2>;
+- };
+- };
+- };
++ data-lanes = <1 2>;
+ };
+ };
+
+ fragment@3 {
+- target = <&i2c_csi_dsi>;
++ target = <&tc358743>;
+ __dormant__ {
+- tc358743@0f {
+- port {
+- endpoint {
+- data-lanes = <1 2 3 4>;
+- };
+- };
+- };
++ data-lanes = <1 2 3 4>;
+ };
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch b/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch
new file mode 100644
index 0000000000..aa19ca11d5
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch
@@ -0,0 +1,326 @@
+From 805e008c18ec09c4115e4cec413642028cd9a8e2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 May 2020 15:12:21 +0100
+Subject: [PATCH] overlays: Move "fixed-clock" nodes to the root
+
+Apart from some special cases, device objects are only created for
+nodes if they are children of a bus or the root node. "fixed-clock"
+is one of the exceptions that will be instantiated wherever it is
+found, but only during kernel initialisation - ruling out loading the
+overlay at runtime.
+
+Move most of the affected clocks to be children of the root, only
+leaving those in overlays that could be multiply instantiated, to avoid
+a potential name clash.
+
+See: https://github.com/raspberrypi/linux/issues/3602
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../dts/overlays/audiosense-pi-overlay.dts | 14 ++++++-------
+ arch/arm/boot/dts/overlays/draws-overlay.dts | 12 +++++------
+ .../boot/dts/overlays/fe-pi-audio-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/imx219-overlay.dts | 12 +++++------
+ .../arm/boot/dts/overlays/irs1125-overlay.dts | 17 +++++++++------
+ .../dts/overlays/mcp2515-can0-overlay.dts | 2 +-
+ .../dts/overlays/mcp2515-can1-overlay.dts | 2 +-
+ .../boot/dts/overlays/midi-uart0-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 17 +++++++++------
+ .../boot/dts/overlays/rpivid-v4l2-overlay.dts | 17 +++++++++------
+ .../dts/overlays/sc16is752-spi1-overlay.dts | 21 ++++++++++++-------
+ .../boot/dts/overlays/tc358743-overlay.dts | 17 +++++++++------
+ 12 files changed, 80 insertions(+), 55 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
+@@ -24,6 +24,13 @@
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
++
++ /* audio external oscillator */
++ codec_osc: codec_osc {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <12000000>; /* 12 MHz */
++ };
+ };
+ };
+
+@@ -44,13 +51,6 @@
+ #size-cells = <0>;
+ status = "okay";
+
+- /* audio external oscillator */
+- codec_osc: codec_osc {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <12000000>; /* 12 MHz */
+- };
+-
+ codec: tlv320aic32x4@18 {
+ #sound-dai-cells = <0>;
+ compatible = "ti,tlv320aic32x4";
+--- a/arch/arm/boot/dts/overlays/draws-overlay.dts
++++ b/arch/arm/boot/dts/overlays/draws-overlay.dts
+@@ -30,6 +30,12 @@
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
++
++ sc16is752_clk: sc16is752_draws_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <1843200>;
++ };
+ };
+
+ pps: pps {
+@@ -78,12 +84,6 @@
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&sc16is752_irq>;
+-
+- sc16is752_clk: sc16is752_clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <1843200>;
+- };
+ };
+
+ tla2024: tla2024@48 {
+--- a/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&clocks>;
++ target-path = "/";
+ __overlay__ {
+ sgtl5000_mclk: sgtl5000_mclk {
+ compatible = "fixed-clock";
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -27,12 +27,6 @@
+ VDIG-supply = <&imx219_vdig>; /* 1.8v */
+ VDDL-supply = <&imx219_vddl>; /* 1.2v */
+
+- imx219_clk: camera-clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <24000000>;
+- };
+-
+ port {
+ imx219_0: endpoint {
+ remote-endpoint = <&csi1_ep>;
+@@ -90,6 +84,12 @@
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
++
++ imx219_clk: camera-clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts
++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts
+@@ -21,12 +21,6 @@
+ pwdn-gpios = <&gpio 5 0>;
+ clocks = <&irs1125_clk>;
+
+- irs1125_clk: camera-clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <26000000>;
+- };
+-
+ port {
+ irs1125_0: endpoint {
+ remote-endpoint = <&csi1_ep>;
+@@ -75,4 +69,15 @@
+ cam0-pwdn = <&irs1125>,"pwdn-gpios:4";
+ };
+ };
++
++ fragment@5 {
++ target-path = "/";
++ __overlay__ {
++ irs1125_clk: camera-clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <26000000>;
++ };
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp2515-can0-overlay.dts
+@@ -35,7 +35,7 @@
+
+ /* the clock/oscillator of the can-controller */
+ fragment@3 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ /* external oscillator of mcp2515 on SPI0.0 */
+ can0_osc: can0_osc {
+--- a/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp2515-can1-overlay.dts
+@@ -35,7 +35,7 @@
+
+ /* the clock/oscillator of the can-controller */
+ fragment@3 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ /* external oscillator of mcp2515 on spi0.1 */
+ can1_osc: can1_osc {
+--- a/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts
++++ b/arch/arm/boot/dts/overlays/midi-uart0-overlay.dts
+@@ -15,7 +15,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target-path = "/clocks";
++ target-path = "/";
+ __overlay__ {
+ midi_clk: midi_clk {
+ compatible = "fixed-clock";
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -21,12 +21,6 @@
+ pwdn-gpios = <&gpio 41 1>, <&gpio 32 1>;
+ clocks = <&ov5647_clk>;
+
+- ov5647_clk: camera-clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <25000000>;
+- };
+-
+ port {
+ ov5647_0: endpoint {
+ remote-endpoint = <&csi1_ep>;
+@@ -77,4 +71,15 @@
+ cam0-led = <&ov5647>,"pwdn-gpios:16";
+ };
+ };
++
++ fragment@5 {
++ target-path = "/";
++ __overlay__ {
++ ov5647_clk: camera-clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <25000000>;
++ };
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+@@ -26,12 +26,6 @@
+
+ clocks = <&hevc_clk>;
+ clock-names = "hevc";
+-
+- hevc_clk: hevc_clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <500000000>;
+- };
+ };
+ };
+ };
+@@ -53,4 +47,15 @@
+ };
+ };
+ };
++
++ fragment@2 {
++ target-path = "/";
++ __overlay__ {
++ hevc_clk: hevc_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <500000000>;
++ };
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is752-spi1-overlay.dts
+@@ -38,12 +38,6 @@
+ #gpio-controller;
+ #gpio-cells = <2>;
+ spi-max-frequency = <4000000>;
+-
+- sc16is752_clk: sc16is752_clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <14745600>;
+- };
+ };
+ };
+ };
+@@ -55,8 +49,19 @@
+ };
+ };
+
+- __overrides__ {
++ fragment@3 {
++ target-path = "/";
++ __overlay__ {
++ sc16is752_clk: sc16is752_spi1_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <14745600>;
++ };
++ };
++ };
++
++ __overrides__ {
+ int_pin = <&sc16is752>,"interrupts:0";
+ xtal = <&sc16is752_clk>,"clock-frequency:0";
+- };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
+@@ -21,12 +21,6 @@
+ clocks = <&tc358743_clk>;
+ clock-names = "refclk";
+
+- tc358743_clk: bridge-clk {
+- compatible = "fixed-clock";
+- #clock-cells = <0>;
+- clock-frequency = <27000000>;
+- };
+-
+ port {
+ tc358743: endpoint {
+ remote-endpoint = <&csi1_ep>;
+@@ -81,6 +75,17 @@
+ };
+ };
+
++ fragment@6 {
++ target-path = "/";
++ __overlay__ {
++ tc358743_clk: bridge-clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <27000000>;
++ };
++ };
++ };
++
+ __overrides__ {
+ 4lane = <0>, "-2+3";
+ link-frequency = <&tc358743>,"link-frequencies#0";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch b/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch
new file mode 100644
index 0000000000..c5c302f832
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch
@@ -0,0 +1,84 @@
+From c2b3b61053c2efd8fb96633c214d9f959c25aea3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 12 May 2020 08:32:42 +0100
+Subject: [PATCH] raspberrypi: dts: Switch to discrete ALSA devices
+
+Add the command line options required to enable audio over discrete
+ALSA devices.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2708-rpi-zero-w.dts | 2 +-
+ arch/arm/boot/dts/bcm2708-rpi-zero.dts | 2 +-
+ arch/arm/boot/dts/bcm270x.dtsi | 2 +-
+ arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts | 2 +-
+ arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 2 +-
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 2 +-
+ 6 files changed, 6 insertions(+), 6 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
+@@ -10,7 +10,7 @@
+ model = "Raspberry Pi Zero W";
+
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts
+@@ -10,7 +10,7 @@
+ model = "Raspberry Pi Zero";
+
+ chosen {
+- bootargs = "coherent_pool=1M";
++ bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ };
+ };
+
+--- a/arch/arm/boot/dts/bcm270x.dtsi
++++ b/arch/arm/boot/dts/bcm270x.dtsi
+@@ -3,7 +3,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M";
++ bootargs = "coherent_pool=1M snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ /delete-property/ stdout-path;
+ };
+
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
+@@ -11,7 +11,7 @@
+ model = "Raspberry Pi 3 Model B+";
+
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
+@@ -11,7 +11,7 @@
+ model = "Raspberry Pi 3 Model B";
+
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -198,7 +198,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 snd_bcm2835.enable_headphones=1";
+ };
+
+ aliases {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch b/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch
new file mode 100644
index 0000000000..eb5430bbae
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch
@@ -0,0 +1,130 @@
+From 87038c8d2337bd2c79bd96ac1ef9e6471a782331 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 7 May 2020 15:50:54 +0100
+Subject: [PATCH] dt-bindings: media: i2c: Add IMX477 CMOS sensor
+ binding
+
+Add YAML device tree binding for IMX477 CMOS image sensor.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../devicetree/bindings/media/i2c/imx477.yaml | 113 ++++++++++++++++++
+ 1 file changed, 113 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/imx477.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/imx477.yaml
+@@ -0,0 +1,113 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/imx477.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony 1/2.3-Inch 12Mpixel CMOS Digital Image Sensor
++
++maintainers:
++ - Naushir Patuck <naush@raspberypi.com>
++
++description: |-
++ The Sony IMX477 is a 1/2.3-inch CMOS active pixel digital image sensor
++ with an active array size of 4056H x 3040V. It is programmable through
++ I2C interface. The I2C address is fixed to 0x1A as per sensor data sheet.
++ Image data is sent through MIPI CSI-2, which is configured as either 2 or
++ 4 data lanes.
++
++properties:
++ compatible:
++ const: sony,imx477
++
++ reg:
++ description: I2C device address
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ VDIG-supply:
++ description:
++ Digital I/O voltage supply, 1.05 volts
++
++ VANA-supply:
++ description:
++ Analog voltage supply, 2.8 volts
++
++ VDDL-supply:
++ description:
++ Digital core voltage supply, 1.8 volts
++
++ reset-gpios:
++ description: |-
++ Reference to the GPIO connected to the xclr pin, if any.
++ Must be released (set high) after all all supplies and INCK are applied.
++
++ # See ../video-interfaces.txt for more details
++ port:
++ type: object
++ properties:
++ endpoint:
++ type: object
++ properties:
++ data-lanes:
++ description: |-
++ The sensor supports either two-lane, or four-lane operation.
++ For two-lane operation the property must be set to <1 2>.
++ items:
++ - const: 1
++ - const: 2
++
++ clock-noncontinuous:
++ type: boolean
++ description: |-
++ MIPI CSI-2 clock is non-continuous if this property is present,
++ otherwise it's continuous.
++
++ link-frequencies:
++ allOf:
++ - $ref: /schemas/types.yaml#/definitions/uint64-array
++ description:
++ Allowed data bus frequencies.
++
++ required:
++ - link-frequencies
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - VANA-supply
++ - VDIG-supply
++ - VDDL-supply
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ i2c0 {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ imx477: sensor@10 {
++ compatible = "sony,imx477";
++ reg = <0x1a>;
++ clocks = <&imx477_clk>;
++ VANA-supply = <&imx477_vana>; /* 2.8v */
++ VDIG-supply = <&imx477_vdig>; /* 1.05v */
++ VDDL-supply = <&imx477_vddl>; /* 1.8v */
++
++ port {
++ imx477_0: endpoint {
++ remote-endpoint = <&csi1_ep>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <450000000>;
++ };
++ };
++ };
++ };
++
++...
diff --git a/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch
new file mode 100644
index 0000000000..0d5ea3c188
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch
@@ -0,0 +1,156 @@
+From 738defbb964c5d2a34b08c24ac0e7fd4afb16173 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 7 May 2020 15:50:04 +0100
+Subject: [PATCH] dtoverlays: Add IMX477 sensor overlay
+
+Add an overlay for the Sony IMX477 CMOS sensor device.
+Also update overlay README and Makefile.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 8 ++
+ arch/arm/boot/dts/overlays/imx477-overlay.dts | 110 ++++++++++++++++++
+ 3 files changed, 119 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/imx477-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -85,6 +85,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ i2s-gpio28-31.dtbo \
+ ilitek251x.dtbo \
+ imx219.dtbo \
++ imx477.dtbo \
+ iqaudio-codec.dtbo \
+ iqaudio-dac.dtbo \
+ iqaudio-dacplus.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1390,6 +1390,14 @@ Load: dtoverlay=imx219
+ Params: <None>
+
+
++Name: imx477
++Info: Sony IMX477 camera module.
++ Uses Unicam 1, which is the standard camera connector on most Pi
++ variants.
++Load: dtoverlay=imx477
++Params: <None>
++
++
+ Name: iqaudio-codec
+ Info: Configures the IQaudio Codec audio card
+ Load: dtoverlay=iqaudio-codec
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/imx477-overlay.dts
+@@ -0,0 +1,110 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for IMX477 camera module on VC I2C bus
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/gpio/gpio.h>
++
++/{
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2c_csi_dsi>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ imx477: imx477@1a {
++ compatible = "sony,imx477";
++ reg = <0x1a>;
++ status = "okay";
++
++ clocks = <&imx477_clk>;
++ clock-names = "xclk";
++
++ VANA-supply = <&imx477_vana>; /* 2.8v */
++ VDIG-supply = <&imx477_vdig>; /* 1.05v */
++ VDDL-supply = <&imx477_vddl>; /* 1.8v */
++
++ port {
++ imx477_0: endpoint {
++ remote-endpoint = <&csi1_ep>;
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies =
++ /bits/ 64 <450000000>;
++ };
++ };
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&csi1>;
++ __overlay__ {
++ status = "okay";
++
++ port {
++ csi1_ep: endpoint {
++ remote-endpoint = <&imx477_0>;
++ };
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&i2c0if>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@3 {
++ target-path="/";
++ __overlay__ {
++ imx477_vana: fixedregulator@0 {
++ compatible = "regulator-fixed";
++ regulator-name = "imx477_vana";
++ regulator-min-microvolt = <2800000>;
++ regulator-max-microvolt = <2800000>;
++ gpio = <&gpio 41 GPIO_ACTIVE_HIGH>;
++ enable-active-high;
++ startup-delay-us = <300000>;
++ };
++ imx477_vdig: fixedregulator@1 {
++ compatible = "regulator-fixed";
++ regulator-name = "imx477_vdig";
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1050000>;
++ };
++ imx477_vddl: fixedregulator@2 {
++ compatible = "regulator-fixed";
++ regulator-name = "imx477_vddl";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ };
++ imx477_clk: camera-clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++ };
++ };
++
++ fragment@4 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@5 {
++ target-path="/__overrides__";
++ __overlay__ {
++ cam0-pwdn-ctrl = <&imx477_vana>,"gpio:0";
++ cam0-pwdn = <&imx477_vana>,"gpio:4";
++ };
++ };
++};
diff --git a/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
new file mode 100644
index 0000000000..2a271cc641
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
@@ -0,0 +1,2266 @@
+From 58483dcbbb5feca6da79970665950b6b43928e60 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 8 May 2020 10:00:12 +0100
+Subject: [PATCH] media: i2c: Add driver for Sony IMX477 sensor
+
+Adds a driver for the 12MPix Sony IMX477 CSI2 sensor.
+Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
+currently only supports 2 lanes.
+
+The following Bayer modes are currently available:
+
+4056x3040 12-bit @ 10fps
+2028x1520 12-bit (binned) @ 40fps
+2028x1050 12-bit (cropped/binned) @ 50fps
+1012x760 10-bit (scaled) @ 120 fps
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ MAINTAINERS | 8 +
+ drivers/media/i2c/Kconfig | 11 +
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/imx477.c | 2191 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 2211 insertions(+)
+ create mode 100644 drivers/media/i2c/imx477.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15195,6 +15195,14 @@ T: git git://linuxtv.org/media_tree.git
+ S: Maintained
+ F: drivers/media/i2c/imx355.c
+
++SONY IMX477 SENSOR DRIVER
++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
++L: linux-media@vger.kernel.org
++T: git git://linuxtv.org/media_tree.git
++S: Maintained
++F: drivers/media/i2c/imx477.c
++F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
++
+ SONY MEMORYSTICK SUBSYSTEM
+ M: Maxim Levitsky <maximlevitsky@gmail.com>
+ M: Alex Dubov <oakad@yahoo.com>
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -609,6 +609,17 @@ config VIDEO_IMX274
+ This is a V4L2 sensor driver for the Sony IMX274
+ CMOS image sensor.
+
++config VIDEO_IMX477
++ tristate "Sony IMX477 sensor support"
++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
++ depends on MEDIA_CAMERA_SUPPORT
++ help
++ This is a Video4Linux2 sensor driver for the Sony
++ IMX477 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called imx477.
++
+ config VIDEO_IMX319
+ tristate "Sony IMX319 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -114,6 +114,7 @@ obj-$(CONFIG_VIDEO_IMX214) += imx214.o
+ obj-$(CONFIG_VIDEO_IMX219) += imx219.o
+ obj-$(CONFIG_VIDEO_IMX258) += imx258.o
+ obj-$(CONFIG_VIDEO_IMX274) += imx274.o
++obj-$(CONFIG_VIDEO_IMX477) += imx477.o
+ obj-$(CONFIG_VIDEO_IMX319) += imx319.o
+ obj-$(CONFIG_VIDEO_IMX355) += imx355.o
+ obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
+--- /dev/null
++++ b/drivers/media/i2c/imx477.c
+@@ -0,0 +1,2191 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX477 cameras.
++ * Copyright (C) 2020, Raspberry Pi (Trading) Ltd
++ *
++ * Based on Sony imx219 camera driver
++ * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd
++ */
++#include <asm/unaligned.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++
++#define IMX477_REG_VALUE_08BIT 1
++#define IMX477_REG_VALUE_16BIT 2
++
++/* Chip ID */
++#define IMX477_REG_CHIP_ID 0x0016
++#define IMX477_CHIP_ID 0x0477
++
++#define IMX477_REG_MODE_SELECT 0x0100
++#define IMX477_MODE_STANDBY 0x00
++#define IMX477_MODE_STREAMING 0x01
++
++#define IMX477_REG_ORIENTATION 0x101
++
++#define IMX477_XCLK_FREQ 24000000
++
++#define IMX477_DEFAULT_LINK_FREQ 450000000
++
++/* Pixel rate is fixed at 840MHz for all the modes */
++#define IMX477_PIXEL_RATE 840000000
++
++/* V_TIMING internal */
++#define IMX477_REG_FRAME_LENGTH 0x0340
++#define IMX477_FRAME_LENGTH_MAX 0xffdc
++
++/* Exposure control */
++#define IMX477_REG_EXPOSURE 0x0202
++#define IMX477_EXPOSURE_OFFSET 22
++#define IMX477_EXPOSURE_MIN 20
++#define IMX477_EXPOSURE_STEP 1
++#define IMX477_EXPOSURE_DEFAULT 0x640
++#define IMX477_EXPOSURE_MAX (IMX477_FRAME_LENGTH_MAX - \
++ IMX477_EXPOSURE_OFFSET)
++
++/* Analog gain control */
++#define IMX477_REG_ANALOG_GAIN 0x0204
++#define IMX477_ANA_GAIN_MIN 0
++#define IMX477_ANA_GAIN_MAX 978
++#define IMX477_ANA_GAIN_STEP 1
++#define IMX477_ANA_GAIN_DEFAULT 0x0
++
++/* Digital gain control */
++#define IMX477_REG_DIGITAL_GAIN 0x020e
++#define IMX477_DGTL_GAIN_MIN 0x0100
++#define IMX477_DGTL_GAIN_MAX 0xffff
++#define IMX477_DGTL_GAIN_DEFAULT 0x0100
++#define IMX477_DGTL_GAIN_STEP 1
++
++/* Test Pattern Control */
++#define IMX477_REG_TEST_PATTERN 0x0600
++#define IMX477_TEST_PATTERN_DISABLE 0
++#define IMX477_TEST_PATTERN_SOLID_COLOR 1
++#define IMX477_TEST_PATTERN_COLOR_BARS 2
++#define IMX477_TEST_PATTERN_GREY_COLOR 3
++#define IMX477_TEST_PATTERN_PN9 4
++
++/* Test pattern colour components */
++#define IMX477_REG_TEST_PATTERN_R 0x0602
++#define IMX477_REG_TEST_PATTERN_GR 0x0604
++#define IMX477_REG_TEST_PATTERN_B 0x0606
++#define IMX477_REG_TEST_PATTERN_GB 0x0608
++#define IMX477_TEST_PATTERN_COLOUR_MIN 0
++#define IMX477_TEST_PATTERN_COLOUR_MAX 0x0fff
++#define IMX477_TEST_PATTERN_COLOUR_STEP 1
++#define IMX477_TEST_PATTERN_R_DEFAULT IMX477_TEST_PATTERN_COLOUR_MAX
++#define IMX477_TEST_PATTERN_GR_DEFAULT 0
++#define IMX477_TEST_PATTERN_B_DEFAULT 0
++#define IMX477_TEST_PATTERN_GB_DEFAULT 0
++
++/* Embedded metadata stream structure */
++#define IMX477_EMBEDDED_LINE_WIDTH 16384
++#define IMX477_NUM_EMBEDDED_LINES 1
++
++enum pad_types {
++ IMAGE_PAD,
++ METADATA_PAD,
++ NUM_PADS
++};
++
++/* IMX477 native and active pixel array size. */
++#define IMX477_NATIVE_WIDTH 4072U
++#define IMX477_NATIVE_HEIGHT 3176U
++#define IMX477_PIXEL_ARRAY_LEFT 8U
++#define IMX477_PIXEL_ARRAY_TOP 16U
++#define IMX477_PIXEL_ARRAY_WIDTH 4056U
++#define IMX477_PIXEL_ARRAY_HEIGHT 3040U
++
++struct imx477_reg {
++ u16 address;
++ u8 val;
++};
++
++struct imx477_reg_list {
++ unsigned int num_of_regs;
++ const struct imx477_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx477_mode {
++ /* Frame width */
++ unsigned int width;
++
++ /* Frame height */
++ unsigned int height;
++
++ /* H-timing in pixels */
++ unsigned int line_length_pix;
++
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
++ /* Highest possible framerate. */
++ struct v4l2_fract timeperframe_min;
++
++ /* Default framerate. */
++ struct v4l2_fract timeperframe_default;
++
++ /* Default register values */
++ struct imx477_reg_list reg_list;
++};
++
++static const struct imx477_reg mode_common_regs[] = {
++ {0x0136, 0x18},
++ {0x0137, 0x00},
++ {0xe000, 0x00},
++ {0xe07a, 0x01},
++ {0x0808, 0x02},
++ {0x4ae9, 0x18},
++ {0x4aea, 0x08},
++ {0xf61c, 0x04},
++ {0xf61e, 0x04},
++ {0x4ae9, 0x21},
++ {0x4aea, 0x80},
++ {0x38a8, 0x1f},
++ {0x38a9, 0xff},
++ {0x38aa, 0x1f},
++ {0x38ab, 0xff},
++ {0x55d4, 0x00},
++ {0x55d5, 0x00},
++ {0x55d6, 0x07},
++ {0x55d7, 0xff},
++ {0x55e8, 0x07},
++ {0x55e9, 0xff},
++ {0x55ea, 0x00},
++ {0x55eb, 0x00},
++ {0x574c, 0x07},
++ {0x574d, 0xff},
++ {0x574e, 0x00},
++ {0x574f, 0x00},
++ {0x5754, 0x00},
++ {0x5755, 0x00},
++ {0x5756, 0x07},
++ {0x5757, 0xff},
++ {0x5973, 0x04},
++ {0x5974, 0x01},
++ {0x5d13, 0xc3},
++ {0x5d14, 0x58},
++ {0x5d15, 0xa3},
++ {0x5d16, 0x1d},
++ {0x5d17, 0x65},
++ {0x5d18, 0x8c},
++ {0x5d1a, 0x06},
++ {0x5d1b, 0xa9},
++ {0x5d1c, 0x45},
++ {0x5d1d, 0x3a},
++ {0x5d1e, 0xab},
++ {0x5d1f, 0x15},
++ {0x5d21, 0x0e},
++ {0x5d22, 0x52},
++ {0x5d23, 0xaa},
++ {0x5d24, 0x7d},
++ {0x5d25, 0x57},
++ {0x5d26, 0xa8},
++ {0x5d37, 0x5a},
++ {0x5d38, 0x5a},
++ {0x5d77, 0x7f},
++ {0x7b75, 0x0e},
++ {0x7b76, 0x0b},
++ {0x7b77, 0x08},
++ {0x7b78, 0x0a},
++ {0x7b79, 0x47},
++ {0x7b7c, 0x00},
++ {0x7b7d, 0x00},
++ {0x8d1f, 0x00},
++ {0x8d27, 0x00},
++ {0x9004, 0x03},
++ {0x9200, 0x50},
++ {0x9201, 0x6c},
++ {0x9202, 0x71},
++ {0x9203, 0x00},
++ {0x9204, 0x71},
++ {0x9205, 0x01},
++ {0x9371, 0x6a},
++ {0x9373, 0x6a},
++ {0x9375, 0x64},
++ {0x991a, 0x00},
++ {0x996b, 0x8c},
++ {0x996c, 0x64},
++ {0x996d, 0x50},
++ {0x9a4c, 0x0d},
++ {0x9a4d, 0x0d},
++ {0xa001, 0x0a},
++ {0xa003, 0x0a},
++ {0xa005, 0x0a},
++ {0xa006, 0x01},
++ {0xa007, 0xc0},
++ {0xa009, 0xc0},
++ {0x3d8a, 0x01},
++ {0x4421, 0x04},
++ {0x7b3b, 0x01},
++ {0x7b4c, 0x00},
++ {0x9905, 0x00},
++ {0x9907, 0x00},
++ {0x9909, 0x00},
++ {0x990b, 0x00},
++ {0x9944, 0x3c},
++ {0x9947, 0x3c},
++ {0x994a, 0x8c},
++ {0x994b, 0x50},
++ {0x994c, 0x1b},
++ {0x994d, 0x8c},
++ {0x994e, 0x50},
++ {0x994f, 0x1b},
++ {0x9950, 0x8c},
++ {0x9951, 0x1b},
++ {0x9952, 0x0a},
++ {0x9953, 0x8c},
++ {0x9954, 0x1b},
++ {0x9955, 0x0a},
++ {0x9a13, 0x04},
++ {0x9a14, 0x04},
++ {0x9a19, 0x00},
++ {0x9a1c, 0x04},
++ {0x9a1d, 0x04},
++ {0x9a26, 0x05},
++ {0x9a27, 0x05},
++ {0x9a2c, 0x01},
++ {0x9a2d, 0x03},
++ {0x9a2f, 0x05},
++ {0x9a30, 0x05},
++ {0x9a41, 0x00},
++ {0x9a46, 0x00},
++ {0x9a47, 0x00},
++ {0x9c17, 0x35},
++ {0x9c1d, 0x31},
++ {0x9c29, 0x50},
++ {0x9c3b, 0x2f},
++ {0x9c41, 0x6b},
++ {0x9c47, 0x2d},
++ {0x9c4d, 0x40},
++ {0x9c6b, 0x00},
++ {0x9c71, 0xc8},
++ {0x9c73, 0x32},
++ {0x9c75, 0x04},
++ {0x9c7d, 0x2d},
++ {0x9c83, 0x40},
++ {0x9c94, 0x3f},
++ {0x9c95, 0x3f},
++ {0x9c96, 0x3f},
++ {0x9c97, 0x00},
++ {0x9c98, 0x00},
++ {0x9c99, 0x00},
++ {0x9c9a, 0x3f},
++ {0x9c9b, 0x3f},
++ {0x9c9c, 0x3f},
++ {0x9ca0, 0x0f},
++ {0x9ca1, 0x0f},
++ {0x9ca2, 0x0f},
++ {0x9ca3, 0x00},
++ {0x9ca4, 0x00},
++ {0x9ca5, 0x00},
++ {0x9ca6, 0x1e},
++ {0x9ca7, 0x1e},
++ {0x9ca8, 0x1e},
++ {0x9ca9, 0x00},
++ {0x9caa, 0x00},
++ {0x9cab, 0x00},
++ {0x9cac, 0x09},
++ {0x9cad, 0x09},
++ {0x9cae, 0x09},
++ {0x9cbd, 0x50},
++ {0x9cbf, 0x50},
++ {0x9cc1, 0x50},
++ {0x9cc3, 0x40},
++ {0x9cc5, 0x40},
++ {0x9cc7, 0x40},
++ {0x9cc9, 0x0a},
++ {0x9ccb, 0x0a},
++ {0x9ccd, 0x0a},
++ {0x9d17, 0x35},
++ {0x9d1d, 0x31},
++ {0x9d29, 0x50},
++ {0x9d3b, 0x2f},
++ {0x9d41, 0x6b},
++ {0x9d47, 0x42},
++ {0x9d4d, 0x5a},
++ {0x9d6b, 0x00},
++ {0x9d71, 0xc8},
++ {0x9d73, 0x32},
++ {0x9d75, 0x04},
++ {0x9d7d, 0x42},
++ {0x9d83, 0x5a},
++ {0x9d94, 0x3f},
++ {0x9d95, 0x3f},
++ {0x9d96, 0x3f},
++ {0x9d97, 0x00},
++ {0x9d98, 0x00},
++ {0x9d99, 0x00},
++ {0x9d9a, 0x3f},
++ {0x9d9b, 0x3f},
++ {0x9d9c, 0x3f},
++ {0x9d9d, 0x1f},
++ {0x9d9e, 0x1f},
++ {0x9d9f, 0x1f},
++ {0x9da0, 0x0f},
++ {0x9da1, 0x0f},
++ {0x9da2, 0x0f},
++ {0x9da3, 0x00},
++ {0x9da4, 0x00},
++ {0x9da5, 0x00},
++ {0x9da6, 0x1e},
++ {0x9da7, 0x1e},
++ {0x9da8, 0x1e},
++ {0x9da9, 0x00},
++ {0x9daa, 0x00},
++ {0x9dab, 0x00},
++ {0x9dac, 0x09},
++ {0x9dad, 0x09},
++ {0x9dae, 0x09},
++ {0x9dc9, 0x0a},
++ {0x9dcb, 0x0a},
++ {0x9dcd, 0x0a},
++ {0x9e17, 0x35},
++ {0x9e1d, 0x31},
++ {0x9e29, 0x50},
++ {0x9e3b, 0x2f},
++ {0x9e41, 0x6b},
++ {0x9e47, 0x2d},
++ {0x9e4d, 0x40},
++ {0x9e6b, 0x00},
++ {0x9e71, 0xc8},
++ {0x9e73, 0x32},
++ {0x9e75, 0x04},
++ {0x9e94, 0x0f},
++ {0x9e95, 0x0f},
++ {0x9e96, 0x0f},
++ {0x9e97, 0x00},
++ {0x9e98, 0x00},
++ {0x9e99, 0x00},
++ {0x9ea0, 0x0f},
++ {0x9ea1, 0x0f},
++ {0x9ea2, 0x0f},
++ {0x9ea3, 0x00},
++ {0x9ea4, 0x00},
++ {0x9ea5, 0x00},
++ {0x9ea6, 0x3f},
++ {0x9ea7, 0x3f},
++ {0x9ea8, 0x3f},
++ {0x9ea9, 0x00},
++ {0x9eaa, 0x00},
++ {0x9eab, 0x00},
++ {0x9eac, 0x09},
++ {0x9ead, 0x09},
++ {0x9eae, 0x09},
++ {0x9ec9, 0x0a},
++ {0x9ecb, 0x0a},
++ {0x9ecd, 0x0a},
++ {0x9f17, 0x35},
++ {0x9f1d, 0x31},
++ {0x9f29, 0x50},
++ {0x9f3b, 0x2f},
++ {0x9f41, 0x6b},
++ {0x9f47, 0x42},
++ {0x9f4d, 0x5a},
++ {0x9f6b, 0x00},
++ {0x9f71, 0xc8},
++ {0x9f73, 0x32},
++ {0x9f75, 0x04},
++ {0x9f94, 0x0f},
++ {0x9f95, 0x0f},
++ {0x9f96, 0x0f},
++ {0x9f97, 0x00},
++ {0x9f98, 0x00},
++ {0x9f99, 0x00},
++ {0x9f9a, 0x2f},
++ {0x9f9b, 0x2f},
++ {0x9f9c, 0x2f},
++ {0x9f9d, 0x00},
++ {0x9f9e, 0x00},
++ {0x9f9f, 0x00},
++ {0x9fa0, 0x0f},
++ {0x9fa1, 0x0f},
++ {0x9fa2, 0x0f},
++ {0x9fa3, 0x00},
++ {0x9fa4, 0x00},
++ {0x9fa5, 0x00},
++ {0x9fa6, 0x1e},
++ {0x9fa7, 0x1e},
++ {0x9fa8, 0x1e},
++ {0x9fa9, 0x00},
++ {0x9faa, 0x00},
++ {0x9fab, 0x00},
++ {0x9fac, 0x09},
++ {0x9fad, 0x09},
++ {0x9fae, 0x09},
++ {0x9fc9, 0x0a},
++ {0x9fcb, 0x0a},
++ {0x9fcd, 0x0a},
++ {0xa14b, 0xff},
++ {0xa151, 0x0c},
++ {0xa153, 0x50},
++ {0xa155, 0x02},
++ {0xa157, 0x00},
++ {0xa1ad, 0xff},
++ {0xa1b3, 0x0c},
++ {0xa1b5, 0x50},
++ {0xa1b9, 0x00},
++ {0xa24b, 0xff},
++ {0xa257, 0x00},
++ {0xa2ad, 0xff},
++ {0xa2b9, 0x00},
++ {0xb21f, 0x04},
++ {0xb35c, 0x00},
++ {0xb35e, 0x08},
++ {0x0112, 0x0c},
++ {0x0113, 0x0c},
++ {0x0114, 0x01},
++ {0x0350, 0x00},
++ {0xbcf1, 0x02},
++ {0x3ff9, 0x01},
++};
++
++/* 12 mpix 10fps */
++static const struct imx477_reg mode_4056x3040_regs[] = {
++ {0x0342, 0x5d},
++ {0x0343, 0xc0},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x0f},
++ {0x0349, 0xd7},
++ {0x034a, 0x0b},
++ {0x034b, 0xdf},
++ {0x00e3, 0x00},
++ {0x00e4, 0x00},
++ {0x00fc, 0x0a},
++ {0x00fd, 0x0a},
++ {0x00fe, 0x0a},
++ {0x00ff, 0x0a},
++ {0x0220, 0x00},
++ {0x0221, 0x11},
++ {0x0381, 0x01},
++ {0x0383, 0x01},
++ {0x0385, 0x01},
++ {0x0387, 0x01},
++ {0x0900, 0x00},
++ {0x0901, 0x11},
++ {0x0902, 0x02},
++ {0x3140, 0x02},
++ {0x3c00, 0x00},
++ {0x3c01, 0x03},
++ {0x3c02, 0xa2},
++ {0x3f0d, 0x01},
++ {0x5748, 0x07},
++ {0x5749, 0xff},
++ {0x574a, 0x00},
++ {0x574b, 0x00},
++ {0x7b75, 0x0a},
++ {0x7b76, 0x0c},
++ {0x7b77, 0x07},
++ {0x7b78, 0x06},
++ {0x7b79, 0x3c},
++ {0x7b53, 0x01},
++ {0x9369, 0x5a},
++ {0x936b, 0x55},
++ {0x936d, 0x28},
++ {0x9304, 0x00},
++ {0x9305, 0x00},
++ {0x9e9a, 0x2f},
++ {0x9e9b, 0x2f},
++ {0x9e9c, 0x2f},
++ {0x9e9d, 0x00},
++ {0x9e9e, 0x00},
++ {0x9e9f, 0x00},
++ {0xa2a9, 0x60},
++ {0xa2b7, 0x00},
++ {0x0401, 0x00},
++ {0x0404, 0x00},
++ {0x0405, 0x10},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x0f},
++ {0x040d, 0xd8},
++ {0x040e, 0x0b},
++ {0x040f, 0xe0},
++ {0x034c, 0x0f},
++ {0x034d, 0xd8},
++ {0x034e, 0x0b},
++ {0x034f, 0xe0},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x04},
++ {0x0306, 0x01},
++ {0x0307, 0x5e},
++ {0x0309, 0x0c},
++ {0x030b, 0x02},
++ {0x030d, 0x02},
++ {0x030e, 0x00},
++ {0x030f, 0x96},
++ {0x0310, 0x01},
++ {0x0820, 0x07},
++ {0x0821, 0x08},
++ {0x0822, 0x00},
++ {0x0823, 0x00},
++ {0x080a, 0x00},
++ {0x080b, 0x7f},
++ {0x080c, 0x00},
++ {0x080d, 0x4f},
++ {0x080e, 0x00},
++ {0x080f, 0x77},
++ {0x0810, 0x00},
++ {0x0811, 0x5f},
++ {0x0812, 0x00},
++ {0x0813, 0x57},
++ {0x0814, 0x00},
++ {0x0815, 0x4f},
++ {0x0816, 0x01},
++ {0x0817, 0x27},
++ {0x0818, 0x00},
++ {0x0819, 0x3f},
++ {0xe04c, 0x00},
++ {0xe04d, 0x7f},
++ {0xe04e, 0x00},
++ {0xe04f, 0x1f},
++ {0x3e20, 0x01},
++ {0x3e37, 0x00},
++ {0x3f50, 0x00},
++ {0x3f56, 0x02},
++ {0x3f57, 0xae},
++};
++
++/* 2x2 binned. 40fps */
++static const struct imx477_reg mode_2028x1520_regs[] = {
++ {0x0342, 0x31},
++ {0x0343, 0xc4},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x0f},
++ {0x0349, 0xd7},
++ {0x034a, 0x0b},
++ {0x034b, 0xdf},
++ {0x0220, 0x00},
++ {0x0221, 0x11},
++ {0x0381, 0x01},
++ {0x0383, 0x01},
++ {0x0385, 0x01},
++ {0x0387, 0x01},
++ {0x0900, 0x01},
++ {0x0901, 0x12},
++ {0x0902, 0x02},
++ {0x3140, 0x02},
++ {0x3c00, 0x00},
++ {0x3c01, 0x03},
++ {0x3c02, 0xa2},
++ {0x3f0d, 0x01},
++ {0x5748, 0x07},
++ {0x5749, 0xff},
++ {0x574a, 0x00},
++ {0x574b, 0x00},
++ {0x7b53, 0x01},
++ {0x9369, 0x73},
++ {0x936b, 0x64},
++ {0x936d, 0x5f},
++ {0x9304, 0x00},
++ {0x9305, 0x00},
++ {0x9e9a, 0x2f},
++ {0x9e9b, 0x2f},
++ {0x9e9c, 0x2f},
++ {0x9e9d, 0x00},
++ {0x9e9e, 0x00},
++ {0x9e9f, 0x00},
++ {0xa2a9, 0x60},
++ {0xa2b7, 0x00},
++ {0x0401, 0x01},
++ {0x0404, 0x00},
++ {0x0405, 0x20},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x0f},
++ {0x040d, 0xd8},
++ {0x040e, 0x0b},
++ {0x040f, 0xe0},
++ {0x034c, 0x07},
++ {0x034d, 0xec},
++ {0x034e, 0x05},
++ {0x034f, 0xf0},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x04},
++ {0x0306, 0x01},
++ {0x0307, 0x5e},
++ {0x0309, 0x0c},
++ {0x030b, 0x02},
++ {0x030d, 0x02},
++ {0x030e, 0x00},
++ {0x030f, 0x96},
++ {0x0310, 0x01},
++ {0x0820, 0x07},
++ {0x0821, 0x08},
++ {0x0822, 0x00},
++ {0x0823, 0x00},
++ {0x080a, 0x00},
++ {0x080b, 0x7f},
++ {0x080c, 0x00},
++ {0x080d, 0x4f},
++ {0x080e, 0x00},
++ {0x080f, 0x77},
++ {0x0810, 0x00},
++ {0x0811, 0x5f},
++ {0x0812, 0x00},
++ {0x0813, 0x57},
++ {0x0814, 0x00},
++ {0x0815, 0x4f},
++ {0x0816, 0x01},
++ {0x0817, 0x27},
++ {0x0818, 0x00},
++ {0x0819, 0x3f},
++ {0xe04c, 0x00},
++ {0xe04d, 0x7f},
++ {0xe04e, 0x00},
++ {0xe04f, 0x1f},
++ {0x3e20, 0x01},
++ {0x3e37, 0x00},
++ {0x3f50, 0x00},
++ {0x3f56, 0x01},
++ {0x3f57, 0x6c},
++};
++
++/* 1080p cropped mode */
++static const struct imx477_reg mode_2028x1080_regs[] = {
++ {0x0342, 0x31},
++ {0x0343, 0xc4},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x01},
++ {0x0347, 0xb8},
++ {0x0348, 0x0f},
++ {0x0349, 0xd7},
++ {0x034a, 0x0a},
++ {0x034b, 0x27},
++ {0x0220, 0x00},
++ {0x0221, 0x11},
++ {0x0381, 0x01},
++ {0x0383, 0x01},
++ {0x0385, 0x01},
++ {0x0387, 0x01},
++ {0x0900, 0x01},
++ {0x0901, 0x12},
++ {0x0902, 0x02},
++ {0x3140, 0x02},
++ {0x3c00, 0x00},
++ {0x3c01, 0x03},
++ {0x3c02, 0xa2},
++ {0x3f0d, 0x01},
++ {0x5748, 0x07},
++ {0x5749, 0xff},
++ {0x574a, 0x00},
++ {0x574b, 0x00},
++ {0x7b53, 0x01},
++ {0x9369, 0x73},
++ {0x936b, 0x64},
++ {0x936d, 0x5f},
++ {0x9304, 0x00},
++ {0x9305, 0x00},
++ {0x9e9a, 0x2f},
++ {0x9e9b, 0x2f},
++ {0x9e9c, 0x2f},
++ {0x9e9d, 0x00},
++ {0x9e9e, 0x00},
++ {0x9e9f, 0x00},
++ {0xa2a9, 0x60},
++ {0xa2b7, 0x00},
++ {0x0401, 0x01},
++ {0x0404, 0x00},
++ {0x0405, 0x20},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x0f},
++ {0x040d, 0xd8},
++ {0x040e, 0x04},
++ {0x040f, 0x38},
++ {0x034c, 0x07},
++ {0x034d, 0xec},
++ {0x034e, 0x04},
++ {0x034f, 0x38},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x04},
++ {0x0306, 0x01},
++ {0x0307, 0x5e},
++ {0x0309, 0x0c},
++ {0x030b, 0x02},
++ {0x030d, 0x02},
++ {0x030e, 0x00},
++ {0x030f, 0x96},
++ {0x0310, 0x01},
++ {0x0820, 0x07},
++ {0x0821, 0x08},
++ {0x0822, 0x00},
++ {0x0823, 0x00},
++ {0x080a, 0x00},
++ {0x080b, 0x7f},
++ {0x080c, 0x00},
++ {0x080d, 0x4f},
++ {0x080e, 0x00},
++ {0x080f, 0x77},
++ {0x0810, 0x00},
++ {0x0811, 0x5f},
++ {0x0812, 0x00},
++ {0x0813, 0x57},
++ {0x0814, 0x00},
++ {0x0815, 0x4f},
++ {0x0816, 0x01},
++ {0x0817, 0x27},
++ {0x0818, 0x00},
++ {0x0819, 0x3f},
++ {0xe04c, 0x00},
++ {0xe04d, 0x7f},
++ {0xe04e, 0x00},
++ {0xe04f, 0x1f},
++ {0x3e20, 0x01},
++ {0x3e37, 0x00},
++ {0x3f50, 0x00},
++ {0x3f56, 0x01},
++ {0x3f57, 0x6c},
++};
++
++/* 4x4 binned. 120fps */
++static const struct imx477_reg mode_1012x760_regs[] = {
++ {0x420b, 0x01},
++ {0x990c, 0x00},
++ {0x990d, 0x08},
++ {0x9956, 0x8c},
++ {0x9957, 0x64},
++ {0x9958, 0x50},
++ {0x9a48, 0x06},
++ {0x9a49, 0x06},
++ {0x9a4a, 0x06},
++ {0x9a4b, 0x06},
++ {0x9a4c, 0x06},
++ {0x9a4d, 0x06},
++ {0x0112, 0x0a},
++ {0x0113, 0x0a},
++ {0x0114, 0x01},
++ {0x0342, 0x14},
++ {0x0343, 0x60},
++ {0x0344, 0x00},
++ {0x0345, 0x00},
++ {0x0346, 0x00},
++ {0x0347, 0x00},
++ {0x0348, 0x0f},
++ {0x0349, 0xd3},
++ {0x034a, 0x0b},
++ {0x034b, 0xdf},
++ {0x00e3, 0x00},
++ {0x00e4, 0x00},
++ {0x00fc, 0x0a},
++ {0x00fd, 0x0a},
++ {0x00fe, 0x0a},
++ {0x00ff, 0x0a},
++ {0x0220, 0x00},
++ {0x0221, 0x11},
++ {0x0381, 0x01},
++ {0x0383, 0x01},
++ {0x0385, 0x01},
++ {0x0387, 0x03},
++ {0x0900, 0x01},
++ {0x0901, 0x22},
++ {0x0902, 0x02},
++ {0x3140, 0x02},
++ {0x3c00, 0x00},
++ {0x3c01, 0x01},
++ {0x3c02, 0x9c},
++ {0x3f0d, 0x00},
++ {0x5748, 0x00},
++ {0x5749, 0x00},
++ {0x574a, 0x00},
++ {0x574b, 0xa4},
++ {0x7b75, 0x0e},
++ {0x7b76, 0x09},
++ {0x7b77, 0x08},
++ {0x7b78, 0x06},
++ {0x7b79, 0x34},
++ {0x7b53, 0x00},
++ {0x9369, 0x73},
++ {0x936b, 0x64},
++ {0x936d, 0x5f},
++ {0x9304, 0x03},
++ {0x9305, 0x80},
++ {0x9e9a, 0x3f},
++ {0x9e9b, 0x3f},
++ {0x9e9c, 0x3f},
++ {0x9e9d, 0x27},
++ {0x9e9e, 0x27},
++ {0x9e9f, 0x27},
++ {0xa2a9, 0x27},
++ {0xa2b7, 0x03},
++ {0x0401, 0x01},
++ {0x0404, 0x00},
++ {0x0405, 0x20},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x07},
++ {0x040d, 0xea},
++ {0x040e, 0x02},
++ {0x040f, 0xf8},
++ {0x034c, 0x03},
++ {0x034d, 0xf4},
++ {0x034e, 0x02},
++ {0x034f, 0xf8},
++ {0x0301, 0x05},
++ {0x0303, 0x02},
++ {0x0305, 0x02},
++ {0x0306, 0x00},
++ {0x0307, 0xaf},
++ {0x0309, 0x0a},
++ {0x030b, 0x02},
++ {0x030d, 0x02},
++ {0x030e, 0x00},
++ {0x030f, 0x96},
++ {0x0310, 0x01},
++ {0x0820, 0x07},
++ {0x0821, 0x08},
++ {0x0822, 0x00},
++ {0x0823, 0x00},
++ {0x080a, 0x00},
++ {0x080b, 0x6f},
++ {0x080c, 0x00},
++ {0x080d, 0x3f},
++ {0x080e, 0x00},
++ {0x080f, 0xff},
++ {0x0810, 0x00},
++ {0x0811, 0x4f},
++ {0x0812, 0x00},
++ {0x0813, 0x47},
++ {0x0814, 0x00},
++ {0x0815, 0x37},
++ {0x0816, 0x00},
++ {0x0817, 0xe7},
++ {0x0818, 0x00},
++ {0x0819, 0x2f},
++ {0xe04c, 0x00},
++ {0xe04d, 0x5f},
++ {0xe04e, 0x00},
++ {0xe04f, 0x1f},
++ {0x3e20, 0x01},
++ {0x3e37, 0x00},
++ {0x3f50, 0x00},
++ {0x3f56, 0x00},
++ {0x3f57, 0x96},
++};
++
++/* Mode configs */
++static const struct imx477_mode supported_modes_12bit[] = {
++ {
++ /* 12MPix 10fps mode */
++ .width = 4056,
++ .height = 3040,
++ .line_length_pix = 0x5dc0,
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 4056,
++ .height = 3040,
++ },
++ .timeperframe_min = {
++ .numerator = 100,
++ .denominator = 1000
++ },
++ .timeperframe_default = {
++ .numerator = 100,
++ .denominator = 1000
++ },
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
++ .regs = mode_4056x3040_regs,
++ },
++ },
++ {
++ /* 2x2 binned 40fps mode */
++ .width = 2028,
++ .height = 1520,
++ .line_length_pix = 0x31c4,
++ .crop = {
++ .left = 0,
++ .top = 0,
++ .width = 4056,
++ .height = 3040,
++ },
++ .timeperframe_min = {
++ .numerator = 100,
++ .denominator = 4000
++ },
++ .timeperframe_default = {
++ .numerator = 100,
++ .denominator = 3000
++ },
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs),
++ .regs = mode_2028x1520_regs,
++ },
++ },
++ {
++ /* 1080p 50fps cropped mode */
++ .width = 2028,
++ .height = 1080,
++ .line_length_pix = 0x31c4,
++ .crop = {
++ .left = 0,
++ .top = 440,
++ .width = 4056,
++ .height = 2600,
++ },
++ .timeperframe_min = {
++ .numerator = 100,
++ .denominator = 5000
++ },
++ .timeperframe_default = {
++ .numerator = 100,
++ .denominator = 3000
++ },
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_2028x1080_regs),
++ .regs = mode_2028x1080_regs,
++ },
++ }
++};
++
++static const struct imx477_mode supported_modes_10bit[] = {
++ {
++ /* 720P 120fps. 4x4 binned */
++ .width = 1012,
++ .height = 760,
++ .line_length_pix = 0x1460,
++ .crop = {
++ /*
++ * FIXME: the analog crop rectangle is actually
++ * programmed with a horizontal displacement of 0
++ * pixels, not 4. It gets shrunk after going through
++ * the scaler. Move this information to the compose
++ * rectangle once the driver is expanded to represent
++ * its processing blocks with multiple subdevs.
++ */
++ .left = 4,
++ .top = 0,
++ .width = 4052,
++ .height = 3040,
++ },
++ .timeperframe_min = {
++ .numerator = 100,
++ .denominator = 12000
++ },
++ .timeperframe_default = {
++ .numerator = 100,
++ .denominator = 60000
++ },
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_1012x760_regs),
++ .regs = mode_1012x760_regs,
++ }
++ }
++};
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++ /* 12-bit modes. */
++ MEDIA_BUS_FMT_SRGGB12_1X12,
++ MEDIA_BUS_FMT_SGRBG12_1X12,
++ MEDIA_BUS_FMT_SGBRG12_1X12,
++ MEDIA_BUS_FMT_SBGGR12_1X12,
++ /* 10-bit modes. */
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++};
++
++static const char * const imx477_test_pattern_menu[] = {
++ "Disabled",
++ "Color Bars",
++ "Solid Color",
++ "Grey Color Bars",
++ "PN9"
++};
++
++static const int imx477_test_pattern_val[] = {
++ IMX477_TEST_PATTERN_DISABLE,
++ IMX477_TEST_PATTERN_COLOR_BARS,
++ IMX477_TEST_PATTERN_SOLID_COLOR,
++ IMX477_TEST_PATTERN_GREY_COLOR,
++ IMX477_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx477_supply_name[] = {
++ /* Supplies can be enabled in any order */
++ "VANA", /* Analog (2.8V) supply */
++ "VDIG", /* Digital Core (1.05V) supply */
++ "VDDL", /* IF (1.8V) supply */
++};
++
++#define IMX477_NUM_SUPPLIES ARRAY_SIZE(imx477_supply_name)
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software standby), given by T7 in the
++ * datasheet is 8ms. This does include I2C setup time as well.
++ *
++ * Note, that delay between XCLR low->high and reading the CCI ID register (T6
++ * in the datasheet) is much smaller - 600us.
++ */
++#define IMX477_XCLR_MIN_DELAY_US 8000
++#define IMX477_XCLR_DELAY_RANGE_US 1000
++
++struct imx477 {
++ struct v4l2_subdev sd;
++ struct media_pad pad[NUM_PADS];
++
++ struct v4l2_mbus_framefmt fmt;
++
++ struct clk *xclk;
++ u32 xclk_freq;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[IMX477_NUM_SUPPLIES];
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ /* V4L2 Controls */
++ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++
++ /* Current mode */
++ const struct imx477_mode *mode;
++
++ /*
++ * Mutex for serialized access:
++ * Protect sensor module set pad format and start/stop streaming safely.
++ */
++ struct mutex mutex;
++
++ /* Streaming on/off */
++ bool streaming;
++
++ /* Rewrite common registers on stream on? */
++ bool common_regs_written;
++};
++
++static inline struct imx477 *to_imx477(struct v4l2_subdev *_sd)
++{
++ return container_of(_sd, struct imx477, sd);
++}
++
++static inline void get_mode_table(unsigned int code,
++ const struct imx477_mode **mode_list,
++ unsigned int *num_modes)
++{
++ switch (code) {
++ /* 12-bit */
++ case MEDIA_BUS_FMT_SRGGB12_1X12:
++ case MEDIA_BUS_FMT_SGRBG12_1X12:
++ case MEDIA_BUS_FMT_SGBRG12_1X12:
++ case MEDIA_BUS_FMT_SBGGR12_1X12:
++ *mode_list = supported_modes_12bit;
++ *num_modes = ARRAY_SIZE(supported_modes_12bit);
++ break;
++ /* 10-bit */
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ *mode_list = supported_modes_10bit;
++ *num_modes = ARRAY_SIZE(supported_modes_10bit);
++ break;
++ default:
++ *mode_list = NULL;
++ *num_modes = 0;
++ }
++}
++
++/* Read registers up to 2 at a time */
++static int imx477_read_reg(struct imx477 *imx477, u16 reg, u32 len, u32 *val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ struct i2c_msg msgs[2];
++ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++ u8 data_buf[4] = { 0, };
++ int ret;
++
++ if (len > 4)
++ return -EINVAL;
++
++ /* Write register address */
++ msgs[0].addr = client->addr;
++ msgs[0].flags = 0;
++ msgs[0].len = ARRAY_SIZE(addr_buf);
++ msgs[0].buf = addr_buf;
++
++ /* Read data from register */
++ msgs[1].addr = client->addr;
++ msgs[1].flags = I2C_M_RD;
++ msgs[1].len = len;
++ msgs[1].buf = &data_buf[4 - len];
++
++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++ if (ret != ARRAY_SIZE(msgs))
++ return -EIO;
++
++ *val = get_unaligned_be32(data_buf);
++
++ return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx477_write_reg(struct imx477 *imx477, u16 reg, u32 len, u32 val)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ u8 buf[6];
++
++ if (len > 4)
++ return -EINVAL;
++
++ put_unaligned_be16(reg, buf);
++ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++ if (i2c_master_send(client, buf, len + 2) != len + 2)
++ return -EIO;
++
++ return 0;
++}
++
++/* Write a list of registers */
++static int imx477_write_regs(struct imx477 *imx477,
++ const struct imx477_reg *regs, u32 len)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < len; i++) {
++ ret = imx477_write_reg(imx477, regs[i].address, 1, regs[i].val);
++ if (ret) {
++ dev_err_ratelimited(&client->dev,
++ "Failed to write reg 0x%4.4x. error = %d\n",
++ regs[i].address, ret);
++
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx477_get_format_code(struct imx477 *imx477, u32 code)
++{
++ unsigned int i;
++
++ lockdep_assert_held(&imx477->mutex);
++
++ for (i = 0; i < ARRAY_SIZE(codes); i++)
++ if (codes[i] == code)
++ break;
++
++ if (i >= ARRAY_SIZE(codes))
++ i = 0;
++
++ i = (i & ~3) | (imx477->vflip->val ? 2 : 0) |
++ (imx477->hflip->val ? 1 : 0);
++
++ return codes[i];
++}
++
++static void imx477_set_default_format(struct imx477 *imx477)
++{
++ struct v4l2_mbus_framefmt *fmt = &imx477->fmt;
++
++ /* Set default mode to max resolution */
++ imx477->mode = &supported_modes_12bit[0];
++
++ fmt->code = MEDIA_BUS_FMT_SRGGB12_1X12;
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++ fmt->width = imx477->mode->width;
++ fmt->height = imx477->mode->height;
++ fmt->field = V4L2_FIELD_NONE;
++}
++
++static int imx477_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ struct imx477 *imx477 = to_imx477(sd);
++ struct v4l2_mbus_framefmt *try_fmt_img =
++ v4l2_subdev_get_try_format(sd, fh->pad, IMAGE_PAD);
++ struct v4l2_mbus_framefmt *try_fmt_meta =
++ v4l2_subdev_get_try_format(sd, fh->pad, METADATA_PAD);
++ struct v4l2_rect *try_crop;
++
++ mutex_lock(&imx477->mutex);
++
++ /* Initialize try_fmt for the image pad */
++ try_fmt_img->width = supported_modes_12bit[0].width;
++ try_fmt_img->height = supported_modes_12bit[0].height;
++ try_fmt_img->code = imx477_get_format_code(imx477,
++ MEDIA_BUS_FMT_SRGGB12_1X12);
++ try_fmt_img->field = V4L2_FIELD_NONE;
++
++ /* Initialize try_fmt for the embedded metadata pad */
++ try_fmt_meta->width = IMX477_EMBEDDED_LINE_WIDTH;
++ try_fmt_meta->height = IMX477_NUM_EMBEDDED_LINES;
++ try_fmt_meta->code = MEDIA_BUS_FMT_SENSOR_DATA;
++ try_fmt_meta->field = V4L2_FIELD_NONE;
++
++ /* Initialize try_crop */
++ try_crop = v4l2_subdev_get_try_crop(sd, fh->pad, IMAGE_PAD);
++ try_crop->left = IMX477_PIXEL_ARRAY_LEFT;
++ try_crop->top = IMX477_PIXEL_ARRAY_TOP;
++ try_crop->width = IMX477_PIXEL_ARRAY_WIDTH;
++ try_crop->height = IMX477_PIXEL_ARRAY_HEIGHT;
++
++ mutex_unlock(&imx477->mutex);
++
++ return 0;
++}
++
++static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct imx477 *imx477 =
++ container_of(ctrl->handler, struct imx477, ctrl_handler);
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ int ret = 0;
++
++ if (ctrl->id == V4L2_CID_VBLANK) {
++ int exposure_max, exposure_def;
++
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = imx477->mode->height + ctrl->val -
++ IMX477_EXPOSURE_OFFSET;
++ exposure_def = min(exposure_max, imx477->exposure->val);
++ __v4l2_ctrl_modify_range(imx477->exposure,
++ imx477->exposure->minimum,
++ exposure_max, imx477->exposure->step,
++ exposure_def);
++ }
++
++ /*
++ * Applying V4L2 control value only happens
++ * when power is up for streaming
++ */
++ if (pm_runtime_get_if_in_use(&client->dev) == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = imx477_write_reg(imx477, IMX477_REG_ANALOG_GAIN,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_DIGITAL_GAIN:
++ ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN,
++ IMX477_REG_VALUE_16BIT,
++ imx477_test_pattern_val[ctrl->val]);
++ break;
++ case V4L2_CID_TEST_PATTERN_RED:
++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_R,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENR:
++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GR,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_BLUE:
++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_B,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_TEST_PATTERN_GREENB:
++ ret = imx477_write_reg(imx477, IMX477_REG_TEST_PATTERN_GB,
++ IMX477_REG_VALUE_16BIT, ctrl->val);
++ break;
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ ret = imx477_write_reg(imx477, IMX477_REG_ORIENTATION, 1,
++ imx477->hflip->val |
++ imx477->vflip->val << 1);
++ break;
++ case V4L2_CID_VBLANK:
++ ret = imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
++ IMX477_REG_VALUE_16BIT,
++ imx477->mode->height + ctrl->val);
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n",
++ ctrl->id, ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_put(&client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops imx477_ctrl_ops = {
++ .s_ctrl = imx477_set_ctrl,
++};
++
++static int imx477_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct imx477 *imx477 = to_imx477(sd);
++
++ if (code->pad >= NUM_PADS)
++ return -EINVAL;
++
++ if (code->pad == IMAGE_PAD) {
++ if (code->index >= (ARRAY_SIZE(codes) / 4))
++ return -EINVAL;
++
++ code->code = imx477_get_format_code(imx477,
++ codes[code->index * 4]);
++ } else {
++ if (code->index > 0)
++ return -EINVAL;
++
++ code->code = MEDIA_BUS_FMT_SENSOR_DATA;
++ }
++
++ return 0;
++}
++
++static int imx477_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct imx477 *imx477 = to_imx477(sd);
++
++ if (fse->pad >= NUM_PADS)
++ return -EINVAL;
++
++ if (fse->pad == IMAGE_PAD) {
++ const struct imx477_mode *mode_list;
++ unsigned int num_modes;
++
++ get_mode_table(fse->code, &mode_list, &num_modes);
++
++ if (fse->index >= num_modes)
++ return -EINVAL;
++
++ if (fse->code != imx477_get_format_code(imx477, fse->code))
++ return -EINVAL;
++
++ fse->min_width = mode_list[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = mode_list[fse->index].height;
++ fse->max_height = fse->min_height;
++ } else {
++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0)
++ return -EINVAL;
++
++ fse->min_width = IMX477_EMBEDDED_LINE_WIDTH;
++ fse->max_width = fse->min_width;
++ fse->min_height = IMX477_NUM_EMBEDDED_LINES;
++ fse->max_height = fse->min_height;
++ }
++
++ return 0;
++}
++
++static void imx477_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++ fmt->colorspace = V4L2_COLORSPACE_SRGB;
++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++ fmt->colorspace,
++ fmt->ycbcr_enc);
++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx477_update_image_pad_format(struct imx477 *imx477,
++ const struct imx477_mode *mode,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = mode->width;
++ fmt->format.height = mode->height;
++ fmt->format.field = V4L2_FIELD_NONE;
++ imx477_reset_colorspace(&fmt->format);
++}
++
++static void imx477_update_metadata_pad_format(struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = IMX477_EMBEDDED_LINE_WIDTH;
++ fmt->format.height = IMX477_NUM_EMBEDDED_LINES;
++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA;
++ fmt->format.field = V4L2_FIELD_NONE;
++}
++
++static int imx477_get_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx477 *imx477 = to_imx477(sd);
++
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
++
++ mutex_lock(&imx477->mutex);
++
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ struct v4l2_mbus_framefmt *try_fmt =
++ v4l2_subdev_get_try_format(&imx477->sd, cfg, fmt->pad);
++ /* update the code which could change due to vflip or hflip: */
++ try_fmt->code = fmt->pad == IMAGE_PAD ?
++ imx477_get_format_code(imx477, try_fmt->code) :
++ MEDIA_BUS_FMT_SENSOR_DATA;
++ fmt->format = *try_fmt;
++ } else {
++ if (fmt->pad == IMAGE_PAD) {
++ imx477_update_image_pad_format(imx477, imx477->mode,
++ fmt);
++ fmt->format.code =
++ imx477_get_format_code(imx477, imx477->fmt.code);
++ } else {
++ imx477_update_metadata_pad_format(fmt);
++ }
++ }
++
++ mutex_unlock(&imx477->mutex);
++ return 0;
++}
++
++static
++unsigned int imx477_get_frame_length(const struct imx477_mode *mode,
++ const struct v4l2_fract *timeperframe)
++{
++ u64 frame_length;
++
++ frame_length = (u64)timeperframe->numerator * IMX477_PIXEL_RATE;
++ do_div(frame_length,
++ (u64)timeperframe->denominator * mode->line_length_pix);
++
++ if (WARN_ON(frame_length > IMX477_FRAME_LENGTH_MAX))
++ frame_length = IMX477_FRAME_LENGTH_MAX;
++
++ return max_t(unsigned int, frame_length, mode->height);
++}
++
++static void imx477_set_framing_limits(struct imx477 *imx477)
++{
++ const struct imx477_mode *mode = imx477->mode;
++ unsigned int frm_length_min, frm_length_default;
++ unsigned int exposure_max, exposure_def, hblank;
++
++ frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min);
++ frm_length_default =
++ imx477_get_frame_length(mode, &mode->timeperframe_default);
++
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
++ IMX477_FRAME_LENGTH_MAX - mode->height,
++ 1, frm_length_default - mode->height);
++ __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height);
++
++ /* Update max exposure while meeting expected vblanking */
++ exposure_max = IMX477_FRAME_LENGTH_MAX - IMX477_EXPOSURE_OFFSET;
++ exposure_def = frm_length_default - mode->height -
++ IMX477_EXPOSURE_OFFSET;
++ __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
++ exposure_max, imx477->exposure->step,
++ exposure_def);
++ /*
++ * Currently PPL is fixed to the mode specified value, so hblank
++ * depends on mode->width only, and is not changeable in any
++ * way other than changing the mode.
++ */
++ hblank = mode->line_length_pix - mode->width;
++ __v4l2_ctrl_modify_range(imx477->hblank, hblank, hblank, 1, hblank);
++}
++
++static int imx477_set_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_format *fmt)
++{
++ struct v4l2_mbus_framefmt *framefmt;
++ const struct imx477_mode *mode;
++ struct imx477 *imx477 = to_imx477(sd);
++
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
++
++ mutex_lock(&imx477->mutex);
++
++ if (fmt->pad == IMAGE_PAD) {
++ const struct imx477_mode *mode_list;
++ unsigned int num_modes;
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx477_get_format_code(imx477,
++ fmt->format.code);
++
++ get_mode_table(fmt->format.code, &mode_list, &num_modes);
++
++ mode = v4l2_find_nearest_size(mode_list,
++ num_modes,
++ width, height,
++ fmt->format.width,
++ fmt->format.height);
++ imx477_update_image_pad_format(imx477, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else {
++ imx477->mode = mode;
++ imx477_set_framing_limits(imx477);
++ }
++ } else {
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, cfg,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else {
++ /* Only one embedded data mode is supported */
++ imx477_update_metadata_pad_format(fmt);
++ }
++ }
++
++ mutex_unlock(&imx477->mutex);
++
++ return 0;
++}
++
++static const struct v4l2_rect *
++__imx477_get_pad_crop(struct imx477 *imx477, struct v4l2_subdev_pad_config *cfg,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&imx477->sd, cfg, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &imx477->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int imx477_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_pad_config *cfg,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct imx477 *imx477 = to_imx477(sd);
++
++ mutex_lock(&imx477->mutex);
++ sel->r = *__imx477_get_pad_crop(imx477, cfg, sel->pad,
++ sel->which);
++ mutex_unlock(&imx477->mutex);
++
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.left = 0;
++ sel->r.top = 0;
++ sel->r.width = IMX477_NATIVE_WIDTH;
++ sel->r.height = IMX477_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ sel->r.left = IMX477_PIXEL_ARRAY_LEFT;
++ sel->r.top = IMX477_PIXEL_ARRAY_TOP;
++ sel->r.width = IMX477_PIXEL_ARRAY_WIDTH;
++ sel->r.height = IMX477_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++/* Start streaming */
++static int imx477_start_streaming(struct imx477 *imx477)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ const struct imx477_reg_list *reg_list;
++ int ret;
++
++ if (!imx477->common_regs_written) {
++ ret = imx477_write_regs(imx477, mode_common_regs,
++ ARRAY_SIZE(mode_common_regs));
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set common settings\n",
++ __func__);
++ return ret;
++ }
++ imx477->common_regs_written = true;
++ }
++
++ /* Apply default values of current mode */
++ reg_list = &imx477->mode->reg_list;
++ ret = imx477_write_regs(imx477, reg_list->regs, reg_list->num_of_regs);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set mode\n", __func__);
++ return ret;
++ }
++
++ /* Apply customized values from user */
++ ret = __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler);
++ if (ret)
++ return ret;
++
++ /* set stream on register */
++ return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
++ IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING);
++}
++
++/* Stop streaming */
++static void imx477_stop_streaming(struct imx477 *imx477)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ int ret;
++
++ /* set stream off register */
++ ret = imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
++ IMX477_REG_VALUE_08BIT, IMX477_MODE_STANDBY);
++ if (ret)
++ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++}
++
++static int imx477_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct imx477 *imx477 = to_imx477(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = 0;
++
++ mutex_lock(&imx477->mutex);
++ if (imx477->streaming == enable) {
++ mutex_unlock(&imx477->mutex);
++ return 0;
++ }
++
++ if (enable) {
++ ret = pm_runtime_get_sync(&client->dev);
++ if (ret < 0) {
++ pm_runtime_put_noidle(&client->dev);
++ goto err_unlock;
++ }
++
++ /*
++ * Apply default & customized values
++ * and then start streaming.
++ */
++ ret = imx477_start_streaming(imx477);
++ if (ret)
++ goto err_rpm_put;
++ } else {
++ imx477_stop_streaming(imx477);
++ pm_runtime_put(&client->dev);
++ }
++
++ imx477->streaming = enable;
++
++ /* vflip and hflip cannot change during streaming */
++ __v4l2_ctrl_grab(imx477->vflip, enable);
++ __v4l2_ctrl_grab(imx477->hflip, enable);
++
++ mutex_unlock(&imx477->mutex);
++
++ return ret;
++
++err_rpm_put:
++ pm_runtime_put(&client->dev);
++err_unlock:
++ mutex_unlock(&imx477->mutex);
++
++ return ret;
++}
++
++/* Power/clock management functions */
++static int imx477_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx477 *imx477 = to_imx477(sd);
++ int ret;
++
++ ret = regulator_bulk_enable(IMX477_NUM_SUPPLIES,
++ imx477->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ return ret;
++ }
++
++ ret = clk_prepare_enable(imx477->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n",
++ __func__);
++ goto reg_off;
++ }
++
++ gpiod_set_value_cansleep(imx477->reset_gpio, 1);
++ usleep_range(IMX477_XCLR_MIN_DELAY_US,
++ IMX477_XCLR_MIN_DELAY_US + IMX477_XCLR_DELAY_RANGE_US);
++
++ return 0;
++
++reg_off:
++ regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies);
++ return ret;
++}
++
++static int imx477_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx477 *imx477 = to_imx477(sd);
++
++ gpiod_set_value_cansleep(imx477->reset_gpio, 0);
++ regulator_bulk_disable(IMX477_NUM_SUPPLIES, imx477->supplies);
++ clk_disable_unprepare(imx477->xclk);
++
++ /* Force reprogramming of the common registers when powered up again. */
++ imx477->common_regs_written = false;
++
++ return 0;
++}
++
++static int __maybe_unused imx477_suspend(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx477 *imx477 = to_imx477(sd);
++
++ if (imx477->streaming)
++ imx477_stop_streaming(imx477);
++
++ return 0;
++}
++
++static int __maybe_unused imx477_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx477 *imx477 = to_imx477(sd);
++ int ret;
++
++ if (imx477->streaming) {
++ ret = imx477_start_streaming(imx477);
++ if (ret)
++ goto error;
++ }
++
++ return 0;
++
++error:
++ imx477_stop_streaming(imx477);
++ imx477->streaming = 0;
++ return ret;
++}
++
++static int imx477_get_regulators(struct imx477 *imx477)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ unsigned int i;
++
++ for (i = 0; i < IMX477_NUM_SUPPLIES; i++)
++ imx477->supplies[i].supply = imx477_supply_name[i];
++
++ return devm_regulator_bulk_get(&client->dev,
++ IMX477_NUM_SUPPLIES,
++ imx477->supplies);
++}
++
++/* Verify chip ID */
++static int imx477_identify_module(struct imx477 *imx477)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ int ret;
++ u32 val;
++
++ ret = imx477_read_reg(imx477, IMX477_REG_CHIP_ID,
++ IMX477_REG_VALUE_16BIT, &val);
++ if (ret) {
++ dev_err(&client->dev, "failed to read chip id %x, with error %d\n",
++ IMX477_CHIP_ID, ret);
++ return ret;
++ }
++
++ if (val != IMX477_CHIP_ID) {
++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++ IMX477_CHIP_ID, val);
++ ret = -EINVAL;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx477_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx477_video_ops = {
++ .s_stream = imx477_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx477_pad_ops = {
++ .enum_mbus_code = imx477_enum_mbus_code,
++ .get_fmt = imx477_get_pad_format,
++ .set_fmt = imx477_set_pad_format,
++ .get_selection = imx477_get_selection,
++ .enum_frame_size = imx477_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx477_subdev_ops = {
++ .core = &imx477_core_ops,
++ .video = &imx477_video_ops,
++ .pad = &imx477_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx477_internal_ops = {
++ .open = imx477_open,
++};
++
++/* Initialize control handlers */
++static int imx477_init_controls(struct imx477 *imx477)
++{
++ struct v4l2_ctrl_handler *ctrl_hdlr;
++ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
++ unsigned int i;
++ int ret;
++
++ ctrl_hdlr = &imx477->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 14);
++ if (ret)
++ return ret;
++
++ mutex_init(&imx477->mutex);
++ ctrl_hdlr->lock = &imx477->mutex;
++
++ /* By default, PIXEL_RATE is read only */
++ imx477->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_PIXEL_RATE,
++ IMX477_PIXEL_RATE,
++ IMX477_PIXEL_RATE, 1,
++ IMX477_PIXEL_RATE);
++
++ /*
++ * Create the controls here, but mode specific limits are setup
++ * in the imx477_set_framing_limits() call below.
++ */
++ imx477->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
++ imx477->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
++
++ /* HBLANK is read-only for now, but does change with mode. */
++ if (imx477->hblank)
++ imx477->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++ imx477->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ IMX477_EXPOSURE_MIN,
++ IMX477_EXPOSURE_MAX,
++ IMX477_EXPOSURE_STEP,
++ IMX477_EXPOSURE_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ IMX477_ANA_GAIN_MIN, IMX477_ANA_GAIN_MAX,
++ IMX477_ANA_GAIN_STEP, IMX477_ANA_GAIN_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++ IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX,
++ IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT);
++
++ imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ if (imx477->hflip)
++ imx477->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ imx477->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++ if (imx477->vflip)
++ imx477->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(imx477_test_pattern_menu) - 1,
++ 0, 0, imx477_test_pattern_menu);
++ for (i = 0; i < 4; i++) {
++ /*
++ * The assumption is that
++ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2
++ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++ */
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_TEST_PATTERN_RED + i,
++ IMX477_TEST_PATTERN_COLOUR_MIN,
++ IMX477_TEST_PATTERN_COLOUR_MAX,
++ IMX477_TEST_PATTERN_COLOUR_STEP,
++ IMX477_TEST_PATTERN_COLOUR_MAX);
++ /* The "Solid color" pattern is white by default */
++ }
++
++ if (ctrl_hdlr->error) {
++ ret = ctrl_hdlr->error;
++ dev_err(&client->dev, "%s control init failed (%d)\n",
++ __func__, ret);
++ goto error;
++ }
++
++ imx477->sd.ctrl_handler = ctrl_hdlr;
++
++ /* Setup exposure and frame/line length limits. */
++ imx477_set_framing_limits(imx477);
++
++ return 0;
++
++error:
++ v4l2_ctrl_handler_free(ctrl_hdlr);
++ mutex_destroy(&imx477->mutex);
++
++ return ret;
++}
++
++static void imx477_free_controls(struct imx477 *imx477)
++{
++ v4l2_ctrl_handler_free(imx477->sd.ctrl_handler);
++ mutex_destroy(&imx477->mutex);
++}
++
++static int imx477_check_hwcfg(struct device *dev)
++{
++ struct fwnode_handle *endpoint;
++ struct v4l2_fwnode_endpoint ep_cfg = {
++ .bus_type = V4L2_MBUS_CSI2_DPHY
++ };
++ int ret = -EINVAL;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++ dev_err(dev, "could not parse endpoint\n");
++ goto error_out;
++ }
++
++ /* Check the number of MIPI CSI2 data lanes */
++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(dev, "only 2 data lanes are currently supported\n");
++ goto error_out;
++ }
++
++ /* Check the link frequency set in device tree */
++ if (!ep_cfg.nr_of_link_frequencies) {
++ dev_err(dev, "link-frequency property not found in DT\n");
++ goto error_out;
++ }
++
++ if (ep_cfg.nr_of_link_frequencies != 1 ||
++ ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) {
++ dev_err(dev, "Link frequency not supported: %lld\n",
++ ep_cfg.link_frequencies[0]);
++ goto error_out;
++ }
++
++ ret = 0;
++
++error_out:
++ v4l2_fwnode_endpoint_free(&ep_cfg);
++ fwnode_handle_put(endpoint);
++
++ return ret;
++}
++
++static int imx477_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct imx477 *imx477;
++ int ret;
++
++ imx477 = devm_kzalloc(&client->dev, sizeof(*imx477), GFP_KERNEL);
++ if (!imx477)
++ return -ENOMEM;
++
++ v4l2_i2c_subdev_init(&imx477->sd, client, &imx477_subdev_ops);
++
++ /* Check the hardware configuration in device tree */
++ if (imx477_check_hwcfg(dev))
++ return -EINVAL;
++
++ /* Get system clock (xclk) */
++ imx477->xclk = devm_clk_get(dev, NULL);
++ if (IS_ERR(imx477->xclk)) {
++ dev_err(dev, "failed to get xclk\n");
++ return PTR_ERR(imx477->xclk);
++ }
++
++ imx477->xclk_freq = clk_get_rate(imx477->xclk);
++ if (imx477->xclk_freq != IMX477_XCLK_FREQ) {
++ dev_err(dev, "xclk frequency not supported: %d Hz\n",
++ imx477->xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = imx477_get_regulators(imx477);
++ if (ret) {
++ dev_err(dev, "failed to get regulators\n");
++ return ret;
++ }
++
++ /* Request optional enable pin */
++ imx477->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++ GPIOD_OUT_HIGH);
++
++ /*
++ * The sensor must be powered for imx477_identify_module()
++ * to be able to read the CHIP_ID register
++ */
++ ret = imx477_power_on(dev);
++ if (ret)
++ return ret;
++
++ ret = imx477_identify_module(imx477);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize default format */
++ imx477_set_default_format(imx477);
++
++ /* Enable runtime PM and turn off the device */
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_idle(dev);
++
++ /* This needs the pm runtime to be registered. */
++ ret = imx477_init_controls(imx477);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize subdev */
++ imx477->sd.internal_ops = &imx477_internal_ops;
++ imx477->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ imx477->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ /* Initialize source pads */
++ imx477->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
++ imx477->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&imx477->sd.entity, NUM_PADS, imx477->pad);
++ if (ret) {
++ dev_err(dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ret = v4l2_async_register_subdev_sensor_common(&imx477->sd);
++ if (ret < 0) {
++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ return 0;
++
++error_media_entity:
++ media_entity_cleanup(&imx477->sd.entity);
++
++error_handler_free:
++ imx477_free_controls(imx477);
++
++error_power_off:
++ pm_runtime_disable(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++ imx477_power_off(&client->dev);
++
++ return ret;
++}
++
++static int imx477_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx477 *imx477 = to_imx477(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ imx477_free_controls(imx477);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ imx477_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++
++ return 0;
++}
++
++static const struct of_device_id imx477_dt_ids[] = {
++ { .compatible = "sony,imx477" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx477_dt_ids);
++
++static const struct dev_pm_ops imx477_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(imx477_suspend, imx477_resume)
++ SET_RUNTIME_PM_OPS(imx477_power_off, imx477_power_on, NULL)
++};
++
++static struct i2c_driver imx477_i2c_driver = {
++ .driver = {
++ .name = "imx477",
++ .of_match_table = imx477_dt_ids,
++ .pm = &imx477_pm_ops,
++ },
++ .probe_new = imx477_probe,
++ .remove = imx477_remove,
++};
++
++module_i2c_driver(imx477_i2c_driver);
++
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
++MODULE_DESCRIPTION("Sony IMX477 sensor driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch b/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch
new file mode 100644
index 0000000000..1da5dea326
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch
@@ -0,0 +1,182 @@
+From 69f1022fc3c155f8cd5cf7dacaf283b1778835f3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 8 May 2020 09:41:17 +0100
+Subject: [PATCH] media: i2c: imx477: Add support for adaptive frame
+ control
+
+Use V4L2_CID_EXPOSURE_AUTO_PRIORITY to control if the driver should
+automatically adjust the sensor frame length based on exposure time,
+allowing variable frame rates and longer exposures.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 113 +++++++++++++++++++++++++++++--------
+ 1 file changed, 91 insertions(+), 22 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -1082,6 +1082,8 @@ struct imx477 {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
++ /* This ctrl allows automatic variable framerate */
++ struct v4l2_ctrl *exposure_auto;
+
+ /* Current mode */
+ const struct imx477_mode *mode;
+@@ -1278,6 +1280,72 @@ static int imx477_open(struct v4l2_subde
+ return 0;
+ }
+
++static int imx477_set_exposure(struct imx477 *imx477, unsigned int val)
++{
++ int ret;
++
++ ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
++ IMX477_REG_VALUE_16BIT, val);
++
++ /* Setup the frame length in the case of auto framerate mode. */
++ if (imx477->exposure_auto->val) {
++ unsigned int frame_length, frame_length_max, frame_length_min;
++
++ frame_length_min = imx477->vblank->minimum +
++ imx477->mode->height;
++ frame_length_max = imx477->vblank->maximum +
++ imx477->mode->height;
++ frame_length = max(frame_length_min,
++ val + IMX477_EXPOSURE_OFFSET);
++ frame_length = min(frame_length_max, frame_length);
++ ret += imx477_write_reg(imx477, IMX477_REG_FRAME_LENGTH,
++ IMX477_REG_VALUE_16BIT, frame_length);
++ }
++
++ return ret;
++}
++
++static void imx477_adjust_exposure_range(struct imx477 *imx477,
++ struct v4l2_ctrl *ctrl)
++{
++ int exposure_max, exposure_def;
++
++ if (ctrl->id == V4L2_CID_VBLANK || !ctrl->val) {
++ /*
++ * Either VBLANK has been changed or auto framerate
++ * adjusting has been disabled. Honour the VBLANK limits
++ * when setting exposure.
++ */
++ exposure_max = imx477->mode->height + imx477->vblank->val -
++ IMX477_EXPOSURE_OFFSET;
++
++ if (ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) {
++ /*
++ * Allow VBLANK adjustments since the driver is not
++ * handling frame length control automatically.
++ */
++ __v4l2_ctrl_grab(imx477->vblank, false);
++ }
++ } else {
++ /*
++ * Auto framerate adjusting has been enabled. VBLANK
++ * ctrl has been disabled and exposure can ramp up
++ * to the maximum allowable value.
++ */
++ exposure_max = IMX477_EXPOSURE_MAX;
++ /*
++ * Do not allow VBLANK adjustments if the driver is
++ * handling it frame length control automatically.
++ */
++ __v4l2_ctrl_grab(imx477->vblank, true);
++ }
++
++ exposure_def = min(exposure_max, imx477->exposure->val);
++ __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
++ exposure_max, imx477->exposure->step,
++ exposure_def);
++}
++
+ static int imx477_set_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct imx477 *imx477 =
+@@ -1285,17 +1353,13 @@ static int imx477_set_ctrl(struct v4l2_c
+ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+ int ret = 0;
+
+- if (ctrl->id == V4L2_CID_VBLANK) {
+- int exposure_max, exposure_def;
+-
+- /* Update max exposure while meeting expected vblanking */
+- exposure_max = imx477->mode->height + ctrl->val -
+- IMX477_EXPOSURE_OFFSET;
+- exposure_def = min(exposure_max, imx477->exposure->val);
+- __v4l2_ctrl_modify_range(imx477->exposure,
+- imx477->exposure->minimum,
+- exposure_max, imx477->exposure->step,
+- exposure_def);
++ if (ctrl->id == V4L2_CID_VBLANK ||
++ ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) {
++ /*
++ * These controls may change the limits of usable exposure,
++ * so check and adjust if necessary.
++ */
++ imx477_adjust_exposure_range(imx477, ctrl);
+ }
+
+ /*
+@@ -1311,8 +1375,14 @@ static int imx477_set_ctrl(struct v4l2_c
+ IMX477_REG_VALUE_16BIT, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+- ret = imx477_write_reg(imx477, IMX477_REG_EXPOSURE,
+- IMX477_REG_VALUE_16BIT, ctrl->val);
++ ret = imx477_set_exposure(imx477, ctrl->val);
++ break;
++ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
++ /*
++ * imx477_set_exposure() will recalculate the frame length
++ * to adjust the framerate to match the exposure.
++ */
++ ret = imx477_set_exposure(imx477, imx477->exposure->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = imx477_write_reg(imx477, IMX477_REG_DIGITAL_GAIN,
+@@ -1510,9 +1580,8 @@ unsigned int imx477_get_frame_length(con
+
+ static void imx477_set_framing_limits(struct imx477 *imx477)
+ {
++ unsigned int frm_length_min, frm_length_default, hblank;
+ const struct imx477_mode *mode = imx477->mode;
+- unsigned int frm_length_min, frm_length_default;
+- unsigned int exposure_max, exposure_def, hblank;
+
+ frm_length_min = imx477_get_frame_length(mode, &mode->timeperframe_min);
+ frm_length_default =
+@@ -1522,15 +1591,10 @@ static void imx477_set_framing_limits(st
+ __v4l2_ctrl_modify_range(imx477->vblank, frm_length_min - mode->height,
+ IMX477_FRAME_LENGTH_MAX - mode->height,
+ 1, frm_length_default - mode->height);
++
++ /* Setting this will adjust the exposure limits as well. */
+ __v4l2_ctrl_s_ctrl(imx477->vblank, frm_length_default - mode->height);
+
+- /* Update max exposure while meeting expected vblanking */
+- exposure_max = IMX477_FRAME_LENGTH_MAX - IMX477_EXPOSURE_OFFSET;
+- exposure_def = frm_length_default - mode->height -
+- IMX477_EXPOSURE_OFFSET;
+- __v4l2_ctrl_modify_range(imx477->exposure, imx477->exposure->minimum,
+- exposure_max, imx477->exposure->step,
+- exposure_def);
+ /*
+ * Currently PPL is fixed to the mode specified value, so hblank
+ * depends on mode->width only, and is not changeable in any
+@@ -1939,6 +2003,11 @@ static int imx477_init_controls(struct i
+ IMX477_DGTL_GAIN_MIN, IMX477_DGTL_GAIN_MAX,
+ IMX477_DGTL_GAIN_STEP, IMX477_DGTL_GAIN_DEFAULT);
+
++ imx477->exposure_auto =
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
++ V4L2_CID_EXPOSURE_AUTO_PRIORITY,
++ 0, 1, 1, 0);
++
+ imx477->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx477_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (imx477->hflip)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch b/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch
new file mode 100644
index 0000000000..bc7b5bea91
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch
@@ -0,0 +1,52 @@
+From b1f02a027329f23272bd89c80a3f51ff64377fc2 Mon Sep 17 00:00:00 2001
+From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Date: Tue, 26 Nov 2019 15:25:16 +0100
+Subject: [PATCH] udmabuf: Remove deleted map/unmap handlers.
+
+Commit 19d32ace8b6acebc45da1ea748000ac79ccc7721 upstream.
+
+Commit 7f0de8d80816 ("dma-buf: Drop dma_buf_k(un)map") removed map/unmap
+handlers, but they still existed in udmabuf. Remove them there as well
+
+Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Fixes: 7f0de8d80816 ("dma-buf: Drop dma_buf_k(un)map")
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Daniel Vetter <daniel.vetter@intel.com>
+Cc: linux-media@vger.kernel.org
+Cc: linaro-mm-sig@lists.linaro.org
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191126142516.630200-1-maarten.lankhorst@linux.intel.com
+---
+ drivers/dma-buf/udmabuf.c | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -93,26 +93,10 @@ static void release_udmabuf(struct dma_b
+ kfree(ubuf);
+ }
+
+-static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num)
+-{
+- struct udmabuf *ubuf = buf->priv;
+- struct page *page = ubuf->pages[page_num];
+-
+- return kmap(page);
+-}
+-
+-static void kunmap_udmabuf(struct dma_buf *buf, unsigned long page_num,
+- void *vaddr)
+-{
+- kunmap(vaddr);
+-}
+-
+ static const struct dma_buf_ops udmabuf_ops = {
+ .map_dma_buf = map_udmabuf,
+ .unmap_dma_buf = unmap_udmabuf,
+ .release = release_udmabuf,
+- .map = kmap_udmabuf,
+- .unmap = kunmap_udmabuf,
+ .mmap = mmap_udmabuf,
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch b/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch
new file mode 100644
index 0000000000..166055556f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch
@@ -0,0 +1,35 @@
+From d57aecba0cd291e0c28e2c82c3d4bce06c5b5b94 Mon Sep 17 00:00:00 2001
+From: Gurchetan Singh <gurchetansingh@chromium.org>
+Date: Mon, 2 Dec 2019 17:36:24 -0800
+Subject: [PATCH] udmabuf: use cache_sgt_mapping option
+
+Commit bc7a71da43b48333f84c6534ab43d240e34cf9eb uptream.
+
+The GEM prime helpers do it, so should we. It's also possible to make
+it optional later.
+
+Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
+Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-1-gurchetansingh@chromium.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ drivers/dma-buf/udmabuf.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -94,10 +94,11 @@ static void release_udmabuf(struct dma_b
+ }
+
+ static const struct dma_buf_ops udmabuf_ops = {
+- .map_dma_buf = map_udmabuf,
+- .unmap_dma_buf = unmap_udmabuf,
+- .release = release_udmabuf,
+- .mmap = mmap_udmabuf,
++ .cache_sgt_mapping = true,
++ .map_dma_buf = map_udmabuf,
++ .unmap_dma_buf = unmap_udmabuf,
++ .release = release_udmabuf,
++ .mmap = mmap_udmabuf,
+ };
+
+ #define SEALS_WANTED (F_SEAL_SHRINK)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch b/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch
new file mode 100644
index 0000000000..68feae3d48
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch
@@ -0,0 +1,67 @@
+From ad21bb0b5abf5414e31ac3e41ff60216bee52982 Mon Sep 17 00:00:00 2001
+From: Gurchetan Singh <gurchetansingh@chromium.org>
+Date: Mon, 2 Dec 2019 17:36:25 -0800
+Subject: [PATCH] udmabuf: add a pointer to the miscdevice in dma-buf
+ private data
+
+Commit c1bbed668997268c9edccdc9db1bd1487d9e20b0 upstream.
+
+Will be used later.
+
+v2: rename 'udmabuf_misc' to 'device' (kraxel)
+
+Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
+Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-2-gurchetansingh@chromium.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ drivers/dma-buf/udmabuf.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -18,6 +18,7 @@ static const size_t size_limit_mb = 64;
+ struct udmabuf {
+ pgoff_t pagecount;
+ struct page **pages;
++ struct miscdevice *device;
+ };
+
+ static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
+@@ -104,8 +105,9 @@ static const struct dma_buf_ops udmabuf_
+ #define SEALS_WANTED (F_SEAL_SHRINK)
+ #define SEALS_DENIED (F_SEAL_WRITE)
+
+-static long udmabuf_create(const struct udmabuf_create_list *head,
+- const struct udmabuf_create_item *list)
++static long udmabuf_create(struct miscdevice *device,
++ struct udmabuf_create_list *head,
++ struct udmabuf_create_item *list)
+ {
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ struct file *memfd = NULL;
+@@ -172,6 +174,7 @@ static long udmabuf_create(const struct
+ exp_info.priv = ubuf;
+ exp_info.flags = O_RDWR;
+
++ ubuf->device = device;
+ buf = dma_buf_export(&exp_info);
+ if (IS_ERR(buf)) {
+ ret = PTR_ERR(buf);
+@@ -209,7 +212,7 @@ static long udmabuf_ioctl_create(struct
+ list.offset = create.offset;
+ list.size = create.size;
+
+- return udmabuf_create(&head, &list);
++ return udmabuf_create(filp->private_data, &head, &list);
+ }
+
+ static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg)
+@@ -228,7 +231,7 @@ static long udmabuf_ioctl_create_list(st
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+
+- ret = udmabuf_create(&head, list);
++ ret = udmabuf_create(filp->private_data, &head, list);
+ kfree(list);
+ return ret;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch b/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch
new file mode 100644
index 0000000000..5a0a59ced1
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch
@@ -0,0 +1,71 @@
+From 118802c75e04a2f1b94245695076d2506f667ae7 Mon Sep 17 00:00:00 2001
+From: Gurchetan Singh <gurchetansingh@chromium.org>
+Date: Mon, 2 Dec 2019 17:36:26 -0800
+Subject: [PATCH] udmabuf: separate out creating/destroying
+ scatter-table
+
+Commit 17a7ce203490459cff14fb1c8f9a15d65fd1c544 upstream.
+
+These are nice functions and can be re-used.
+
+Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
+Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-3-gurchetansingh@chromium.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ drivers/dma-buf/udmabuf.c | 26 +++++++++++++++++++-------
+ 1 file changed, 19 insertions(+), 7 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -47,10 +47,10 @@ static int mmap_udmabuf(struct dma_buf *
+ return 0;
+ }
+
+-static struct sg_table *map_udmabuf(struct dma_buf_attachment *at,
+- enum dma_data_direction direction)
++static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf,
++ enum dma_data_direction direction)
+ {
+- struct udmabuf *ubuf = at->dmabuf->priv;
++ struct udmabuf *ubuf = buf->priv;
+ struct sg_table *sg;
+ int ret;
+
+@@ -62,7 +62,7 @@ static struct sg_table *map_udmabuf(stru
+ GFP_KERNEL);
+ if (ret < 0)
+ goto err;
+- if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) {
++ if (!dma_map_sg(dev, sg->sgl, sg->nents, direction)) {
+ ret = -EINVAL;
+ goto err;
+ }
+@@ -74,13 +74,25 @@ err:
+ return ERR_PTR(ret);
+ }
+
++static void put_sg_table(struct device *dev, struct sg_table *sg,
++ enum dma_data_direction direction)
++{
++ dma_unmap_sg(dev, sg->sgl, sg->nents, direction);
++ sg_free_table(sg);
++ kfree(sg);
++}
++
++static struct sg_table *map_udmabuf(struct dma_buf_attachment *at,
++ enum dma_data_direction direction)
++{
++ return get_sg_table(at->dev, at->dmabuf, direction);
++}
++
+ static void unmap_udmabuf(struct dma_buf_attachment *at,
+ struct sg_table *sg,
+ enum dma_data_direction direction)
+ {
+- dma_unmap_sg(at->dev, sg->sgl, sg->nents, direction);
+- sg_free_table(sg);
+- kfree(sg);
++ return put_sg_table(at->dev, sg, direction);
+ }
+
+ static void release_udmabuf(struct dma_buf *buf)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch b/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch
new file mode 100644
index 0000000000..79f6a67387
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch
@@ -0,0 +1,90 @@
+From 9dc454ebc4380cd90c24a3c224bb0ac7b3d9cc29 Mon Sep 17 00:00:00 2001
+From: Gurchetan Singh <gurchetansingh@chromium.org>
+Date: Mon, 2 Dec 2019 17:36:27 -0800
+Subject: [PATCH] udmabuf: implement begin_cpu_access/end_cpu_access
+ hooks
+
+Commit 284562e1f34874e267d4f499362c3816f8f6bc3f upstream.
+
+With the misc device, we should end up using the result of
+get_arch_dma_ops(..) or dma-direct ops.
+
+This can allow us to have WC mappings in the guest after
+synchronization.
+
+Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
+Link: http://patchwork.freedesktop.org/patch/msgid/20191203013627.85991-4-gurchetansingh@chromium.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ drivers/dma-buf/udmabuf.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -18,6 +18,7 @@ static const size_t size_limit_mb = 64;
+ struct udmabuf {
+ pgoff_t pagecount;
+ struct page **pages;
++ struct sg_table *sg;
+ struct miscdevice *device;
+ };
+
+@@ -98,20 +99,58 @@ static void unmap_udmabuf(struct dma_buf
+ static void release_udmabuf(struct dma_buf *buf)
+ {
+ struct udmabuf *ubuf = buf->priv;
++ struct device *dev = ubuf->device->this_device;
+ pgoff_t pg;
+
++ if (ubuf->sg)
++ put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL);
++
+ for (pg = 0; pg < ubuf->pagecount; pg++)
+ put_page(ubuf->pages[pg]);
+ kfree(ubuf->pages);
+ kfree(ubuf);
+ }
+
++static int begin_cpu_udmabuf(struct dma_buf *buf,
++ enum dma_data_direction direction)
++{
++ struct udmabuf *ubuf = buf->priv;
++ struct device *dev = ubuf->device->this_device;
++
++ if (!ubuf->sg) {
++ ubuf->sg = get_sg_table(dev, buf, direction);
++ if (IS_ERR(ubuf->sg))
++ return PTR_ERR(ubuf->sg);
++ } else {
++ dma_sync_sg_for_device(dev, ubuf->sg->sgl,
++ ubuf->sg->nents,
++ direction);
++ }
++
++ return 0;
++}
++
++static int end_cpu_udmabuf(struct dma_buf *buf,
++ enum dma_data_direction direction)
++{
++ struct udmabuf *ubuf = buf->priv;
++ struct device *dev = ubuf->device->this_device;
++
++ if (!ubuf->sg)
++ return -EINVAL;
++
++ dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, direction);
++ return 0;
++}
++
+ static const struct dma_buf_ops udmabuf_ops = {
+ .cache_sgt_mapping = true,
+ .map_dma_buf = map_udmabuf,
+ .unmap_dma_buf = unmap_udmabuf,
+ .release = release_udmabuf,
+ .mmap = mmap_udmabuf,
++ .begin_cpu_access = begin_cpu_udmabuf,
++ .end_cpu_access = end_cpu_udmabuf,
+ };
+
+ #define SEALS_WANTED (F_SEAL_SHRINK)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch b/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch
new file mode 100644
index 0000000000..14cfe8aed3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch
@@ -0,0 +1,60 @@
+From 153f1e1def226f87cc4c307d2806b000a820f64b Mon Sep 17 00:00:00 2001
+From: Gurchetan Singh <gurchetansingh@chromium.org>
+Date: Tue, 17 Dec 2019 15:02:28 -0800
+Subject: [PATCH] udmabuf: fix dma-buf cpu access
+
+Commit 1ffe09590121fbb3786d6c860acdd200f7ab095c upstream.
+
+I'm just going to put Chia's review comment here since it sums
+the issue rather nicely:
+
+"(1) Semantically, a dma-buf is in DMA domain. CPU access from the
+importer must be surrounded by {begin,end}_cpu_access. This gives the
+exporter a chance to move the buffer to the CPU domain temporarily.
+
+(2) When the exporter itself has other means to do CPU access, it is
+only reasonable for the exporter to move the buffer to the CPU domain
+before access, and to the DMA domain after access. The exporter can
+potentially reuse {begin,end}_cpu_access for that purpose.
+
+Because of (1), udmabuf does need to implement the
+{begin,end}_cpu_access hooks. But "begin" should mean
+dma_sync_sg_for_cpu and "end" should mean dma_sync_sg_for_device.
+
+Because of (2), if userspace wants to continuing accessing through the
+memfd mapping, it should call udmabuf's {begin,end}_cpu_access to
+avoid cache issues."
+
+Reported-by: Chia-I Wu <olvaffe@gmail.com>
+Suggested-by: Chia-I Wu <olvaffe@gmail.com>
+Fixes: 284562e1f348 ("udmabuf: implement begin_cpu_access/end_cpu_access hooks")
+Signed-off-by: Gurchetan Singh <gurchetansingh@chromium.org>
+Link: http://patchwork.freedesktop.org/patch/msgid/20191217230228.453-1-gurchetansingh@chromium.org
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ drivers/dma-buf/udmabuf.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma-buf/udmabuf.c
++++ b/drivers/dma-buf/udmabuf.c
+@@ -122,9 +122,8 @@ static int begin_cpu_udmabuf(struct dma_
+ if (IS_ERR(ubuf->sg))
+ return PTR_ERR(ubuf->sg);
+ } else {
+- dma_sync_sg_for_device(dev, ubuf->sg->sgl,
+- ubuf->sg->nents,
+- direction);
++ dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents,
++ direction);
+ }
+
+ return 0;
+@@ -139,7 +138,7 @@ static int end_cpu_udmabuf(struct dma_bu
+ if (!ubuf->sg)
+ return -EINVAL;
+
+- dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, direction);
++ dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction);
+ return 0;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch b/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch
new file mode 100644
index 0000000000..03d5d0c85b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch
@@ -0,0 +1,525 @@
+From 4c5a9a5db543b5fd998fbc3e15fd4a2d2a3971a9 Mon Sep 17 00:00:00 2001
+From: "Andrew F. Davis" <afd@ti.com>
+Date: Tue, 3 Dec 2019 17:26:37 +0000
+Subject: [PATCH] dma-buf: Add dma-buf heaps framework
+
+Commit c02a81fba74fe3488ad6b08bfb5a1329005418f8 upstream.
+This framework allows a unified userspace interface for dma-buf
+exporters, allowing userland to allocate specific types of memory
+for use in dma-buf sharing.
+
+Each heap is given its own device node, which a user can allocate
+a dma-buf fd from using the DMA_HEAP_IOC_ALLOC.
+
+This code is an evoluiton of the Android ION implementation,
+and a big thanks is due to its authors/maintainers over time
+for their effort:
+ Rebecca Schultz Zavin, Colin Cross, Benjamin Gaignard,
+ Laura Abbott, and many other contributors!
+
+Cc: Laura Abbott <labbott@redhat.com>
+Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Liam Mark <lmark@codeaurora.org>
+Cc: Pratik Patel <pratikp@codeaurora.org>
+Cc: Brian Starkey <Brian.Starkey@arm.com>
+Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
+Cc: Sudipto Paul <Sudipto.Paul@arm.com>
+Cc: Andrew F. Davis <afd@ti.com>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Chenbo Feng <fengc@google.com>
+Cc: Alistair Strachan <astrachan@google.com>
+Cc: Hridya Valsaraju <hridya@google.com>
+Cc: Sandeep Patil <sspatil@google.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Brian Starkey <brian.starkey@arm.com>
+Acked-by: Sandeep Patil <sspatil@android.com>
+Signed-off-by: Andrew F. Davis <afd@ti.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-2-john.stultz@linaro.org
+---
+ MAINTAINERS | 18 +++
+ drivers/dma-buf/Kconfig | 9 ++
+ drivers/dma-buf/Makefile | 1 +
+ drivers/dma-buf/dma-heap.c | 297 ++++++++++++++++++++++++++++++++++
+ include/linux/dma-heap.h | 59 +++++++
+ include/uapi/linux/dma-heap.h | 53 ++++++
+ 6 files changed, 437 insertions(+)
+ create mode 100644 drivers/dma-buf/dma-heap.c
+ create mode 100644 include/linux/dma-heap.h
+ create mode 100644 include/uapi/linux/dma-heap.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -4962,6 +4962,24 @@ F: include/linux/*fence.h
+ F: Documentation/driver-api/dma-buf.rst
+ T: git git://anongit.freedesktop.org/drm/drm-misc
+
++DMA-BUF HEAPS FRAMEWORK
++M: Sumit Semwal <sumit.semwal@linaro.org>
++R: Andrew F. Davis <afd@ti.com>
++R: Benjamin Gaignard <benjamin.gaignard@linaro.org>
++R: Liam Mark <lmark@codeaurora.org>
++R: Laura Abbott <labbott@redhat.com>
++R: Brian Starkey <Brian.Starkey@arm.com>
++R: John Stultz <john.stultz@linaro.org>
++S: Maintained
++L: linux-media@vger.kernel.org
++L: dri-devel@lists.freedesktop.org
++L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers)
++F: include/uapi/linux/dma-heap.h
++F: include/linux/dma-heap.h
++F: drivers/dma-buf/dma-heap.c
++F: drivers/dma-buf/heaps/*
++T: git git://anongit.freedesktop.org/drm/drm-misc
++
+ DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
+ M: Vinod Koul <vkoul@kernel.org>
+ L: dmaengine@vger.kernel.org
+--- a/drivers/dma-buf/Kconfig
++++ b/drivers/dma-buf/Kconfig
+@@ -44,4 +44,13 @@ config DMABUF_SELFTESTS
+ default n
+ depends on DMA_SHARED_BUFFER
+
++menuconfig DMABUF_HEAPS
++ bool "DMA-BUF Userland Memory Heaps"
++ select DMA_SHARED_BUFFER
++ help
++ Choose this option to enable the DMA-BUF userland memory heaps.
++ This options creates per heap chardevs in /dev/dma_heap/ which
++ allows userspace to allocate dma-bufs that can be shared
++ between drivers.
++
+ endmenu
+--- a/drivers/dma-buf/Makefile
++++ b/drivers/dma-buf/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-s
+
+ dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
+ dma-resv.o seqno-fence.o
++obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
+ dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o
+ dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
+ dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o
+--- /dev/null
++++ b/drivers/dma-buf/dma-heap.c
+@@ -0,0 +1,297 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Framework for userspace DMA-BUF allocations
++ *
++ * Copyright (C) 2011 Google, Inc.
++ * Copyright (C) 2019 Linaro Ltd.
++ */
++
++#include <linux/cdev.h>
++#include <linux/debugfs.h>
++#include <linux/device.h>
++#include <linux/dma-buf.h>
++#include <linux/err.h>
++#include <linux/xarray.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/syscalls.h>
++#include <linux/dma-heap.h>
++#include <uapi/linux/dma-heap.h>
++
++#define DEVNAME "dma_heap"
++
++#define NUM_HEAP_MINORS 128
++
++/**
++ * struct dma_heap - represents a dmabuf heap in the system
++ * @name: used for debugging/device-node name
++ * @ops: ops struct for this heap
++ * @heap_devt heap device node
++ * @list list head connecting to list of heaps
++ * @heap_cdev heap char device
++ *
++ * Represents a heap of memory from which buffers can be made.
++ */
++struct dma_heap {
++ const char *name;
++ const struct dma_heap_ops *ops;
++ void *priv;
++ dev_t heap_devt;
++ struct list_head list;
++ struct cdev heap_cdev;
++};
++
++static LIST_HEAD(heap_list);
++static DEFINE_MUTEX(heap_list_lock);
++static dev_t dma_heap_devt;
++static struct class *dma_heap_class;
++static DEFINE_XARRAY_ALLOC(dma_heap_minors);
++
++static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
++ unsigned int fd_flags,
++ unsigned int heap_flags)
++{
++ /*
++ * Allocations from all heaps have to begin
++ * and end on page boundaries.
++ */
++ len = PAGE_ALIGN(len);
++ if (!len)
++ return -EINVAL;
++
++ return heap->ops->allocate(heap, len, fd_flags, heap_flags);
++}
++
++static int dma_heap_open(struct inode *inode, struct file *file)
++{
++ struct dma_heap *heap;
++
++ heap = xa_load(&dma_heap_minors, iminor(inode));
++ if (!heap) {
++ pr_err("dma_heap: minor %d unknown.\n", iminor(inode));
++ return -ENODEV;
++ }
++
++ /* instance data as context */
++ file->private_data = heap;
++ nonseekable_open(inode, file);
++
++ return 0;
++}
++
++static long dma_heap_ioctl_allocate(struct file *file, void *data)
++{
++ struct dma_heap_allocation_data *heap_allocation = data;
++ struct dma_heap *heap = file->private_data;
++ int fd;
++
++ if (heap_allocation->fd)
++ return -EINVAL;
++
++ if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
++ return -EINVAL;
++
++ if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
++ return -EINVAL;
++
++ fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
++ heap_allocation->fd_flags,
++ heap_allocation->heap_flags);
++ if (fd < 0)
++ return fd;
++
++ heap_allocation->fd = fd;
++
++ return 0;
++}
++
++unsigned int dma_heap_ioctl_cmds[] = {
++ DMA_HEAP_IOC_ALLOC,
++};
++
++static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
++ unsigned long arg)
++{
++ char stack_kdata[128];
++ char *kdata = stack_kdata;
++ unsigned int kcmd;
++ unsigned int in_size, out_size, drv_size, ksize;
++ int nr = _IOC_NR(ucmd);
++ int ret = 0;
++
++ if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
++ return -EINVAL;
++
++ /* Get the kernel ioctl cmd that matches */
++ kcmd = dma_heap_ioctl_cmds[nr];
++
++ /* Figure out the delta between user cmd size and kernel cmd size */
++ drv_size = _IOC_SIZE(kcmd);
++ out_size = _IOC_SIZE(ucmd);
++ in_size = out_size;
++ if ((ucmd & kcmd & IOC_IN) == 0)
++ in_size = 0;
++ if ((ucmd & kcmd & IOC_OUT) == 0)
++ out_size = 0;
++ ksize = max(max(in_size, out_size), drv_size);
++
++ /* If necessary, allocate buffer for ioctl argument */
++ if (ksize > sizeof(stack_kdata)) {
++ kdata = kmalloc(ksize, GFP_KERNEL);
++ if (!kdata)
++ return -ENOMEM;
++ }
++
++ if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
++ ret = -EFAULT;
++ goto err;
++ }
++
++ /* zero out any difference between the kernel/user structure size */
++ if (ksize > in_size)
++ memset(kdata + in_size, 0, ksize - in_size);
++
++ switch (kcmd) {
++ case DMA_HEAP_IOC_ALLOC:
++ ret = dma_heap_ioctl_allocate(file, kdata);
++ break;
++ default:
++ return -ENOTTY;
++ }
++
++ if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
++ ret = -EFAULT;
++err:
++ if (kdata != stack_kdata)
++ kfree(kdata);
++ return ret;
++}
++
++static const struct file_operations dma_heap_fops = {
++ .owner = THIS_MODULE,
++ .open = dma_heap_open,
++ .unlocked_ioctl = dma_heap_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = dma_heap_ioctl,
++#endif
++};
++
++/**
++ * dma_heap_get_drvdata() - get per-subdriver data for the heap
++ * @heap: DMA-Heap to retrieve private data for
++ *
++ * Returns:
++ * The per-subdriver data for the heap.
++ */
++void *dma_heap_get_drvdata(struct dma_heap *heap)
++{
++ return heap->priv;
++}
++
++struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
++{
++ struct dma_heap *heap, *h, *err_ret;
++ struct device *dev_ret;
++ unsigned int minor;
++ int ret;
++
++ if (!exp_info->name || !strcmp(exp_info->name, "")) {
++ pr_err("dma_heap: Cannot add heap without a name\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ if (!exp_info->ops || !exp_info->ops->allocate) {
++ pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
++ return ERR_PTR(-EINVAL);
++ }
++
++ /* check the name is unique */
++ mutex_lock(&heap_list_lock);
++ list_for_each_entry(h, &heap_list, list) {
++ if (!strcmp(h->name, exp_info->name)) {
++ mutex_unlock(&heap_list_lock);
++ pr_err("dma_heap: Already registered heap named %s\n",
++ exp_info->name);
++ return ERR_PTR(-EINVAL);
++ }
++ }
++ mutex_unlock(&heap_list_lock);
++
++ heap = kzalloc(sizeof(*heap), GFP_KERNEL);
++ if (!heap)
++ return ERR_PTR(-ENOMEM);
++
++ heap->name = exp_info->name;
++ heap->ops = exp_info->ops;
++ heap->priv = exp_info->priv;
++
++ /* Find unused minor number */
++ ret = xa_alloc(&dma_heap_minors, &minor, heap,
++ XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
++ if (ret < 0) {
++ pr_err("dma_heap: Unable to get minor number for heap\n");
++ err_ret = ERR_PTR(ret);
++ goto err0;
++ }
++
++ /* Create device */
++ heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor);
++
++ cdev_init(&heap->heap_cdev, &dma_heap_fops);
++ ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
++ if (ret < 0) {
++ pr_err("dma_heap: Unable to add char device\n");
++ err_ret = ERR_PTR(ret);
++ goto err1;
++ }
++
++ dev_ret = device_create(dma_heap_class,
++ NULL,
++ heap->heap_devt,
++ NULL,
++ heap->name);
++ if (IS_ERR(dev_ret)) {
++ pr_err("dma_heap: Unable to create device\n");
++ err_ret = ERR_CAST(dev_ret);
++ goto err2;
++ }
++ /* Add heap to the list */
++ mutex_lock(&heap_list_lock);
++ list_add(&heap->list, &heap_list);
++ mutex_unlock(&heap_list_lock);
++
++ return heap;
++
++err2:
++ cdev_del(&heap->heap_cdev);
++err1:
++ xa_erase(&dma_heap_minors, minor);
++err0:
++ kfree(heap);
++ return err_ret;
++}
++
++static char *dma_heap_devnode(struct device *dev, umode_t *mode)
++{
++ return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
++}
++
++static int dma_heap_init(void)
++{
++ int ret;
++
++ ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
++ if (ret)
++ return ret;
++
++ dma_heap_class = class_create(THIS_MODULE, DEVNAME);
++ if (IS_ERR(dma_heap_class)) {
++ unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
++ return PTR_ERR(dma_heap_class);
++ }
++ dma_heap_class->devnode = dma_heap_devnode;
++
++ return 0;
++}
++subsys_initcall(dma_heap_init);
+--- /dev/null
++++ b/include/linux/dma-heap.h
+@@ -0,0 +1,59 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DMABUF Heaps Allocation Infrastructure
++ *
++ * Copyright (C) 2011 Google, Inc.
++ * Copyright (C) 2019 Linaro Ltd.
++ */
++
++#ifndef _DMA_HEAPS_H
++#define _DMA_HEAPS_H
++
++#include <linux/cdev.h>
++#include <linux/types.h>
++
++struct dma_heap;
++
++/**
++ * struct dma_heap_ops - ops to operate on a given heap
++ * @allocate: allocate dmabuf and return fd
++ *
++ * allocate returns dmabuf fd on success, -errno on error.
++ */
++struct dma_heap_ops {
++ int (*allocate)(struct dma_heap *heap,
++ unsigned long len,
++ unsigned long fd_flags,
++ unsigned long heap_flags);
++};
++
++/**
++ * struct dma_heap_export_info - information needed to export a new dmabuf heap
++ * @name: used for debugging/device-node name
++ * @ops: ops struct for this heap
++ * @priv: heap exporter private data
++ *
++ * Information needed to export a new dmabuf heap.
++ */
++struct dma_heap_export_info {
++ const char *name;
++ const struct dma_heap_ops *ops;
++ void *priv;
++};
++
++/**
++ * dma_heap_get_drvdata() - get per-heap driver data
++ * @heap: DMA-Heap to retrieve private data for
++ *
++ * Returns:
++ * The per-heap data for the heap.
++ */
++void *dma_heap_get_drvdata(struct dma_heap *heap);
++
++/**
++ * dma_heap_add - adds a heap to dmabuf heaps
++ * @exp_info: information needed to register this heap
++ */
++struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
++
++#endif /* _DMA_HEAPS_H */
+--- /dev/null
++++ b/include/uapi/linux/dma-heap.h
+@@ -0,0 +1,53 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++/*
++ * DMABUF Heaps Userspace API
++ *
++ * Copyright (C) 2011 Google, Inc.
++ * Copyright (C) 2019 Linaro Ltd.
++ */
++#ifndef _UAPI_LINUX_DMABUF_POOL_H
++#define _UAPI_LINUX_DMABUF_POOL_H
++
++#include <linux/ioctl.h>
++#include <linux/types.h>
++
++/**
++ * DOC: DMABUF Heaps Userspace API
++ */
++
++/* Valid FD_FLAGS are O_CLOEXEC, O_RDONLY, O_WRONLY, O_RDWR */
++#define DMA_HEAP_VALID_FD_FLAGS (O_CLOEXEC | O_ACCMODE)
++
++/* Currently no heap flags */
++#define DMA_HEAP_VALID_HEAP_FLAGS (0)
++
++/**
++ * struct dma_heap_allocation_data - metadata passed from userspace for
++ * allocations
++ * @len: size of the allocation
++ * @fd: will be populated with a fd which provides the
++ * handle to the allocated dma-buf
++ * @fd_flags: file descriptor flags used when allocating
++ * @heap_flags: flags passed to heap
++ *
++ * Provided by userspace as an argument to the ioctl
++ */
++struct dma_heap_allocation_data {
++ __u64 len;
++ __u32 fd;
++ __u32 fd_flags;
++ __u64 heap_flags;
++};
++
++#define DMA_HEAP_IOC_MAGIC 'H'
++
++/**
++ * DOC: DMA_HEAP_IOC_ALLOC - allocate memory from pool
++ *
++ * Takes a dma_heap_allocation_data struct and returns it with the fd field
++ * populated with the dmabuf handle of the allocation.
++ */
++#define DMA_HEAP_IOC_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\
++ struct dma_heap_allocation_data)
++
++#endif /* _UAPI_LINUX_DMABUF_POOL_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch
new file mode 100644
index 0000000000..10eb46ca7b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch
@@ -0,0 +1,394 @@
+From adde2d6532428cdcaeb60081abb299ce6e5aa76b Mon Sep 17 00:00:00 2001
+From: John Stultz <john.stultz@linaro.org>
+Date: Tue, 3 Dec 2019 17:26:38 +0000
+Subject: [PATCH] dma-buf: heaps: Add heap helpers
+
+Commit 5248eb12fea890a03b4cdc3ef546d6319d4d9b73 upstream.
+
+Add generic helper dmabuf ops for dma heaps, so we can reduce
+the amount of duplicative code for the exported dmabufs.
+
+This code is an evolution of the Android ION implementation, so
+thanks to its original authors and maintainters:
+ Rebecca Schultz Zavin, Colin Cross, Laura Abbott, and others!
+
+Cc: Laura Abbott <labbott@redhat.com>
+Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Liam Mark <lmark@codeaurora.org>
+Cc: Pratik Patel <pratikp@codeaurora.org>
+Cc: Brian Starkey <Brian.Starkey@arm.com>
+Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
+Cc: Sudipto Paul <Sudipto.Paul@arm.com>
+Cc: Andrew F. Davis <afd@ti.com>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Chenbo Feng <fengc@google.com>
+Cc: Alistair Strachan <astrachan@google.com>
+Cc: Hridya Valsaraju <hridya@google.com>
+Cc: Sandeep Patil <sspatil@google.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Reviewed-by: Brian Starkey <brian.starkey@arm.com>
+Acked-by: Sandeep Patil <sspatil@android.com>
+Acked-by: Laura Abbott <labbott@redhat.com>
+Tested-by: Ayan Kumar Halder <ayan.halder@arm.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-3-john.stultz@linaro.org
+---
+ drivers/dma-buf/Makefile | 1 +
+ drivers/dma-buf/heaps/Makefile | 2 +
+ drivers/dma-buf/heaps/heap-helpers.c | 271 +++++++++++++++++++++++++++
+ drivers/dma-buf/heaps/heap-helpers.h | 53 ++++++
+ 4 files changed, 327 insertions(+)
+ create mode 100644 drivers/dma-buf/heaps/Makefile
+ create mode 100644 drivers/dma-buf/heaps/heap-helpers.c
+ create mode 100644 drivers/dma-buf/heaps/heap-helpers.h
+
+--- a/drivers/dma-buf/Makefile
++++ b/drivers/dma-buf/Makefile
+@@ -4,6 +4,7 @@ obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-s
+ dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
+ dma-resv.o seqno-fence.o
+ obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
++obj-$(CONFIG_DMABUF_HEAPS) += heaps/
+ dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o
+ dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
+ dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o
+--- /dev/null
++++ b/drivers/dma-buf/heaps/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-y += heap-helpers.o
+--- /dev/null
++++ b/drivers/dma-buf/heaps/heap-helpers.c
+@@ -0,0 +1,271 @@
++// SPDX-License-Identifier: GPL-2.0
++#include <linux/device.h>
++#include <linux/dma-buf.h>
++#include <linux/err.h>
++#include <linux/highmem.h>
++#include <linux/idr.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/vmalloc.h>
++#include <uapi/linux/dma-heap.h>
++
++#include "heap-helpers.h"
++
++void init_heap_helper_buffer(struct heap_helper_buffer *buffer,
++ void (*free)(struct heap_helper_buffer *))
++{
++ buffer->priv_virt = NULL;
++ mutex_init(&buffer->lock);
++ buffer->vmap_cnt = 0;
++ buffer->vaddr = NULL;
++ buffer->pagecount = 0;
++ buffer->pages = NULL;
++ INIT_LIST_HEAD(&buffer->attachments);
++ buffer->free = free;
++}
++
++struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer,
++ int fd_flags)
++{
++ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
++
++ exp_info.ops = &heap_helper_ops;
++ exp_info.size = buffer->size;
++ exp_info.flags = fd_flags;
++ exp_info.priv = buffer;
++
++ return dma_buf_export(&exp_info);
++}
++
++static void *dma_heap_map_kernel(struct heap_helper_buffer *buffer)
++{
++ void *vaddr;
++
++ vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
++ if (!vaddr)
++ return ERR_PTR(-ENOMEM);
++
++ return vaddr;
++}
++
++static void dma_heap_buffer_destroy(struct heap_helper_buffer *buffer)
++{
++ if (buffer->vmap_cnt > 0) {
++ WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
++ vunmap(buffer->vaddr);
++ }
++
++ buffer->free(buffer);
++}
++
++static void *dma_heap_buffer_vmap_get(struct heap_helper_buffer *buffer)
++{
++ void *vaddr;
++
++ if (buffer->vmap_cnt) {
++ buffer->vmap_cnt++;
++ return buffer->vaddr;
++ }
++ vaddr = dma_heap_map_kernel(buffer);
++ if (IS_ERR(vaddr))
++ return vaddr;
++ buffer->vaddr = vaddr;
++ buffer->vmap_cnt++;
++ return vaddr;
++}
++
++static void dma_heap_buffer_vmap_put(struct heap_helper_buffer *buffer)
++{
++ if (!--buffer->vmap_cnt) {
++ vunmap(buffer->vaddr);
++ buffer->vaddr = NULL;
++ }
++}
++
++struct dma_heaps_attachment {
++ struct device *dev;
++ struct sg_table table;
++ struct list_head list;
++};
++
++static int dma_heap_attach(struct dma_buf *dmabuf,
++ struct dma_buf_attachment *attachment)
++{
++ struct dma_heaps_attachment *a;
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++ int ret;
++
++ a = kzalloc(sizeof(*a), GFP_KERNEL);
++ if (!a)
++ return -ENOMEM;
++
++ ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
++ buffer->pagecount, 0,
++ buffer->pagecount << PAGE_SHIFT,
++ GFP_KERNEL);
++ if (ret) {
++ kfree(a);
++ return ret;
++ }
++
++ a->dev = attachment->dev;
++ INIT_LIST_HEAD(&a->list);
++
++ attachment->priv = a;
++
++ mutex_lock(&buffer->lock);
++ list_add(&a->list, &buffer->attachments);
++ mutex_unlock(&buffer->lock);
++
++ return 0;
++}
++
++static void dma_heap_detach(struct dma_buf *dmabuf,
++ struct dma_buf_attachment *attachment)
++{
++ struct dma_heaps_attachment *a = attachment->priv;
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++
++ mutex_lock(&buffer->lock);
++ list_del(&a->list);
++ mutex_unlock(&buffer->lock);
++
++ sg_free_table(&a->table);
++ kfree(a);
++}
++
++static
++struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
++ enum dma_data_direction direction)
++{
++ struct dma_heaps_attachment *a = attachment->priv;
++ struct sg_table *table;
++
++ table = &a->table;
++
++ if (!dma_map_sg(attachment->dev, table->sgl, table->nents,
++ direction))
++ table = ERR_PTR(-ENOMEM);
++ return table;
++}
++
++static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
++ struct sg_table *table,
++ enum dma_data_direction direction)
++{
++ dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction);
++}
++
++static vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf)
++{
++ struct vm_area_struct *vma = vmf->vma;
++ struct heap_helper_buffer *buffer = vma->vm_private_data;
++
++ if (vmf->pgoff > buffer->pagecount)
++ return VM_FAULT_SIGBUS;
++
++ vmf->page = buffer->pages[vmf->pgoff];
++ get_page(vmf->page);
++
++ return 0;
++}
++
++static const struct vm_operations_struct dma_heap_vm_ops = {
++ .fault = dma_heap_vm_fault,
++};
++
++static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++
++ if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
++ return -EINVAL;
++
++ vma->vm_ops = &dma_heap_vm_ops;
++ vma->vm_private_data = buffer;
++
++ return 0;
++}
++
++static void dma_heap_dma_buf_release(struct dma_buf *dmabuf)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++
++ dma_heap_buffer_destroy(buffer);
++}
++
++static int dma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
++ enum dma_data_direction direction)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++ struct dma_heaps_attachment *a;
++ int ret = 0;
++
++ mutex_lock(&buffer->lock);
++
++ if (buffer->vmap_cnt)
++ invalidate_kernel_vmap_range(buffer->vaddr, buffer->size);
++
++ list_for_each_entry(a, &buffer->attachments, list) {
++ dma_sync_sg_for_cpu(a->dev, a->table.sgl, a->table.nents,
++ direction);
++ }
++ mutex_unlock(&buffer->lock);
++
++ return ret;
++}
++
++static int dma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
++ enum dma_data_direction direction)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++ struct dma_heaps_attachment *a;
++
++ mutex_lock(&buffer->lock);
++
++ if (buffer->vmap_cnt)
++ flush_kernel_vmap_range(buffer->vaddr, buffer->size);
++
++ list_for_each_entry(a, &buffer->attachments, list) {
++ dma_sync_sg_for_device(a->dev, a->table.sgl, a->table.nents,
++ direction);
++ }
++ mutex_unlock(&buffer->lock);
++
++ return 0;
++}
++
++static void *dma_heap_dma_buf_vmap(struct dma_buf *dmabuf)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++ void *vaddr;
++
++ mutex_lock(&buffer->lock);
++ vaddr = dma_heap_buffer_vmap_get(buffer);
++ mutex_unlock(&buffer->lock);
++
++ return vaddr;
++}
++
++static void dma_heap_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
++{
++ struct heap_helper_buffer *buffer = dmabuf->priv;
++
++ mutex_lock(&buffer->lock);
++ dma_heap_buffer_vmap_put(buffer);
++ mutex_unlock(&buffer->lock);
++}
++
++const struct dma_buf_ops heap_helper_ops = {
++ .map_dma_buf = dma_heap_map_dma_buf,
++ .unmap_dma_buf = dma_heap_unmap_dma_buf,
++ .mmap = dma_heap_mmap,
++ .release = dma_heap_dma_buf_release,
++ .attach = dma_heap_attach,
++ .detach = dma_heap_detach,
++ .begin_cpu_access = dma_heap_dma_buf_begin_cpu_access,
++ .end_cpu_access = dma_heap_dma_buf_end_cpu_access,
++ .vmap = dma_heap_dma_buf_vmap,
++ .vunmap = dma_heap_dma_buf_vunmap,
++};
+--- /dev/null
++++ b/drivers/dma-buf/heaps/heap-helpers.h
+@@ -0,0 +1,53 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DMABUF Heaps helper code
++ *
++ * Copyright (C) 2011 Google, Inc.
++ * Copyright (C) 2019 Linaro Ltd.
++ */
++
++#ifndef _HEAP_HELPERS_H
++#define _HEAP_HELPERS_H
++
++#include <linux/dma-heap.h>
++#include <linux/list.h>
++
++/**
++ * struct heap_helper_buffer - helper buffer metadata
++ * @heap: back pointer to the heap the buffer came from
++ * @dmabuf: backing dma-buf for this buffer
++ * @size: size of the buffer
++ * @priv_virt pointer to heap specific private value
++ * @lock mutext to protect the data in this structure
++ * @vmap_cnt count of vmap references on the buffer
++ * @vaddr vmap'ed virtual address
++ * @pagecount number of pages in the buffer
++ * @pages list of page pointers
++ * @attachments list of device attachments
++ *
++ * @free heap callback to free the buffer
++ */
++struct heap_helper_buffer {
++ struct dma_heap *heap;
++ struct dma_buf *dmabuf;
++ size_t size;
++
++ void *priv_virt;
++ struct mutex lock;
++ int vmap_cnt;
++ void *vaddr;
++ pgoff_t pagecount;
++ struct page **pages;
++ struct list_head attachments;
++
++ void (*free)(struct heap_helper_buffer *buffer);
++};
++
++void init_heap_helper_buffer(struct heap_helper_buffer *buffer,
++ void (*free)(struct heap_helper_buffer *));
++
++struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer,
++ int fd_flags);
++
++extern const struct dma_buf_ops heap_helper_ops;
++#endif /* _HEAP_HELPERS_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch b/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch
new file mode 100644
index 0000000000..55c2450821
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch
@@ -0,0 +1,200 @@
+From 8392cd87592f31737286ea16f11781a234de3564 Mon Sep 17 00:00:00 2001
+From: John Stultz <john.stultz@linaro.org>
+Date: Tue, 3 Dec 2019 17:26:39 +0000
+Subject: [PATCH] dma-buf: heaps: Add system heap to dmabuf heaps
+
+Commit efa04fefebbd724ffda7f49e42d057a7217c45b0 upstream.
+
+This patch adds system heap to the dma-buf heaps framework.
+
+This allows applications to get a page-allocator backed dma-buf
+for non-contiguous memory.
+
+This code is an evolution of the Android ION implementation, so
+thanks to its original authors and maintainters:
+ Rebecca Schultz Zavin, Colin Cross, Laura Abbott, and others!
+
+Cc: Laura Abbott <labbott@redhat.com>
+Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Liam Mark <lmark@codeaurora.org>
+Cc: Pratik Patel <pratikp@codeaurora.org>
+Cc: Brian Starkey <Brian.Starkey@arm.com>
+Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
+Cc: Sudipto Paul <Sudipto.Paul@arm.com>
+Cc: Andrew F. Davis <afd@ti.com>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Chenbo Feng <fengc@google.com>
+Cc: Alistair Strachan <astrachan@google.com>
+Cc: Hridya Valsaraju <hridya@google.com>
+Cc: Sandeep Patil <sspatil@google.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Reviewed-by: Brian Starkey <brian.starkey@arm.com>
+Acked-by: Sandeep Patil <sspatil@android.com>
+Acked-by: Laura Abbott <labbott@redhat.com>
+Tested-by: Ayan Kumar Halder <ayan.halder@arm.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-4-john.stultz@linaro.org
+---
+ drivers/dma-buf/Kconfig | 2 +
+ drivers/dma-buf/heaps/Kconfig | 6 ++
+ drivers/dma-buf/heaps/Makefile | 1 +
+ drivers/dma-buf/heaps/system_heap.c | 123 ++++++++++++++++++++++++++++
+ 4 files changed, 132 insertions(+)
+ create mode 100644 drivers/dma-buf/heaps/Kconfig
+ create mode 100644 drivers/dma-buf/heaps/system_heap.c
+
+--- a/drivers/dma-buf/Kconfig
++++ b/drivers/dma-buf/Kconfig
+@@ -53,4 +53,6 @@ menuconfig DMABUF_HEAPS
+ allows userspace to allocate dma-bufs that can be shared
+ between drivers.
+
++source "drivers/dma-buf/heaps/Kconfig"
++
+ endmenu
+--- /dev/null
++++ b/drivers/dma-buf/heaps/Kconfig
+@@ -0,0 +1,6 @@
++config DMABUF_HEAPS_SYSTEM
++ bool "DMA-BUF System Heap"
++ depends on DMABUF_HEAPS
++ help
++ Choose this option to enable the system dmabuf heap. The system heap
++ is backed by pages from the buddy allocator. If in doubt, say Y.
+--- a/drivers/dma-buf/heaps/Makefile
++++ b/drivers/dma-buf/heaps/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-y += heap-helpers.o
++obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
+--- /dev/null
++++ b/drivers/dma-buf/heaps/system_heap.c
+@@ -0,0 +1,123 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * DMABUF System heap exporter
++ *
++ * Copyright (C) 2011 Google, Inc.
++ * Copyright (C) 2019 Linaro Ltd.
++ */
++
++#include <linux/dma-buf.h>
++#include <linux/dma-mapping.h>
++#include <linux/dma-heap.h>
++#include <linux/err.h>
++#include <linux/highmem.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/sched/signal.h>
++#include <asm/page.h>
++
++#include "heap-helpers.h"
++
++struct dma_heap *sys_heap;
++
++static void system_heap_free(struct heap_helper_buffer *buffer)
++{
++ pgoff_t pg;
++
++ for (pg = 0; pg < buffer->pagecount; pg++)
++ __free_page(buffer->pages[pg]);
++ kfree(buffer->pages);
++ kfree(buffer);
++}
++
++static int system_heap_allocate(struct dma_heap *heap,
++ unsigned long len,
++ unsigned long fd_flags,
++ unsigned long heap_flags)
++{
++ struct heap_helper_buffer *helper_buffer;
++ struct dma_buf *dmabuf;
++ int ret = -ENOMEM;
++ pgoff_t pg;
++
++ helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
++ if (!helper_buffer)
++ return -ENOMEM;
++
++ init_heap_helper_buffer(helper_buffer, system_heap_free);
++ helper_buffer->heap = heap;
++ helper_buffer->size = len;
++
++ helper_buffer->pagecount = len / PAGE_SIZE;
++ helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
++ sizeof(*helper_buffer->pages),
++ GFP_KERNEL);
++ if (!helper_buffer->pages) {
++ ret = -ENOMEM;
++ goto err0;
++ }
++
++ for (pg = 0; pg < helper_buffer->pagecount; pg++) {
++ /*
++ * Avoid trying to allocate memory if the process
++ * has been killed by by SIGKILL
++ */
++ if (fatal_signal_pending(current))
++ goto err1;
++
++ helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO);
++ if (!helper_buffer->pages[pg])
++ goto err1;
++ }
++
++ /* create the dmabuf */
++ dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
++ if (IS_ERR(dmabuf)) {
++ ret = PTR_ERR(dmabuf);
++ goto err1;
++ }
++
++ helper_buffer->dmabuf = dmabuf;
++
++ ret = dma_buf_fd(dmabuf, fd_flags);
++ if (ret < 0) {
++ dma_buf_put(dmabuf);
++ /* just return, as put will call release and that will free */
++ return ret;
++ }
++
++ return ret;
++
++err1:
++ while (pg > 0)
++ __free_page(helper_buffer->pages[--pg]);
++ kfree(helper_buffer->pages);
++err0:
++ kfree(helper_buffer);
++
++ return ret;
++}
++
++static const struct dma_heap_ops system_heap_ops = {
++ .allocate = system_heap_allocate,
++};
++
++static int system_heap_create(void)
++{
++ struct dma_heap_export_info exp_info;
++ int ret = 0;
++
++ exp_info.name = "system_heap";
++ exp_info.ops = &system_heap_ops;
++ exp_info.priv = NULL;
++
++ sys_heap = dma_heap_add(&exp_info);
++ if (IS_ERR(sys_heap))
++ ret = PTR_ERR(sys_heap);
++
++ return ret;
++}
++module_init(system_heap_create);
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch b/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch
new file mode 100644
index 0000000000..d1828702b4
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch
@@ -0,0 +1,251 @@
+From d5e996267c71a9517b2c831d072e76bacb8f0e56 Mon Sep 17 00:00:00 2001
+From: John Stultz <john.stultz@linaro.org>
+Date: Tue, 3 Dec 2019 17:26:40 +0000
+Subject: [PATCH] dma-buf: heaps: Add CMA heap to dmabuf heaps
+
+Commit b61614ec318aae0c77ecd2816878d851dd61d9a6 upstream.
+
+This adds a CMA heap, which allows userspace to allocate
+a dma-buf of contiguous memory out of a CMA region.
+
+This code is an evolution of the Android ION implementation, so
+thanks to its original author and maintainters:
+ Benjamin Gaignard, Laura Abbott, and others!
+
+NOTE: This patch only adds the default CMA heap. We will enable
+selectively adding other CMA memory regions to the dmabuf heaps
+interface with a later patch (which requires a dt binding)
+
+Cc: Laura Abbott <labbott@redhat.com>
+Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Liam Mark <lmark@codeaurora.org>
+Cc: Pratik Patel <pratikp@codeaurora.org>
+Cc: Brian Starkey <Brian.Starkey@arm.com>
+Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
+Cc: Sudipto Paul <Sudipto.Paul@arm.com>
+Cc: Andrew F. Davis <afd@ti.com>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Chenbo Feng <fengc@google.com>
+Cc: Alistair Strachan <astrachan@google.com>
+Cc: Hridya Valsaraju <hridya@google.com>
+Cc: Sandeep Patil <sspatil@google.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Reviewed-by: Brian Starkey <brian.starkey@arm.com>
+Acked-by: Sandeep Patil <sspatil@android.com>
+Acked-by: Laura Abbott <labbott@redhat.com>
+Tested-by: Ayan Kumar Halder <ayan.halder@arm.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-5-john.stultz@linaro.org
+---
+ drivers/dma-buf/heaps/Kconfig | 8 ++
+ drivers/dma-buf/heaps/Makefile | 1 +
+ drivers/dma-buf/heaps/cma_heap.c | 177 +++++++++++++++++++++++++++++++
+ 3 files changed, 186 insertions(+)
+ create mode 100644 drivers/dma-buf/heaps/cma_heap.c
+
+--- a/drivers/dma-buf/heaps/Kconfig
++++ b/drivers/dma-buf/heaps/Kconfig
+@@ -4,3 +4,11 @@ config DMABUF_HEAPS_SYSTEM
+ help
+ Choose this option to enable the system dmabuf heap. The system heap
+ is backed by pages from the buddy allocator. If in doubt, say Y.
++
++config DMABUF_HEAPS_CMA
++ bool "DMA-BUF CMA Heap"
++ depends on DMABUF_HEAPS && DMA_CMA
++ help
++ Choose this option to enable dma-buf CMA heap. This heap is backed
++ by the Contiguous Memory Allocator (CMA). If your system has these
++ regions, you should say Y here.
+--- a/drivers/dma-buf/heaps/Makefile
++++ b/drivers/dma-buf/heaps/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-y += heap-helpers.o
+ obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
++obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
+--- /dev/null
++++ b/drivers/dma-buf/heaps/cma_heap.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * DMABUF CMA heap exporter
++ *
++ * Copyright (C) 2012, 2019 Linaro Ltd.
++ * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
++ */
++
++#include <linux/cma.h>
++#include <linux/device.h>
++#include <linux/dma-buf.h>
++#include <linux/dma-heap.h>
++#include <linux/dma-contiguous.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/highmem.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/scatterlist.h>
++#include <linux/sched/signal.h>
++
++#include "heap-helpers.h"
++
++struct cma_heap {
++ struct dma_heap *heap;
++ struct cma *cma;
++};
++
++static void cma_heap_free(struct heap_helper_buffer *buffer)
++{
++ struct cma_heap *cma_heap = dma_heap_get_drvdata(buffer->heap);
++ unsigned long nr_pages = buffer->pagecount;
++ struct page *cma_pages = buffer->priv_virt;
++
++ /* free page list */
++ kfree(buffer->pages);
++ /* release memory */
++ cma_release(cma_heap->cma, cma_pages, nr_pages);
++ kfree(buffer);
++}
++
++/* dmabuf heap CMA operations functions */
++static int cma_heap_allocate(struct dma_heap *heap,
++ unsigned long len,
++ unsigned long fd_flags,
++ unsigned long heap_flags)
++{
++ struct cma_heap *cma_heap = dma_heap_get_drvdata(heap);
++ struct heap_helper_buffer *helper_buffer;
++ struct page *cma_pages;
++ size_t size = PAGE_ALIGN(len);
++ unsigned long nr_pages = size >> PAGE_SHIFT;
++ unsigned long align = get_order(size);
++ struct dma_buf *dmabuf;
++ int ret = -ENOMEM;
++ pgoff_t pg;
++
++ if (align > CONFIG_CMA_ALIGNMENT)
++ align = CONFIG_CMA_ALIGNMENT;
++
++ helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
++ if (!helper_buffer)
++ return -ENOMEM;
++
++ init_heap_helper_buffer(helper_buffer, cma_heap_free);
++ helper_buffer->heap = heap;
++ helper_buffer->size = len;
++
++ cma_pages = cma_alloc(cma_heap->cma, nr_pages, align, false);
++ if (!cma_pages)
++ goto free_buf;
++
++ if (PageHighMem(cma_pages)) {
++ unsigned long nr_clear_pages = nr_pages;
++ struct page *page = cma_pages;
++
++ while (nr_clear_pages > 0) {
++ void *vaddr = kmap_atomic(page);
++
++ memset(vaddr, 0, PAGE_SIZE);
++ kunmap_atomic(vaddr);
++ /*
++ * Avoid wasting time zeroing memory if the process
++ * has been killed by by SIGKILL
++ */
++ if (fatal_signal_pending(current))
++ goto free_cma;
++
++ page++;
++ nr_clear_pages--;
++ }
++ } else {
++ memset(page_address(cma_pages), 0, size);
++ }
++
++ helper_buffer->pagecount = nr_pages;
++ helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
++ sizeof(*helper_buffer->pages),
++ GFP_KERNEL);
++ if (!helper_buffer->pages) {
++ ret = -ENOMEM;
++ goto free_cma;
++ }
++
++ for (pg = 0; pg < helper_buffer->pagecount; pg++)
++ helper_buffer->pages[pg] = &cma_pages[pg];
++
++ /* create the dmabuf */
++ dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
++ if (IS_ERR(dmabuf)) {
++ ret = PTR_ERR(dmabuf);
++ goto free_pages;
++ }
++
++ helper_buffer->dmabuf = dmabuf;
++ helper_buffer->priv_virt = cma_pages;
++
++ ret = dma_buf_fd(dmabuf, fd_flags);
++ if (ret < 0) {
++ dma_buf_put(dmabuf);
++ /* just return, as put will call release and that will free */
++ return ret;
++ }
++
++ return ret;
++
++free_pages:
++ kfree(helper_buffer->pages);
++free_cma:
++ cma_release(cma_heap->cma, cma_pages, nr_pages);
++free_buf:
++ kfree(helper_buffer);
++ return ret;
++}
++
++static const struct dma_heap_ops cma_heap_ops = {
++ .allocate = cma_heap_allocate,
++};
++
++static int __add_cma_heap(struct cma *cma, void *data)
++{
++ struct cma_heap *cma_heap;
++ struct dma_heap_export_info exp_info;
++
++ cma_heap = kzalloc(sizeof(*cma_heap), GFP_KERNEL);
++ if (!cma_heap)
++ return -ENOMEM;
++ cma_heap->cma = cma;
++
++ exp_info.name = cma_get_name(cma);
++ exp_info.ops = &cma_heap_ops;
++ exp_info.priv = cma_heap;
++
++ cma_heap->heap = dma_heap_add(&exp_info);
++ if (IS_ERR(cma_heap->heap)) {
++ int ret = PTR_ERR(cma_heap->heap);
++
++ kfree(cma_heap);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int add_default_cma_heap(void)
++{
++ struct cma *default_cma = dev_get_cma_area(NULL);
++ int ret = 0;
++
++ if (default_cma)
++ ret = __add_cma_heap(default_cma, NULL);
++
++ return ret;
++}
++module_init(add_default_cma_heap);
++MODULE_DESCRIPTION("DMA-BUF CMA Heap");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch b/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch
new file mode 100644
index 0000000000..ff4dda9a63
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch
@@ -0,0 +1,453 @@
+From 31501b71a0237f3753d0210e3e122c548d6f3051 Mon Sep 17 00:00:00 2001
+From: John Stultz <john.stultz@linaro.org>
+Date: Tue, 3 Dec 2019 17:26:41 +0000
+Subject: [PATCH] kselftests: Add dma-heap test
+
+Commit a8779927fd86c91f5400bfcbccfa018a667d8350 upstream.
+
+Add very trivial allocation and import test for dma-heaps,
+utilizing the vgem driver as a test importer.
+
+A good chunk of this code taken from:
+ tools/testing/selftests/android/ion/ionmap_test.c
+ Originally by Laura Abbott <labbott@redhat.com>
+
+Cc: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Cc: Sumit Semwal <sumit.semwal@linaro.org>
+Cc: Liam Mark <lmark@codeaurora.org>
+Cc: Pratik Patel <pratikp@codeaurora.org>
+Cc: Brian Starkey <Brian.Starkey@arm.com>
+Cc: Vincent Donnefort <Vincent.Donnefort@arm.com>
+Cc: Sudipto Paul <Sudipto.Paul@arm.com>
+Cc: Andrew F. Davis <afd@ti.com>
+Cc: Christoph Hellwig <hch@infradead.org>
+Cc: Chenbo Feng <fengc@google.com>
+Cc: Alistair Strachan <astrachan@google.com>
+Cc: Hridya Valsaraju <hridya@google.com>
+Cc: Sandeep Patil <sspatil@google.com>
+Cc: Hillf Danton <hdanton@sina.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+Reviewed-by: Brian Starkey <brian.starkey@arm.com>
+Acked-by: Sandeep Patil <sspatil@android.com>
+Acked-by: Laura Abbott <labbott@redhat.com>
+Tested-by: Ayan Kumar Halder <ayan.halder@arm.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191203172641.66642-6-john.stultz@linaro.org
+---
+ tools/testing/selftests/dmabuf-heaps/Makefile | 6 +
+ .../selftests/dmabuf-heaps/dmabuf-heap.c | 396 ++++++++++++++++++
+ 2 files changed, 402 insertions(+)
+ create mode 100644 tools/testing/selftests/dmabuf-heaps/Makefile
+ create mode 100644 tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
+
+--- /dev/null
++++ b/tools/testing/selftests/dmabuf-heaps/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++CFLAGS += -static -O3 -Wl,-no-as-needed -Wall -I../../../../usr/include
++
++TEST_GEN_PROGS = dmabuf-heap
++
++include ../lib.mk
+--- /dev/null
++++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
+@@ -0,0 +1,396 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <sys/mman.h>
++#include <sys/types.h>
++
++#include <linux/dma-buf.h>
++#include <drm/drm.h>
++
++#include "../../../../include/uapi/linux/dma-heap.h"
++
++#define DEVPATH "/dev/dma_heap"
++
++static int check_vgem(int fd)
++{
++ drm_version_t version = { 0 };
++ char name[5];
++ int ret;
++
++ version.name_len = 4;
++ version.name = name;
++
++ ret = ioctl(fd, DRM_IOCTL_VERSION, &version);
++ if (ret)
++ return 0;
++
++ return !strcmp(name, "vgem");
++}
++
++static int open_vgem(void)
++{
++ int i, fd;
++ const char *drmstr = "/dev/dri/card";
++
++ fd = -1;
++ for (i = 0; i < 16; i++) {
++ char name[80];
++
++ snprintf(name, 80, "%s%u", drmstr, i);
++
++ fd = open(name, O_RDWR);
++ if (fd < 0)
++ continue;
++
++ if (!check_vgem(fd)) {
++ close(fd);
++ fd = -1;
++ continue;
++ } else {
++ break;
++ }
++ }
++ return fd;
++}
++
++static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)
++{
++ struct drm_prime_handle import_handle = {
++ .fd = dma_buf_fd,
++ .flags = 0,
++ .handle = 0,
++ };
++ int ret;
++
++ ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
++ if (ret == 0)
++ *handle = import_handle.handle;
++ return ret;
++}
++
++static void close_handle(int vgem_fd, uint32_t handle)
++{
++ struct drm_gem_close close = {
++ .handle = handle,
++ };
++
++ ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);
++}
++
++static int dmabuf_heap_open(char *name)
++{
++ int ret, fd;
++ char buf[256];
++
++ ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);
++ if (ret < 0) {
++ printf("snprintf failed!\n");
++ return ret;
++ }
++
++ fd = open(buf, O_RDWR);
++ if (fd < 0)
++ printf("open %s failed!\n", buf);
++ return fd;
++}
++
++static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,
++ unsigned int heap_flags, int *dmabuf_fd)
++{
++ struct dma_heap_allocation_data data = {
++ .len = len,
++ .fd = 0,
++ .fd_flags = fd_flags,
++ .heap_flags = heap_flags,
++ };
++ int ret;
++
++ if (!dmabuf_fd)
++ return -EINVAL;
++
++ ret = ioctl(fd, DMA_HEAP_IOC_ALLOC, &data);
++ if (ret < 0)
++ return ret;
++ *dmabuf_fd = (int)data.fd;
++ return ret;
++}
++
++static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,
++ int *dmabuf_fd)
++{
++ return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,
++ dmabuf_fd);
++}
++
++static void dmabuf_sync(int fd, int start_stop)
++{
++ struct dma_buf_sync sync = {
++ .flags = start_stop | DMA_BUF_SYNC_RW,
++ };
++ int ret;
++
++ ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
++ if (ret)
++ printf("sync failed %d\n", errno);
++}
++
++#define ONE_MEG (1024 * 1024)
++
++static int test_alloc_and_import(char *heap_name)
++{
++ int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;
++ uint32_t handle = 0;
++ void *p = NULL;
++ int ret;
++
++ printf("Testing heap: %s\n", heap_name);
++
++ heap_fd = dmabuf_heap_open(heap_name);
++ if (heap_fd < 0)
++ return -1;
++
++ printf("Allocating 1 MEG\n");
++ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
++ if (ret) {
++ printf("Allocation Failed!\n");
++ ret = -1;
++ goto out;
++ }
++ /* mmap and write a simple pattern */
++ p = mmap(NULL,
++ ONE_MEG,
++ PROT_READ | PROT_WRITE,
++ MAP_SHARED,
++ dmabuf_fd,
++ 0);
++ if (p == MAP_FAILED) {
++ printf("mmap() failed: %m\n");
++ ret = -1;
++ goto out;
++ }
++ printf("mmap passed\n");
++
++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
++ memset(p, 1, ONE_MEG / 2);
++ memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);
++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
++
++ importer_fd = open_vgem();
++ if (importer_fd < 0) {
++ ret = importer_fd;
++ printf("Failed to open vgem\n");
++ goto out;
++ }
++
++ ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
++ if (ret < 0) {
++ printf("Failed to import buffer\n");
++ goto out;
++ }
++ printf("import passed\n");
++
++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
++ memset(p, 0xff, ONE_MEG);
++ dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
++ printf("syncs passed\n");
++
++ close_handle(importer_fd, handle);
++ ret = 0;
++
++out:
++ if (p)
++ munmap(p, ONE_MEG);
++ if (importer_fd >= 0)
++ close(importer_fd);
++ if (dmabuf_fd >= 0)
++ close(dmabuf_fd);
++ if (heap_fd >= 0)
++ close(heap_fd);
++
++ return ret;
++}
++
++/* Test the ioctl version compatibility w/ a smaller structure then expected */
++static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
++ int *dmabuf_fd)
++{
++ int ret;
++ unsigned int older_alloc_ioctl;
++ struct dma_heap_allocation_data_smaller {
++ __u64 len;
++ __u32 fd;
++ __u32 fd_flags;
++ } data = {
++ .len = len,
++ .fd = 0,
++ .fd_flags = O_RDWR | O_CLOEXEC,
++ };
++
++ older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
++ struct dma_heap_allocation_data_smaller);
++ if (!dmabuf_fd)
++ return -EINVAL;
++
++ ret = ioctl(fd, older_alloc_ioctl, &data);
++ if (ret < 0)
++ return ret;
++ *dmabuf_fd = (int)data.fd;
++ return ret;
++}
++
++/* Test the ioctl version compatibility w/ a larger structure then expected */
++static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
++ int *dmabuf_fd)
++{
++ int ret;
++ unsigned int newer_alloc_ioctl;
++ struct dma_heap_allocation_data_bigger {
++ __u64 len;
++ __u32 fd;
++ __u32 fd_flags;
++ __u64 heap_flags;
++ __u64 garbage1;
++ __u64 garbage2;
++ __u64 garbage3;
++ } data = {
++ .len = len,
++ .fd = 0,
++ .fd_flags = O_RDWR | O_CLOEXEC,
++ .heap_flags = flags,
++ .garbage1 = 0xffffffff,
++ .garbage2 = 0x88888888,
++ .garbage3 = 0x11111111,
++ };
++
++ newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
++ struct dma_heap_allocation_data_bigger);
++ if (!dmabuf_fd)
++ return -EINVAL;
++
++ ret = ioctl(fd, newer_alloc_ioctl, &data);
++ if (ret < 0)
++ return ret;
++
++ *dmabuf_fd = (int)data.fd;
++ return ret;
++}
++
++static int test_alloc_compat(char *heap_name)
++{
++ int heap_fd = -1, dmabuf_fd = -1;
++ int ret;
++
++ heap_fd = dmabuf_heap_open(heap_name);
++ if (heap_fd < 0)
++ return -1;
++
++ printf("Testing (theoretical)older alloc compat\n");
++ ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
++ if (ret) {
++ printf("Older compat allocation failed!\n");
++ ret = -1;
++ goto out;
++ }
++ close(dmabuf_fd);
++
++ printf("Testing (theoretical)newer alloc compat\n");
++ ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
++ if (ret) {
++ printf("Newer compat allocation failed!\n");
++ ret = -1;
++ goto out;
++ }
++ printf("Ioctl compatibility tests passed\n");
++out:
++ if (dmabuf_fd >= 0)
++ close(dmabuf_fd);
++ if (heap_fd >= 0)
++ close(heap_fd);
++
++ return ret;
++}
++
++static int test_alloc_errors(char *heap_name)
++{
++ int heap_fd = -1, dmabuf_fd = -1;
++ int ret;
++
++ heap_fd = dmabuf_heap_open(heap_name);
++ if (heap_fd < 0)
++ return -1;
++
++ printf("Testing expected error cases\n");
++ ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
++ if (!ret) {
++ printf("Did not see expected error (invalid fd)!\n");
++ ret = -1;
++ goto out;
++ }
++
++ ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
++ if (!ret) {
++ printf("Did not see expected error (invalid heap flags)!\n");
++ ret = -1;
++ goto out;
++ }
++
++ ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
++ ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
++ if (!ret) {
++ printf("Did not see expected error (invalid fd flags)!\n");
++ ret = -1;
++ goto out;
++ }
++
++ printf("Expected error checking passed\n");
++out:
++ if (dmabuf_fd >= 0)
++ close(dmabuf_fd);
++ if (heap_fd >= 0)
++ close(heap_fd);
++
++ return ret;
++}
++
++int main(void)
++{
++ DIR *d;
++ struct dirent *dir;
++ int ret = -1;
++
++ d = opendir(DEVPATH);
++ if (!d) {
++ printf("No %s directory?\n", DEVPATH);
++ return -1;
++ }
++
++ while ((dir = readdir(d)) != NULL) {
++ if (!strncmp(dir->d_name, ".", 2))
++ continue;
++ if (!strncmp(dir->d_name, "..", 3))
++ continue;
++
++ ret = test_alloc_and_import(dir->d_name);
++ if (ret)
++ break;
++
++ ret = test_alloc_compat(dir->d_name);
++ if (ret)
++ break;
++
++ ret = test_alloc_errors(dir->d_name);
++ if (ret)
++ break;
++ }
++ closedir(d);
++
++ return ret;
++}
diff --git a/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch b/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch
new file mode 100644
index 0000000000..a949831525
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch
@@ -0,0 +1,69 @@
+From 8153056fa1d45394057017843070d3a366dbd918 Mon Sep 17 00:00:00 2001
+From: "Andrew F. Davis" <afd@ti.com>
+Date: Mon, 16 Dec 2019 08:34:04 -0500
+Subject: [PATCH] dma-buf: heaps: Use _IOCTL_ for userspace IOCTL
+ identifier
+
+Commit b3b4346544b571c96d46be615b9db69a601ce4c8 upstream.
+
+This is more consistent with the DMA and DRM frameworks convention. This
+patch is only a name change, no logic is changed.
+
+Signed-off-by: Andrew F. Davis <afd@ti.com>
+Acked-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191216133405.1001-2-afd@ti.com
+---
+ drivers/dma-buf/dma-heap.c | 4 ++--
+ include/uapi/linux/dma-heap.h | 4 ++--
+ tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c | 2 +-
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/dma-buf/dma-heap.c
++++ b/drivers/dma-buf/dma-heap.c
+@@ -107,7 +107,7 @@ static long dma_heap_ioctl_allocate(stru
+ }
+
+ unsigned int dma_heap_ioctl_cmds[] = {
+- DMA_HEAP_IOC_ALLOC,
++ DMA_HEAP_IOCTL_ALLOC,
+ };
+
+ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
+@@ -153,7 +153,7 @@ static long dma_heap_ioctl(struct file *
+ memset(kdata + in_size, 0, ksize - in_size);
+
+ switch (kcmd) {
+- case DMA_HEAP_IOC_ALLOC:
++ case DMA_HEAP_IOCTL_ALLOC:
+ ret = dma_heap_ioctl_allocate(file, kdata);
+ break;
+ default:
+--- a/include/uapi/linux/dma-heap.h
++++ b/include/uapi/linux/dma-heap.h
+@@ -42,12 +42,12 @@ struct dma_heap_allocation_data {
+ #define DMA_HEAP_IOC_MAGIC 'H'
+
+ /**
+- * DOC: DMA_HEAP_IOC_ALLOC - allocate memory from pool
++ * DOC: DMA_HEAP_IOCTL_ALLOC - allocate memory from pool
+ *
+ * Takes a dma_heap_allocation_data struct and returns it with the fd field
+ * populated with the dmabuf handle of the allocation.
+ */
+-#define DMA_HEAP_IOC_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\
++#define DMA_HEAP_IOCTL_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,\
+ struct dma_heap_allocation_data)
+
+ #endif /* _UAPI_LINUX_DMABUF_POOL_H */
+--- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
++++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
+@@ -116,7 +116,7 @@ static int dmabuf_heap_alloc_fdflags(int
+ if (!dmabuf_fd)
+ return -EINVAL;
+
+- ret = ioctl(fd, DMA_HEAP_IOC_ALLOC, &data);
++ ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
+ if (ret < 0)
+ return ret;
+ *dmabuf_fd = (int)data.fd;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch b/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch
new file mode 100644
index 0000000000..288f468a78
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch
@@ -0,0 +1,28 @@
+From 0b5efcbb99c7b36f84cf8f8f4b582d88ccb9cc35 Mon Sep 17 00:00:00 2001
+From: "Andrew F. Davis" <afd@ti.com>
+Date: Mon, 16 Dec 2019 08:34:05 -0500
+Subject: [PATCH] dma-buf: heaps: Remove redundant heap identifier from
+ system heap name
+
+The heaps are already in a directory of heaps, adding _heap to a heap
+name is redundant. This patch is only a name change, no logic is changed.
+
+Signed-off-by: Andrew F. Davis <afd@ti.com>
+Acked-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191216133405.1001-3-afd@ti.com
+---
+ drivers/dma-buf/heaps/system_heap.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/dma-buf/heaps/system_heap.c
++++ b/drivers/dma-buf/heaps/system_heap.c
+@@ -109,7 +109,7 @@ static int system_heap_create(void)
+ struct dma_heap_export_info exp_info;
+ int ret = 0;
+
+- exp_info.name = "system_heap";
++ exp_info.name = "system";
+ exp_info.ops = &system_heap_ops;
+ exp_info.priv = NULL;
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch b/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch
new file mode 100644
index 0000000000..89b66956e0
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch
@@ -0,0 +1,34 @@
+From 0960432c8261efb05bcf5ba6d8fe13c8293086a9 Mon Sep 17 00:00:00 2001
+From: Colin Ian King <colin.king@canonical.com>
+Date: Mon, 16 Dec 2019 16:10:59 +0000
+Subject: [PATCH] dma-buf: fix resource leak on -ENOTTY error return
+ path
+
+Commit f9d3b2c600075d1f79efcd5cdb1718c2f554c0f9 upstream.
+
+The -ENOTTY error return path does not free the allocated
+kdata as it returns directly. Fix this by returning via the
+error handling label err.
+
+Addresses-Coverity: ("Resource leak")
+Fixes: c02a81fba74f ("dma-buf: Add dma-buf heaps framework")
+Signed-off-by: Colin Ian King <colin.king@canonical.com>
+Acked-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191216161059.269492-1-colin.king@canonical.com
+---
+ drivers/dma-buf/dma-heap.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma-buf/dma-heap.c
++++ b/drivers/dma-buf/dma-heap.c
+@@ -157,7 +157,8 @@ static long dma_heap_ioctl(struct file *
+ ret = dma_heap_ioctl_allocate(file, kdata);
+ break;
+ default:
+- return -ENOTTY;
++ ret = -ENOTTY;
++ goto err;
+ }
+
+ if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch b/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch
new file mode 100644
index 0000000000..900637ddeb
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch
@@ -0,0 +1,34 @@
+From d59927ce7f8a4ee9abcad9bd3405881c7a9ac99e Mon Sep 17 00:00:00 2001
+From: zhong jiang <zhongjiang@huawei.com>
+Date: Wed, 18 Dec 2019 00:38:22 +0530
+Subject: [PATCH] dma-heap: Make the symbol 'dma_heap_ioctl_cmds'
+ static
+
+Commit 7d411afe8444060454a53b1f9b70ee78b3e75ef1 upstream.
+
+Fix the following sparse warning.
+
+drivers/dma-buf/dma-heap.c:109:14: warning: symbol 'dma_heap_ioctl_cmds'
+was not declared. Should it be static?
+
+Acked-by: Andrew F. Davis <afd@ti.com>
+Acked-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: zhong jiang <zhongjiang@huawei.com>
+Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
+ [sumits: rebased over IOCTL rename patches]
+Link: https://patchwork.freedesktop.org/patch/msgid/20191217190822.1969-1-sumit.semwal@linaro.org
+---
+ drivers/dma-buf/dma-heap.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/dma-buf/dma-heap.c
++++ b/drivers/dma-buf/dma-heap.c
+@@ -106,7 +106,7 @@ static long dma_heap_ioctl_allocate(stru
+ return 0;
+ }
+
+-unsigned int dma_heap_ioctl_cmds[] = {
++static unsigned int dma_heap_ioctl_cmds[] = {
+ DMA_HEAP_IOCTL_ALLOC,
+ };
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch b/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch
new file mode 100644
index 0000000000..f319bdca9f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch
@@ -0,0 +1,25 @@
+From e2882043cba45fd9f027dfc102aaaaf1208a65d0 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 6 May 2020 17:02:26 +0100
+Subject: [PATCH] ARM: dts: Enable firmware-clocks on all Pis
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm270x.dtsi | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm270x.dtsi
++++ b/arch/arm/boot/dts/bcm270x.dtsi
+@@ -7,6 +7,12 @@
+ /delete-property/ stdout-path;
+ };
+
++ firmware_clocks: firmware-clocks {
++ compatible = "raspberrypi,firmware-clocks";
++ raspberrypi,firmware = <&firmware>;
++ #clock-cells = <1>;
++ };
++
+ soc: soc {
+
+ watchdog: watchdog@7e100000 {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch b/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch
new file mode 100644
index 0000000000..378824c495
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch
@@ -0,0 +1,51 @@
+From b52dee833768d1cb3572bf8269baeda077d0e41b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 13 May 2020 18:28:27 +0100
+Subject: [PATCH] media: bcm2835-unicam: Always service interrupts
+
+From when bringing up the driver, there was a check in the isr
+to ignore interrupts (claiming them handled) should the driver
+not be streaming.
+
+The VPU now will not register a camera driver if it finds a
+CSI2 node enabled in device tree, therefore this flawed check is
+redundant.
+
+https://github.com/raspberrypi/linux/issues/3602
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/platform/bcm2835/bcm2835-unicam.c | 15 ---------------
+ 1 file changed, 15 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -766,12 +766,6 @@ static int unicam_all_nodes_streaming(st
+ return ret;
+ }
+
+-static int unicam_all_nodes_disabled(struct unicam_device *dev)
+-{
+- return !dev->node[IMAGE_PAD].streaming &&
+- !dev->node[METADATA_PAD].streaming;
+-}
+-
+ static void unicam_queue_event_sof(struct unicam_device *unicam)
+ {
+ struct v4l2_event event = {
+@@ -801,15 +795,6 @@ static irqreturn_t unicam_isr(int irq, v
+ u64 ts;
+ int i;
+
+- /*
+- * Don't service interrupts if not streaming.
+- * Avoids issues if the VPU should enable the
+- * peripheral without the kernel knowing (that
+- * shouldn't happen, but causes issues if it does).
+- */
+- if (unicam_all_nodes_disabled(unicam))
+- return IRQ_HANDLED;
+-
+ sta = reg_read(cfg, UNICAM_STA);
+ /* Write value back to clear the interrupts */
+ reg_write(cfg, UNICAM_STA, sta);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch b/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch
new file mode 100644
index 0000000000..3ff77bcea9
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch
@@ -0,0 +1,70 @@
+From 41ed4262b7398a3170399af81cf78cb8d7dd8b8d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 13 May 2020 20:10:15 +0100
+Subject: [PATCH] sc16is7xx: Fix for hardware flow control
+
+The SC16IS7XX hardware flow control is mishandled by the driver in
+a number of ways:
+
+ 1. The set_baud method accidentally clears it when setting EFR bit.
+ 2. Even though hardware flow control is enabled, it isn't indicated
+ back to the serial framework.
+ 3. Applying the flow control clears the EFR bit.
+ 4. The CTS support is not indicated in the return from
+ sc16is7xx_get_mctrl.
+
+Address all of those issues using a mixture of patches found on the
+linked pages.
+
+See: https://github.com/raspberrypi/linux/issues/2542
+See: https://www.spinics.net/lists/linux-serial/msg21794.html
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/sc16is7xx.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -523,8 +523,9 @@ static int sc16is7xx_set_baud(struct uar
+
+ /* Enable enhanced features */
+ regcache_cache_bypass(s->regmap, true);
+- sc16is7xx_port_write(port, SC16IS7XX_EFR_REG,
+- SC16IS7XX_EFR_ENABLE_BIT);
++ sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
++ SC16IS7XX_EFR_ENABLE_BIT,
++ SC16IS7XX_EFR_ENABLE_BIT);
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Put LCR back to the normal mode */
+@@ -846,7 +847,7 @@ static unsigned int sc16is7xx_get_mctrl(
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+- return TIOCM_DSR | TIOCM_CAR;
++ return TIOCM_DSR | TIOCM_CAR | TIOCM_RI | TIOCM_CTS;
+ }
+
+ static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+@@ -933,14 +934,19 @@ static void sc16is7xx_set_termios(struct
+ regcache_cache_bypass(s->regmap, true);
+ sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
+ sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
+- if (termios->c_cflag & CRTSCTS)
++ if (termios->c_cflag & CRTSCTS) {
+ flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
+ SC16IS7XX_EFR_AUTORTS_BIT;
++ port->status |= UPSTAT_AUTOCTS;
++ };
+ if (termios->c_iflag & IXON)
+ flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
+ if (termios->c_iflag & IXOFF)
+ flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
+
++ /* Always set enable enhanced */
++ flow |= SC16IS7XX_EFR_ENABLE_BIT;
++
+ sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow);
+ regcache_cache_bypass(s->regmap, false);
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch b/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch
new file mode 100644
index 0000000000..611e5db743
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch
@@ -0,0 +1,58 @@
+From a90dcdf7cf7ad632f5a260758a76e406403a7e3c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 14 May 2020 14:44:15 +0100
+Subject: [PATCH] drm/vc4: Fix VIC usage with Broadcast RGB
+
+Adding the Broadcast RGB range selection broke the VIC
+field of the AVI infoframes on HDMI, zeroing them for all
+modes on an HDMI monitor.
+
+Correct this so that it is only zeroed if the range is
+contrary to the standard range of the mode.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 19 ++++++++++++-------
+ 1 file changed, 12 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -936,19 +936,14 @@ static void vc4_crtc_mode_set_nofb(struc
+ break;
+ }
+
++ mb.timings.video_id_code = frame.avi.video_code;
++
+ if (!vc4_encoder->hdmi_monitor) {
+ mb.timings.flags |= TIMINGS_FLAGS_DVI;
+- mb.timings.video_id_code = frame.avi.video_code;
+ } else {
+ struct vc4_fkms_connector_state *conn_state =
+ to_vc4_fkms_connector_state(vc4_crtc->connector->state);
+
+- /* Do not provide a VIC as the HDMI spec requires that we do not
+- * signal the opposite of the defined range in the AVI
+- * infoframe.
+- */
+- mb.timings.video_id_code = 0;
+-
+ if (conn_state->broadcast_rgb == VC4_BROADCAST_RGB_AUTO) {
+ /* See CEA-861-E - 5.1 Default Encoding Parameters */
+ if (drm_default_rgb_quant_range(mode) ==
+@@ -958,6 +953,16 @@ static void vc4_crtc_mode_set_nofb(struc
+ if (conn_state->broadcast_rgb ==
+ VC4_BROADCAST_RGB_LIMITED)
+ mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
++
++ /* If not using the default range, then do not provide
++ * a VIC as the HDMI spec requires that we do not
++ * signal the opposite of the defined range in the AVI
++ * infoframe.
++ */
++ if (!!(mb.timings.flags & TIMINGS_FLAGS_RGB_LIMITED) !=
++ (drm_default_rgb_quant_range(mode) ==
++ HDMI_QUANTIZATION_RANGE_LIMITED))
++ mb.timings.video_id_code = 0;
+ }
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch b/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch
new file mode 100644
index 0000000000..fd878ef737
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch
@@ -0,0 +1,28 @@
+From d121fed62ecf09a60c98fb5e8cb54596a1794622 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 15 May 2020 13:42:10 +0100
+Subject: [PATCH] staging: vc04_services: mmal-vchiq: Update parameters
+ list
+
+Adds in a couple of new MMAL parameter defines.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
++++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h
+@@ -668,6 +668,12 @@ enum mmal_parameter_video_type {
+
+ /**< Takes a @ref MMAL_PARAMETER_UINT32_T */
+ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAME_LENGTH,
++
++ /**< Take a @ref MMAL_PARAMETER_VIDEO_STALL_T */
++ MMAL_PARAMETER_VIDEO_STALL_THRESHOLD,
++
++ /**< Take a @ref MMAL_PARAMETER_BOOLEAN_T */
++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+ };
+
+ /** Valid mirror modes */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch b/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch
new file mode 100644
index 0000000000..7698f822d3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch
@@ -0,0 +1,29 @@
+From 2822134c5f5c03893706df64625f3390792bb68a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 15 May 2020 13:43:08 +0100
+Subject: [PATCH] staging:vc04_services: bcm2835-codec: Request headers
+ with I-frame
+
+V4L2 wishes to have the codec header bytes in the same buffer as the
+first encoded frame, so it does become 1-in 1-out for encoding.
+The firmware now has an option to do this, so enable it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -1967,6 +1967,11 @@ static int bcm2835_codec_create_componen
+ MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING,
+ &param, sizeof(param));
+
++ /* Enable inserting headers into the first frame */
++ vchiq_mmal_port_parameter_set(ctx->dev->instance,
++ &ctx->component->control,
++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
++ &param, sizeof(param));
+ } else {
+ if (ctx->q_data[V4L2_M2M_DST].sizeimage <
+ ctx->component->output[0].minimum_buffer.size)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch b/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch
new file mode 100644
index 0000000000..da273ca65b
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch
@@ -0,0 +1,32 @@
+From 39956d31d19658af7a0759e05366d4bc1c04a50a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 15 May 2020 13:47:13 +0100
+Subject: [PATCH] staging:vc04_services: bcm2835-codec: Avoid
+ fragmenting buffers
+
+The firmware by default is quite happy to fragment encoded
+frames as the original MMAL and IL APIs support this.
+V4L2 doesn't, so we need to enable the firmware option to avoid this.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -1972,6 +1972,14 @@ static int bcm2835_codec_create_componen
+ &ctx->component->control,
+ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
+ &param, sizeof(param));
++ /*
++ * Avoid fragmenting the buffers over multiple frames (unless
++ * the frame is bigger than the whole buffer)
++ */
++ vchiq_mmal_port_parameter_set(ctx->dev->instance,
++ &ctx->component->control,
++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION,
++ &param, sizeof(param));
+ } else {
+ if (ctx->q_data[V4L2_M2M_DST].sizeimage <
+ ctx->component->output[0].minimum_buffer.size)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch b/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch
new file mode 100644
index 0000000000..5d400e2530
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch
@@ -0,0 +1,30 @@
+From 1dbe31a80537e398865442562fd21d4cb3fa6dcc Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 15 May 2020 13:48:59 +0100
+Subject: [PATCH] staging:vc04_services: bcm2835-camera: Request
+ headers with I-frame
+
+V4L2 wishes to have the codec header bytes in the same buffer as the
+first encoded frame, so it does become 1-in 1-out for encoding.
+The firmware now has an option to do this, so enable it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../staging/vc04_services/bcm2835-camera/bcm2835-camera.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
++++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c
+@@ -1762,6 +1762,12 @@ static int mmal_init(struct bm2835_mmal_
+ MMAL_PARAMETER_MINIMISE_FRAGMENTATION,
+ &enable,
+ sizeof(enable));
++
++ /* Enable inserting headers into the first frame */
++ vchiq_mmal_port_parameter_set(dev->instance,
++ &dev->component[COMP_VIDEO_ENCODE]->control,
++ MMAL_PARAMETER_VIDEO_ENCODE_HEADERS_WITH_FRAME,
++ &enable, sizeof(enable));
+ }
+ ret = bm2835_mmal_set_all_camera_controls(dev);
+ if (ret < 0) {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch b/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch
new file mode 100644
index 0000000000..4e69e41710
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch
@@ -0,0 +1,25 @@
+From 8882c25c4e92de78d767c541d1115c726f538c77 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 18 May 2020 09:46:48 +0100
+Subject: [PATCH] overlays: Fix audio parameter of vc4-kms-v3d
+
+The CMA handling change broke the audio parameter - the fragment
+numbering has changed - so fix it.
+
+See: https://github.com/raspberrypi/linux/issues/2489
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
+@@ -109,6 +109,6 @@
+ };
+
+ __overrides__ {
+- audio = <0>,"!17";
++ audio = <0>,"!13";
+ };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch b/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch
new file mode 100644
index 0000000000..b104f00efc
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch
@@ -0,0 +1,38 @@
+From 2e5f704305c97c5ae26420f61c0242c151c91533 Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@i2audio.com>
+Date: Tue, 19 May 2020 13:56:17 +0200
+Subject: [PATCH] Switch to snd_soc_dai_set_bclk_ratio Replaces
+ obsolete function snd_soc_dai_set_tdm_slot
+
+Signed-off-by: Joerg Schambacher <joerg@i2audio.com>
+---
+ sound/soc/bcm/hifiberry_dacplusadcpro.c | 13 +++----------
+ 1 file changed, 3 insertions(+), 10 deletions(-)
+
+--- a/sound/soc/bcm/hifiberry_dacplusadcpro.c
++++ b/sound/soc/bcm/hifiberry_dacplusadcpro.c
+@@ -406,21 +406,14 @@ static int snd_rpi_hifiberry_dacplusadcp
+ return ret;
+ }
+
+- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x03, 0x03,
+- channels, width);
++ ret = snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, channels * width);
+ if (ret)
+ return ret;
+- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[0], 0x03, 0x03,
+- channels, width);
++ ret = snd_soc_dai_set_bclk_ratio(rtd->codec_dais[0], channels * width);
+ if (ret)
+ return ret;
+- ret = snd_soc_dai_set_tdm_slot(rtd->codec_dais[1], 0x03, 0x03,
+- channels, width);
+- if (ret)
+- return ret;
+-
+ if (snd_rpi_hifiberry_is_dacpro && ops->hw_params)
+- ret = ops->hw_params(substream, params, dai);
++ ret = ops->hw_params(substream, params, dai);
+ return ret;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch b/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch
new file mode 100644
index 0000000000..1e0051be30
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch
@@ -0,0 +1,48 @@
+From de4d7e44c08c2768de4b638af09072c80f1c3fa1 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 19 May 2020 11:46:47 +0100
+Subject: [PATCH] media: bcm2835-unicam: Retain packing information on
+ G_FMT
+
+The change to retrieve the pixel format always on g_fmt didn't
+check whether the native or unpacked version of the format
+had been requested, and always returned the packed one.
+Correct this so that the packing setting is retained whereever
+possible.
+
+Fixes "9d59e89 media: bcm2835-unicam: Re-fetch mbus code from subdev
+on a g_fmt call"
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -974,8 +974,23 @@ static int unicam_g_fmt_vid_cap(struct f
+ if (!fmt)
+ return -EINVAL;
+
+- node->fmt = fmt;
+- node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
++ if (node->fmt != fmt) {
++ /*
++ * The sensor format has changed so the pixelformat needs to
++ * be updated. Try and retain the packed/unpacked choice if
++ * at all possible.
++ */
++ if (node->fmt->repacked_fourcc ==
++ node->v_fmt.fmt.pix.pixelformat)
++ /* Using the repacked format */
++ node->v_fmt.fmt.pix.pixelformat = fmt->repacked_fourcc;
++ else
++ /* Using the native format */
++ node->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
++
++ node->fmt = fmt;
++ }
++
+ *f = node->v_fmt;
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch b/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch
new file mode 100644
index 0000000000..124a1d95cd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch
@@ -0,0 +1,115 @@
+From 66e0ea531f4975fed5899a2cbbfa3986fca40680 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 5 May 2020 15:23:32 +0100
+Subject: [PATCH] zswap: Defer zswap initialisation
+
+Enabling zswap support in the kernel configuration costs about 1.5MB
+of RAM, even when zswap is not enabled at runtime. This cost can be
+reduced significantly by deferring initialisation (including pool
+creation) until the "enabled" parameter is set to true. There is a
+small cost to this in that some initialisation code has to remain in
+memory after the init phase, just in case they are needed later,
+but the total size increase is negligible.
+
+See: https://github.com/raspberrypi/linux/pull/3432
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ mm/zswap.c | 48 ++++++++++++++++++++++++++++--------------------
+ 1 file changed, 28 insertions(+), 20 deletions(-)
+
+--- a/mm/zswap.c
++++ b/mm/zswap.c
+@@ -564,8 +564,9 @@ error:
+ return NULL;
+ }
+
+-static __init struct zswap_pool *__zswap_pool_create_fallback(void)
++static bool zswap_try_pool_create(void)
+ {
++ struct zswap_pool *pool;
+ bool has_comp, has_zpool;
+
+ has_comp = crypto_has_comp(zswap_compressor, 0, 0);
+@@ -599,9 +600,21 @@ static __init struct zswap_pool *__zswap
+ }
+
+ if (!has_comp || !has_zpool)
+- return NULL;
++ return false;
++
++ pool = zswap_pool_create(zswap_zpool_type, zswap_compressor);
++
++ if (pool) {
++ pr_info("loaded using pool %s/%s\n", pool->tfm_name,
++ zpool_get_type(pool->zpool));
++ list_add(&pool->list, &zswap_pools);
++ zswap_has_pool = true;
++ } else {
++ pr_err("pool creation failed\n");
++ zswap_enabled = false;
++ }
+
+- return zswap_pool_create(zswap_zpool_type, zswap_compressor);
++ return zswap_enabled;
+ }
+
+ static void zswap_pool_destroy(struct zswap_pool *pool)
+@@ -773,16 +786,19 @@ static int zswap_zpool_param_set(const c
+ static int zswap_enabled_param_set(const char *val,
+ const struct kernel_param *kp)
+ {
++ int ret;
++
+ if (zswap_init_failed) {
+ pr_err("can't enable, initialization failed\n");
+ return -ENODEV;
+ }
+- if (!zswap_has_pool && zswap_init_started) {
+- pr_err("can't enable, no pool configured\n");
+- return -ENODEV;
+- }
+
+- return param_set_bool(val, kp);
++ ret = param_set_bool(val, kp);
++ if (!ret && zswap_enabled && zswap_init_started && !zswap_has_pool)
++ if (!zswap_try_pool_create())
++ ret = -ENODEV;
++
++ return ret;
+ }
+
+ /*********************************
+@@ -1297,7 +1313,6 @@ static void __exit zswap_debugfs_exit(vo
+ **********************************/
+ static int __init init_zswap(void)
+ {
+- struct zswap_pool *pool;
+ int ret;
+
+ zswap_init_started = true;
+@@ -1321,20 +1336,13 @@ static int __init init_zswap(void)
+ if (ret)
+ goto hp_fail;
+
+- pool = __zswap_pool_create_fallback();
+- if (pool) {
+- pr_info("loaded using pool %s/%s\n", pool->tfm_name,
+- zpool_get_type(pool->zpool));
+- list_add(&pool->list, &zswap_pools);
+- zswap_has_pool = true;
+- } else {
+- pr_err("pool creation failed\n");
+- zswap_enabled = false;
+- }
+-
+ frontswap_register_ops(&zswap_frontswap_ops);
+ if (zswap_debugfs_init())
+ pr_warn("debugfs initialization failed\n");
++
++ if (zswap_enabled)
++ zswap_try_pool_create();
++
+ return 0;
+
+ hp_fail:
diff --git a/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch b/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch
new file mode 100644
index 0000000000..2c9449df0f
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch
@@ -0,0 +1,54 @@
+From 13c11f40ded81f258178936e9fa22788e59b82cf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 19 May 2020 14:54:28 +0100
+Subject: [PATCH] drm/vc4: Adopt the dma configuration from the HVS or
+ V3D component
+
+vc4_drv isn't necessarily under the /soc node in DT as it is a
+virtual device, but it is the one that does the allocations.
+The DMA addresses are consumed by primarily the HVS or V3D, and
+those require VideoCore cache alias address mapping, and so will be
+under /soc.
+
+During probe find the a suitable device node for HVS or V3D,
+and adopt the DMA configuration of that node.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -249,6 +249,14 @@ static void vc4_match_add_drivers(struct
+ }
+ }
+
++const struct of_device_id vc4_dma_range_matches[] = {
++ { .compatible = "brcm,bcm2835-hvs" },
++ { .compatible = "brcm,bcm2835-v3d" },
++ { .compatible = "brcm,cygnus-v3d" },
++ { .compatible = "brcm,vc4-v3d" },
++ {}
++};
++
+ static int vc4_drm_bind(struct device *dev)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+@@ -269,6 +277,16 @@ static int vc4_drm_bind(struct device *d
+ vc4_drm_driver.driver_features &= ~DRIVER_RENDER;
+ of_node_put(node);
+
++ node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
++ NULL);
++ if (node) {
++ ret = of_dma_configure(dev, node, true);
++ of_node_put(node);
++
++ if (ret)
++ return ret;
++ }
++
+ drm = drm_dev_alloc(&vc4_drm_driver, dev);
+ if (IS_ERR(drm))
+ return PTR_ERR(drm);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch b/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch
new file mode 100644
index 0000000000..127c5aef11
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch
@@ -0,0 +1,27 @@
+From 971a2bb14b459819db1bda8fcdf953e493242b42 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 19 May 2020 16:20:30 +0100
+Subject: [PATCH] drm/vc4: Add FKMS as an acceptable node for dma
+ ranges.
+
+Under FKMS, the firmware (via FKMS) also requires the VideoCore cache
+aliases for image planes, as defined by the dma-ranges under /soc.
+
+Add rpi-firmware-kms to the list of acceptable nodes to look for
+to copy dma config from.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -251,6 +251,7 @@ static void vc4_match_add_drivers(struct
+
+ const struct of_device_id vc4_dma_range_matches[] = {
+ { .compatible = "brcm,bcm2835-hvs" },
++ { .compatible = "raspberrypi,rpi-firmware-kms" },
+ { .compatible = "brcm,bcm2835-v3d" },
+ { .compatible = "brcm,cygnus-v3d" },
+ { .compatible = "brcm,vc4-v3d" },
diff --git a/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch b/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch
new file mode 100644
index 0000000000..4585d2d8dd
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch
@@ -0,0 +1,25 @@
+From 7048ac9fd3b918d83a71caf5c94bb061a2866794 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 19 May 2020 16:56:33 +0100
+Subject: [PATCH] media: i2c: imx477: Return correct result on sensor
+ id verification
+
+The test should return -EIO if the register read id does not match
+the expected sensor id.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -1919,7 +1919,7 @@ static int imx477_identify_module(struct
+ if (val != IMX477_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+ IMX477_CHIP_ID, val);
+- ret = -EINVAL;
++ return -EIO;
+ }
+
+ return 0;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch
new file mode 100644
index 0000000000..1dac504877
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch
@@ -0,0 +1,154 @@
+From d8cbdaa729d5d3e9a1c18150bf4de69335a85a40 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 20 May 2020 16:36:33 +0100
+Subject: [PATCH] staging: vchiq_arm: Clean up 40-bit DMA support
+
+Manage the split between addresses for the VPU and addresses for the
+40-bit DMA controller with a dedicated DMA device pointer that on non-
+BCM2711 platforms is the same as the main VCHIQ device. This allows
+the VCHIQ node to stay in the usual place in the DT, and removes the
+ugly VC_SAFE macros.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../interface/vchiq_arm/vchiq_2835_arm.c | 39 ++++++++++++-------
+ .../interface/vchiq_arm/vchiq_arm.c | 14 -------
+ 2 files changed, 25 insertions(+), 28 deletions(-)
+
+--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+@@ -16,8 +16,6 @@
+ #include <soc/bcm2835/raspberrypi-firmware.h>
+
+ #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32)
+-#define VC_SAFE(x) (g_use_36bit_addrs ? ((u32)(x) | 0xc0000000) : (u32)(x))
+-#define IS_VC_SAFE(x) (g_use_36bit_addrs ? !((x) & ~0x3fffffffull) : 1)
+
+ #include "vchiq_arm.h"
+ #include "vchiq_connected.h"
+@@ -71,6 +69,7 @@ static char *g_fragments_base;
+ static char *g_free_fragments;
+ static struct semaphore g_free_fragments_sema;
+ static struct device *g_dev;
++static struct device *g_dma_dev;
+
+ static DEFINE_SEMAPHORE(g_free_fragments_mutex);
+
+@@ -87,6 +86,7 @@ free_pagelist(struct vchiq_pagelist_info
+ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state)
+ {
+ struct device *dev = &pdev->dev;
++ struct device *dma_dev = NULL;
+ struct vchiq_drvdata *drvdata = platform_get_drvdata(pdev);
+ struct rpi_firmware *fw = drvdata->fw;
+ struct vchiq_slot_zero *vchiq_slot_zero;
+@@ -109,7 +109,23 @@ int vchiq_platform_init(struct platform_
+ g_cache_line_size = drvdata->cache_line_size;
+ g_fragments_size = 2 * g_cache_line_size;
+
+- g_use_36bit_addrs = (dev->dma_pfn_offset == 0);
++ if (drvdata->use_36bit_addrs) {
++ struct device_node *dma_node =
++ of_find_compatible_node(NULL, NULL, "brcm,bcm2711-dma");
++
++ if (dma_node) {
++ struct platform_device *pdev;
++
++ pdev = of_find_device_by_node(dma_node);
++ if (pdev)
++ dma_dev = &pdev->dev;
++ of_node_put(dma_node);
++ g_use_36bit_addrs = true;
++ } else {
++ dev_err(dev, "40-bit DMA controller not found\n");
++ return -EINVAL;
++ }
++ }
+
+ /* Allocate space for the channels in coherent memory */
+ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE);
+@@ -122,14 +138,8 @@ int vchiq_platform_init(struct platform_
+ return -ENOMEM;
+ }
+
+- if (!IS_VC_SAFE(slot_phys)) {
+- dev_err(dev, "allocated DMA memory %pad is not VC-safe\n",
+- &slot_phys);
+- return -ENOMEM;
+- }
+-
+ WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0);
+- channelbase = VC_SAFE(slot_phys);
++ channelbase = slot_phys;
+
+ vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size);
+ if (!vchiq_slot_zero)
+@@ -178,6 +188,7 @@ int vchiq_platform_init(struct platform_
+ }
+
+ g_dev = dev;
++ g_dma_dev = dma_dev ?: dev;
+ g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev,
+ VCHIQ_DMA_POOL_SIZE, g_cache_line_size,
+ 0);
+@@ -255,7 +266,7 @@ vchiq_prepare_bulk_data(struct vchiq_bul
+ if (!pagelistinfo)
+ return VCHIQ_ERROR;
+
+- bulk->data = (void *)(uintptr_t)VC_SAFE(pagelistinfo->dma_addr);
++ bulk->data = (void *)(uintptr_t)pagelistinfo->dma_addr;
+
+ /*
+ * Store the pagelistinfo address in remote_data,
+@@ -354,7 +365,7 @@ static void
+ cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo)
+ {
+ if (pagelistinfo->scatterlist_mapped) {
+- dma_unmap_sg(g_dev, pagelistinfo->scatterlist,
++ dma_unmap_sg(g_dma_dev, pagelistinfo->scatterlist,
+ pagelistinfo->num_pages, pagelistinfo->dma_dir);
+ }
+
+@@ -519,7 +530,7 @@ create_pagelist(char __user *buf, size_t
+ count -= len;
+ }
+
+- dma_buffers = dma_map_sg(g_dev,
++ dma_buffers = dma_map_sg(g_dma_dev,
+ scatterlist,
+ num_pages,
+ pagelistinfo->dma_dir);
+@@ -569,7 +580,7 @@ create_pagelist(char __user *buf, size_t
+ } else {
+ for_each_sg(scatterlist, sg, dma_buffers, i) {
+ u32 len = sg_dma_len(sg);
+- u32 addr = VC_SAFE(sg_dma_address(sg));
++ u32 addr = sg_dma_address(sg);
+ u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ /* Note: addrs is the address + page_count - 1
+--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
++++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+@@ -3205,22 +3205,8 @@ vchiq_register_child(struct platform_dev
+
+ child->dev.of_node = np;
+
+- /*
+- * We want the dma-ranges etc to be copied from a device with the
+- * correct dma-ranges for the VPU.
+- * VCHIQ on Pi4 is now under scb which doesn't get those dma-ranges.
+- * Take the "dma" node as going to be suitable as it sees the world
+- * through the same eyes as the VPU.
+- */
+- np = of_find_node_by_path("dma");
+- if (!np)
+- np = pdev->dev.of_node;
+-
+ of_dma_configure(&child->dev, np, true);
+
+- if (np != pdev->dev.of_node)
+- of_node_put(np);
+-
+ return child;
+ }
+
diff --git a/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch b/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch
new file mode 100644
index 0000000000..947fb73a39
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch
@@ -0,0 +1,67 @@
+From 79495a5ecdfba69de51e88701a69c42d09806d84 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 20 May 2020 16:36:57 +0100
+Subject: [PATCH] ARM: dts: Update for new VCHIQ BCM2711 DMA support
+
+Now that the enhanced BCM2711 DMA controller is located by compatible
+string and used directly for generating bulk transfer addresses,
+remove the workaround of moving the vchiq node.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 23 ++++-------------------
+ 1 file changed, 4 insertions(+), 19 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -4,7 +4,6 @@
+ / {
+ soc {
+ /delete-node/ v3d@7ec00000;
+- /delete-node/ mailbox@7e00b840;
+ };
+
+ __overrides__ {
+@@ -88,12 +87,6 @@
+ brcm,dma-channel-mask = <0x7800>;
+ };
+
+- vchiq: mailbox@7e00b840 {
+- compatible = "brcm,bcm2711-vchiq";
+- reg = <0 0x7e00b840 0x0 0x3c>;
+- interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+- };
+-
+ xhci: xhci@7e9c0000 {
+ compatible = "generic-xhci";
+ status = "disabled";
+@@ -127,18 +120,6 @@
+ };
+ };
+
+-&vchiq {
+- /* Onboard audio
+- * This node is replicated because the original from bcm270x-rpi.dtsi
+- * was deleted when the vchiq node was deleted above.
+- */
+- audio: bcm2835_audio {
+- compatible = "brcm,bcm2835-audio";
+- brcm,pwm-channels = <8>;
+- status = "disabled";
+- };
+-};
+-
+ &dma {
+ /* The VPU firmware uses DMA channel 11 for VCHIQ */
+ brcm,dma-channel-mask = <0x1f5>;
+@@ -149,6 +130,10 @@
+ brcm,dma-channel-mask = <0x7000>;
+ };
+
++&vchiq {
++ compatible = "brcm,bcm2711-vchiq";
++};
++
+ &firmwarekms {
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+ };