From 584d4bf1d3c2265a810e1494eb5c8ef0a72ee934 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
Date: Wed, 27 May 2020 15:12:04 +0200
Subject: [PATCH] bcm27xx: update patches from RPi foundation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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>
---
 target/linux/bcm27xx/bcm2708/config-5.4       |    8 +-
 target/linux/bcm27xx/bcm2709/config-5.4       |    5 +
 target/linux/bcm27xx/bcm2710/config-5.4       |    6 +
 target/linux/bcm27xx/bcm2711/config-5.4       |    6 +
 target/linux/bcm27xx/modules/sound.mk         |   49 +
 target/linux/bcm27xx/modules/video.mk         |    5 +-
 ...3d_drv-Allow-clock-retrieval-by-name.patch |   23 -
 ...clock-so-firmware-knows-we-are-usin.patch} |    0
 ...-0352-clk-bcm2835-Disable-v3d-clock.patch} |    0
 ...eq-Only-report-integer-pll-divisor-.patch} |    0
 ...54-arm-dts-Correct-Pi-4B-LED-values.patch} |    0
 ...k-raspberrypi-Also-support-v3d-clock.patch |  647 ----
 ...a_mask-as-well-as-coherent_dma_mask.patch} |    0
 ...0-0356-arm-dts-2711-Add-pcie0-alias.patch} |    0
 ...2-overlay-fix-pinctrl-configuration.patch} |    0
 ...-Set-up-dma-ranges-on-child-devices.patch} |    0
 ...-the-old-dma-controller-for-OF-conf.patch} |    0
 ...the-urb-transfer_buffer-too-early-3.patch} |    0
 ...ys-Make-mcp342x-run-time-compatible.patch} |    0
 ...-overlay-use-reset-gpios-instead-of.patch} |    0
 ...rst-codec-is-master-in-multicodec-s.patch} |    0
 ...aneous-use-of-JustBoom-DAC-and-Digi.patch} |    0
 ...-dht11-Allow-multiple-instantiation.patch} |    0
 ...erlays-i2c-rtc-Add-pcf85363-support.patch} |    0
 ...rl-bcm2835-Remove-gpiochip-on-error.patch} |    0
 ...835-Change-init-order-for-gpio-hogs.patch} |    0
 ...unication-fixes-for-scaled-down-CPU.patch} |    0
 ...Remove-simple-bus-from-fixed-clocks.patch} |    0
 ...ove-system-timer-back-to-bcm283x.dt.patch} |    0
 ...ove-pixelvalve-to-bcm2835-common.dt.patch} |    0
 ...dts-bcm2838-rpi-4-b-Fix-memory-node.patch} |    0
 ...pi-4-b-Backport-BT-part-from-upstre.patch} |    0
 ...8-Backport-node-names-from-upstream.patch} |    0
 ...ove-intc-label-to-bcm2835-common.dt.patch} |    0
 ...8-Remove-always-on-from-armv7-timer.patch} |    0
 ...net-bcmgenet-Add-RGMII_RXID-support.patch} |    0
 ...cm2838-Backport-genet-from-upstream.patch} |    0
 ...kport-BCM2711-support-from-upstream.patch} |    0
 ...proc-rng200-Add-support-for-BCM2711.patch} |    0
 ...bcm2838-Add-upstream-RNG-compatible.patch} |    0
 ...d-Destroy-the-legacy-device-on-remo.patch} |    0
 ...d-Clean-up-error-handling-use-of-ER.patch} |    0
 ...d-Add-error-handling-to-the-legacy-.patch} |    0
 ...d-Fix-coding-style-whitespace-issue.patch} |    0
 ...char-rpimem-Add-SPDX-licence-header.patch} |    0
 ...r-rpivid-Fix-access-to-freed-memory.patch} |    0
 ...89-add-BME680-to-i2c-sensor-overlay.patch} |    0
 ...-endpoint-max-packet-and-transfer-s.patch} |    0
 ...ause-when-cancelling-split-transact.patch} |    0
 ...dd-a-barrier-on-entry-into-FIQ-hand.patch} |    0
 ...device-tree-overlay-for-SPI-devices.patch} |    0
 ...nd-Add-the-HiFiBerry-DAC-HD-version.patch} |    0
 ...ise-rpi-firmware-before-clk-bcm2835.patch} |    0
 ...ettings-of-HiFiBerry-DAC-ADC-PRO-ca.patch} |    0
 ...ys-Use-preferred-compatible-strings.patch} |    0
 ...-amba-pl011-Add-un-throttle-support.patch} |    0
 ...0-0399-Fix-i2c-pwm-pca9685a-overlay.patch} |    0
 ...ure-to-HiFiBerry-DAC-ADC-PRO-sound-.patch} |    0
 ...ure-to-HiFiBerry-DAC-ADC-sound-card.patch} |    0
 ...ure-to-HiFiBerry-DAC-DAC-PRO-sound-.patch} |    0
 ...ding-Pisound-board-hardware-revisio.patch} |    0
 ...-iproc-Fix-vmmc-regulators-on-iProc.patch} |    0
 ...lare-RPi-4B-SD-card-power-regulator.patch} |    0
 ...-Use-BCM2711-PCIe-compatible-string.patch} |    0
 ...-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch} |    0
 ...11-Avoid-rare-write-when-full-error.patch} |    0
 ...aspberry-Pi-FW-loader-for-VIA-VL805.patch} |    0
 ...rect-the-eth_led-colour-assignments.patch} |    0
 ...d_poll_once-dtparam-to-bcm283x-2711.patch} |    0
 ...sd1306-spi-ssh1106-spi-ssd-1351-spi.patch} |    0
 ...overlays-dwc2-Increase-RX-FIFO-size.patch} |    0
 ...rlays-Fix-mcp23017-s-addr-parameter.patch} |    0
 ...SH-Fix-spi-driver-compiler-warnings.patch} |    0
 ...-hdmi-backlight-hwhack-gpio-overlay.patch} |    0
 ...t-all-changes-to-upstream-dts-files.patch} |    0
 ...n-out-downstream-BCM2711-2838-files.patch} |    0
 ...-Add-minimal-Raspberry-Pi-4-support.patch} |    0
 ...1-force-CMA-into-first-GB-of-memory.patch} |    0
 ...-bcm2711-rpi-4-Enable-GENET-support.patch} |    0
 ...s-bcm2711-fix-soc-s-node-dma-ranges.patch} |    0
 ...RM-dts-Rebuild-downstream-DTS-files.patch} |    0
 ...q_arm-Fix-bcm2711-compatible-string.patch} |    0
 ...al-brcmstb_thermal-Correct-SoC-name.patch} |    0
 ...hwrng-iproc-rng200-Correct-SoC-name.patch} |    0
 ...> 950-0427-ARM-dts-Correct-SoC-name.patch} |    0
 ...Remove-CMA-allocation-from-Pi-4-dts.patch} |    0
 ...iq_arm-Give-vchiq-children-DT-nodes.patch} |    0
 ..._arm-Add-a-matching-unregister-call.patch} |    0
 ...e-audio-node-under-the-vchiq-parent.patch} |    0
 ...ts-overlays-Create-custom-clocks-in.patch} |    0
 ...ices-Fix-vcsm-overflow-bug-when-cou.patch} |    0
 ...meout_ms-parameter-to-gpio-poweroff.patch} |    0
 ...-overlay-Correct-symbol-path-fixups.patch} |    0
 ...ys-sc16ic750-i2c-Fix-xtal-parameter.patch} |    0
 ...oduce-of_get_next_dma_parent-helper.patch} |    0
 ...-Follow-DMA-parent-for-dma-coherent.patch} |    0
 ...-Factor-out-addr-size-cells-parsing.patch} |    0
 ...ate-dma-ranges-for-parent-nodes-mis.patch} |    0
 ...-of_dma_get_range-work-on-bus-nodes.patch} |    0
 ...4_dma_phys_limit-instead-of-calling.patch} |    0
 ...ables-used-to-calculate-ZONE_DMA32-.patch} |    0
 ...64-use-both-ZONE_DMA-and-ZONE_DMA32.patch} |    0
 ...MA-and-ZONE_DMA32-comments-in-enum-.patch} |    0
 ...d-a-resource_list_first_type-helper.patch} |    0
 ...-ARCH_ZONE_DMA_BITS-into-a-variable.patch} |    0
 ...use-default-DMA-address-translation.patch} |    0
 ...inbound-resource-parsing-to-helpers.patch} |    0
 ...t-unify-the-dma_capable-definitions.patch} |    0
 ...a-forward-declaration-for-phys_to_d.patch} |    0
 ...e-dma_direct_map_resource-from-the-.patch} |    0
 ...eat-dev-bus_dma_mask-as-a-DMA-limit.patch} |    0
 ...-dts-bcm2711-Enable-PCIe-controller.patch} |    0
 ...roadcom-STB-PCIe-host-controller-dr.patch} |    0
 ...50-0456-PCI-brcmstb-Add-MSI-support.patch} |    0
 ...uild-on-32bit-ARM-platforms-with-ol.patch} |    0
 ...711-rpi.dtsi-Use-upstream-pcie-node.patch} |    0
 ...-media-i2c-Add-IMX219-CMOS-sensor-b.patch} |    0
 ...c-Add-driver-for-Sony-IMX219-sensor.patch} |    0
 ...orrect-link-frequency-to-match-the-.patch} |    0
 ...w-.dtbo-overlays-to-be-built-adjust.patch} |    0
 ...return-codes-from-ov5647_write-ov56.patch} |    0
 ...basic-support-for-multiple-sensor-m.patch} |    0
 ...V4L2-controls-for-analogue-gain-exp.patch} |    0
 ...v5647-Add-extra-10-bit-sensor-modes.patch} |    0
 ...ge-defaults-to-better-match-raw-cam.patch} |    0
 ...ge-crtc_state-structure-name-to-avo.patch} |    0
 ...c-Add-packed-10bit-YUV-4-2-0-format.patch} |    0
 ...FORMAT_P030-support-to-firmware-kms.patch} |    0
 ...dd-parameter-to-configure-signal-po.patch} |    0
 ...erus-amp-soundcard-and-ma120x0p-cod.patch} |    0
 ...cm2711-Add-32-bit-PMU-compatibility.patch} |    0
 ...dts-bcm271x-Use-a53-pmu-drop-RPI364.patch} |    0
 ...r-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch} |    0
 ...mdline-Fix-possible-reference-past-.patch} |    0
 ...mdline-Make-various-char-pointers-c.patch} |    0
 ...mdline-Stop-parsing-extras-after-bp.patch} |    0
 ...mdline-Accept-extras-directly-after.patch} |    0
 ...mdline-Rework-drm_mode_parse_cmdlin.patch} |    0
 ...mdline-Add-freestanding-argument-to.patch} |    0
 ...mdline-Set-bpp-refresh_specified-af.patch} |    0
 ...mdline-Allow-specifying-stand-alone.patch} |    0
 ...mdline-Add-support-for-specifying-p.patch} |    0
 ...mdline-Remove-some-unnecessary-code.patch} |    0
 ...mdline-Explicitly-memset-the-passed.patch} |    0
 ...ait_for-macros-to-remove-use-of-msl.patch} |    0
 ...8-Reduce-noise-from-rpi-poe-hat-fan.patch} |    0
 ...nsirion-SPS30-to-i2c-sensor-overlay.patch} |    0
 ...dd-V4L2_CTRL_TYPE_AREA-control-type.patch} |    0
 ...add-V4L2_CID_UNIT_CELL_SIZE-control.patch} |    0
 ...2-common-add-pixel-encoding-support.patch} |    0
 ...-add-RGB565-and-RGB55-to-v4l2_forma.patch} |    0
 ...-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch} |    0
 ...em2mem-support-held-capture-buffers.patch} |    0
 ...-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch} |    0
 ...m-add-stateless_-try_-decoder_cmd-i.patch} |    0
 ...4l2-mem2mem-add-new_frame-detection.patch} |    0
 ...on-media-Document-V4L2_CTRL_TYPE_AR.patch} |    0
 ...initions-for-HEVC-stateless-decodin.patch} |    0
 ...l2-mem2mem-Fix-hold-buf-flag-checks.patch} |    0
 ...ocument-the-HEVC-slice-pixel-format.patch} |    0
 ...api-hevc-Add-scaling-matrix-control.patch} |    0
 ...uapi-hevc-Add-segment-address-field.patch} |    0
 ...Add-slice-param-dependent-slice-seg.patch} |    0
 ...api-Add-hevc-ctrls-for-WPP-decoding.patch} |    0
 ...-Add-a-format-for-column-YUV4-2-0-m.patch} |    0
 ...m-allow-request-job-buffer-processi.patch} |    0
 ...-media-Add-binding-for-the-Raspberr.patch} |    0
 ...-Add-Raspberry-Pi-V4L2-H265-decoder.patch} |    0
 ...erlay-to-enable-the-HEVC-V4L2-drive.patch} |    0
 ...-0512-mmc-sdhci-Silence-MMC-warnings.patch |   42 +
 ...brcmstb-Convert-the-BRCMSTB-binding-.patch |  126 +
 ...brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch |   96 +
 ...Support-BCM2711-HDMI-BSC-controllers.patch |   87 +
 ...cmstb-Allow-to-compile-it-on-BCM2835.patch |   31 +
 ...k-Add-a-binding-for-the-RPi-Firmware.patch |   63 +
 ...-Allow-the-driver-to-be-probed-by-DT.patch |   60 +
 ...cm-rpi-Statically-init-clk_init_data.patch |   32 +
 ...rpi-Use-clk_hw_register-for-pllb_arm.patch |   56 +
 ...Remove-global-pllb_arm-clock-pointer.patch |   45 +
 ...cm-rpi-Make-sure-pllb_arm-is-removed.patch |   40 +
 ...emove-pllb_arm_lookup-global-pointer.patch |   51 +
 ...rpi-Switch-to-clk_hw_register_clkdev.patch |   45 +
 ...ke-sure-the-clkdev-lookup-is-removed.patch |   34 +
 ...eate-a-data-structure-for-the-clocks.patch |  126 +
 ...527-clk-bcm-rpi-Add-clock-id-to-data.patch |   86 +
 ...-the-clocks-data-to-the-firmware-fun.patch |   96 +
 ...-bcm-rpi-Rename-is_prepared-function.patch |   40 +
 ...0-clk-bcm-rpi-Split-pllb-clock-hooks.patch |   80 +
 ...-the-PLLB-registration-function-retu.patch |  144 +
 ...m-rpi-Add-DT-provider-for-the-clocks.patch |   67 +
 ...bcm-rpi-Discover-the-firmware-clocks.patch |  173 ++
 ...dts-bcm2711-Add-firmware-clocks-node.patch |   39 +
 ...t-simple-header-out-of-drivers-reset.patch |  168 +
 ...0536-reset-simple-Add-reset-callback.patch |   85 +
 ...ndings-clock-Add-BCM2711-DVP-binding.patch |   68 +
 ...-0538-clk-bcm-Add-BCM2711-DVP-driver.patch |  172 ++
 ...50-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch |   43 +
 ...play-Convert-VC4-bindings-to-schemas.patch |  706 +++++
 ...lay-vc4-dpi-Add-missing-clock-names-.patch |   38 +
 ...lay-vc4-dsi-Add-missing-clock-proper.patch |   54 +
 ...lay-vc4-hdmi-Add-missing-clock-names.patch |   34 +
 ...ngs-display-vc4-Document-BCM2711-VC5.patch |   24 +
 ...-0545-drm-vc4-drv-Add-include-guards.patch |   30 +
 ...950-0546-drm-vc4-drv-Support-BCM2711.patch |  109 +
 ...drv-Add-support-for-the-BCM2711-HVS5.patch |  497 +++
 ...0548-drm-vc4-plane-Improve-LBM-usage.patch |   98 +
 ...ve-planes-creation-to-its-own-functi.patch |  125 +
 ...ve-additional-planes-creation-to-dri.patch |   75 +
 ...lane-Register-all-the-planes-at-once.patch |  128 +
 ...4-plane-Create-overlays-for-any-CRTC.patch |   70 +
 ...553-drm-vc4-plane-Create-more-planes.patch |   32 +
 ...-vc4-crtc-Rename-SoC-data-structures.patch |   56 +
 ...rtc-Move-crtc-state-to-common-header.patch |   74 +
 ...l-with-different-number-of-pixel-per.patch |   86 +
 ...-drm-vc4-crtc-Use-a-shared-interrupt.patch |   26 +
 ...n-static-const-variable-into-a-defin.patch |   50 +
 ...e-the-cob-allocation-outside-of-bind.patch |  110 +
 ...c4-crtc-Rename-HVS-channel-to-output.patch |   80 +
 ...drm-vc4-crtc-Use-local-chan-variable.patch |   24 +
 ...ble-and-disable-the-PV-in-atomic_ena.patch |   55 +
 ...sign-output-to-channel-automatically.patch |  459 +++
 ...crtc-Add-FIFO-depth-to-vc4_crtc_data.patch |   86 +
 ...-function-to-compute-FIFO-level-bits.patch |   44 +
 ...tc-Rename-HDMI-encoder-type-to-HDMI0.patch |   49 +
 ...-drm-vc4-crtc-Add-HDMI1-encoder-type.patch |   23 +
 ...ove-redundant-call-to-drm_crtc_enabl.patch |   24 +
 ...tc-Disable-color-management-for-HVS5.patch |   54 +
 ...play-vc4-pv-Add-BCM2711-pixel-valves.patch |   30 +
 ...drm-vc4-crtc-Add-BCM2711-pixelvalves.patch |  152 +
 ...m-vc4-hdmi-Use-debugfs-private-field.patch |   30 +
 ...rm-vc4-hdmi-Move-structure-to-header.patch |  195 ++
 ...-hdmi-rework-connectors-and-encoders.patch |  348 +++
 ...drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch |  682 +++++
 ...-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch |  152 +
 ...vc4-hdmi-Use-local-vc4_hdmi-directly.patch |   45 +
 ...-container_of-macros-for-encoders-an.patch |  151 +
 ...m-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch |  107 +
 ...vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch |   67 +
 ...m-vc4-hdmi-Remove-vc4_hdmi_connector.patch |  141 +
 ...-Introduce-resource-init-and-variant.patch |  151 +
 ...lement-a-register-layout-abstraction.patch | 1310 ++++++++
 ...0584-drm-vc4-hdmi-Add-reset-callback.patch |   66 +
 ...mi-Add-PHY-init-and-disable-function.patch |  128 +
 ...-Add-PHY-RNG-enable-disable-function.patch |  104 +
 ...rm-vc4-hdmi-Add-a-CSC-setup-callback.patch |  134 +
 ...-vc4-hdmi-Add-a-set_timings-callback.patch |  133 +
 .../950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch   |   46 +
 ...dmi-Deal-with-multiple-debugfs-files.patch |   33 +
 ...m-vc4-hdmi-Add-an-audio-support-flag.patch |   47 +
 ...mi-Move-CEC-init-to-its-own-function.patch |  165 +
 ...93-drm-vc4-hdmi-Add-CEC-support-flag.patch |   47 +
 ...i-Remove-unused-CEC_CLOCK_DIV-define.patch |   23 +
 ...ame-drm_encoder-pointer-in-mode_vali.patch |   26 +
 ...ust-HSM-clock-rate-depending-on-pixe.patch |  163 +
 ...Support-the-BCM2711-HDMI-controllers.patch | 1131 +++++++
 ...lay-vc4-hdmi-Add-BCM2711-HDMI-contro.patch |  174 ++
 ...-bcm2711-Enable-the-display-pipeline.patch |  210 ++
 ...ts-rpi4-Disable-KMS-driver-by-defaul.patch |   90 +
 ...rlays-Add-Pi4-version-of-vc4-kms-v3d.patch |  241 ++
 ...the-pitch-is-only-valid-for-linear-f.patch |   39 +
 ...ort-for-DRM_FORMAT_P030-to-vc4-plane.patch |  174 ++
 .../950-0604-Fixup-P030-support.patch         |   26 +
 ...k-for-assigned-HVS-channels-is-not-a.patch |   33 +
 ...dt-Update-v3d-to-use-firmware_clocks.patch |   23 +
 ...dio-infoframe-on-encoder_enable-if-p.patch |   72 +
 ...b-frame-marker-to-the-match-ALSA-s-d.patch |   31 +
 ...es-for-the-HDMI-registers-on-bcm2835.patch |   27 +
 ...DMI-audio-dma-values-to-bcm2711.dtsi.patch |   32 +
 ...se-reg-names-to-configure-HDMI-audio.patch |   35 +
 ...vc4-Add-audio-initialisation-for-Pi4.patch |  127 +
 ...950-0613-drm-vc4-Enable-audio-on-Pi4.patch |   31 +
 ...e-HDMI-state-machine-clock-calc-to-a.patch |   46 +
 ...e-comment-about-vc4-kms-v3d-locking-.patch |   28 +
 ...e-core-clock-up-during-a-mode-change.patch |   97 +
 ...-0617-drm-vc4-Fixup-for-firmware-KMS.patch |   36 +
 ...Fixup-plane-init-within-firmware-kms.patch |   31 +
 ...e-the-HDMI-audio-instances-different.patch |   26 +
 ...interrupt-line-is-optional-so-use-pl.patch |   49 +
 ...C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch |   35 +
 ...rlays-Add-missing-rpi-poe-parameters.patch |   39 +
 ...-vc4_hdmi_phy-Fix-offset-calculation.patch |   30 +
 .../950-0624-overlays-Add-overlay_map.patch   |  101 +
 ...rmally-rename-deprecate-old-overlays.patch |  226 ++
 ...s-Add-vc4-kms-v3d-pi4-to-overlay_map.patch |   26 +
 ...ream-and-upstream-pi4-to-overlay_map.patch |  229 ++
 ...llow-cpufreq-driver-to-also-adjust-.patch} |    6 +-
 ...the-AudioInjector.net-Isolated-sound.patch |  323 ++
 ...verlays-Fix-dtc-warnings-in-i2c-gpio.patch |   24 +
 .../950-0631-kbuild-Disable-gcc-plugins.patch |   28 +
 ...ASoC-ma120x0p-Add-96KHz-rate-support.patch |   42 +
 ...ve-CMA-and-crashkernel-in-ZONE_DMA32.patch |   44 +
 ...tialisation-of-DMA-zones-on-non-NUMA.patch |  105 +
 ...-dts-bcm283x-Unify-CMA-configuration.patch |   95 +
 ...guous-CMA-give-precedence-to-cmdline.patch |   49 +
 ...M-dts-Use-upstream-CMA-configuration.patch |   36 +
 ...-overlays-Unify-overlay-CMA-handling.patch |  871 ++++++
 ...Fix-vc4-s-firmware-bus-DMA-limitatio.patch |   28 +
 ...-bcm2711-Restrict-CMA-to-first-768MB.patch |   33 +
 ...ARM-dts-Extend-SCB-bus-address-range.patch |   23 +
 ...ts-bcm2711-Move-emmc2-to-its-own-bus.patch |   52 +
 ...ence-pixel-clock-error-on-EPROBE_DEF.patch |   29 +
 ...t-Silence-bind-error-on-EPROBE_DEFER.patch |   41 +
 ...with-clock-settings-of-HiFiBerry-DAC.patch |   42 +
 ...on-media-Update-sub-device-API-intro.patch |   34 +
 ...n-media-Document-read-only-subdevice.patch |  217 ++
 ...dd-v4l2_device_register_ro_subdev_no.patch |  206 ++
 ...icam-Driver-for-CCP2-CSI2-camera-int.patch | 2709 +++++++++++++++++
 ...core-Add-sensor-ancillary-data-V4L2-.patch |   85 +
 ...EDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch |   64 +
 ...icam-Add-support-for-mulitple-device.patch | 1084 +++++++
 ...cm2835-unicam-Add-embedded-data-node.patch | 1170 +++++++
 ...icam-Use-dummy-buffer-if-none-have-b.patch |  308 ++
 ...CS_HIGH-if-GPIO-descriptors-are-used.patch |   48 +
 ...-media-i2c-imx219-Fix-power-sequence.patch |   55 +
 ...-Add-support-for-RAW8-bit-bayer-form.patch |  319 ++
 ...-Add-support-for-cropped-640x480-res.patch |  118 +
 ...-Fix-a-bug-in-imx219_enum_frame_size.patch |   34 +
 ...icam-Disable-event-related-ioctls-on.patch |   31 +
 ...icam-Add-support-for-the-FRAME_SYNC-.patch |   55 +
 ...ware-raspberrypi-register-clk-device.patch |   58 +
 ...ertise-embedded-data-node-on-media-p.patch |  335 ++
 ...EMMC2-can-address-the-whole-first-GB.patch |   31 +
 ...ar-rpivid-Remove-legacy-name-support.patch |   53 +
 ...ar-rpivid-Don-t-map-more-than-wanted.patch |   51 +
 ...mplement-an-I2C-pinctrl-mux-for-BSC0.patch |  434 +++
 ...date-CSI-overlays-to-use-i2c_csi_dsi.patch |  425 +++
 ...inline-bcm283x-dt-files-for-i2c0-pin.patch |  201 ++
 ...RM-dts-Create-bcm2708-rpi-b-rev1.dts.patch |  182 ++
 ...50-0671-dts-bcm2711-set-size-cells-2.patch |  117 +
 ...711-add-High-Peripheral-mode-overlay.patch |  140 +
 ...v-Fix-CS-polarity-if-GPIO-descriptor.patch |   32 +
 ..._descriptor-fixup-moved-to-spi_setup.patch |   55 +
 ...-rpivid-v4l2-also-needs-size-cells-2.patch |   30 +
 ...icam-Re-fetch-mbus-code-from-subdev-.patch |   49 +
 ...isp-Add-bcm2835-isp-uapi-header-file.patch |  337 ++
 ...core-Add-ISP-statistics-output-V4L2-.patch |   94 +
 ...trls-Add-CID-base-for-the-bcm2835-is.patch |  169 +
 ...iq-Fix-formatting-errors-in-mmal_par.patch |  116 +
 ...vices-ISP-Add-a-more-complex-ISP-pro.patch | 2255 ++++++++++++++
 ...q-Load-bcm2835_isp-driver-from-vchiq.patch |   39 +
 ...-vc4_hvs-Mark-core-clock-as-optional.patch |   23 +
 ...-requires-a-fixed-hsm-clock-for-CEC-.patch |   93 +
 ...a-i2c-imx219-Implement-get_selection.patch |  181 ++
 ...-Add-support-for-g_selection-to-refl.patch |  206 ++
 ...67-Fixup-error-path-to-release-mutex.patch |   29 +
 ...c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch |  131 +
 ...7-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch |  143 +
 ...5647-Add-support-for-V4L2_CID_VBLANK.patch |  205 ++
 ...-Neither-analogue-gain-nor-exposure-.patch |   58 +
 ...5647-Use-member-names-in-mode-tables.patch |  111 +
 ...-Advertise-the-correct-exposure-rang.patch |  119 +
 ...-Declare-that-the-driver-can-create-.patch |   27 +
 ...icam-Add-support-for-VIDIOC_-S-G-_SE.patch |   82 +
 ...icam-Do-not-stop-streaming-in-unicam.patch |   28 +
 ...icam-Fix-reference-counting-in-unica.patch |   38 +
 ...rvices-ISP-Add-enum_framesizes-ioctl.patch |  333 ++
 ...te-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch |   28 +
 ...35-dma-Add-proper-40-bit-DMA-support.patch |  801 +++++
 ...dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch |   40 +
 ...Make-the-i2c-gpio-overlay-safe-again.patch |   32 +
 ...vices-isp-Remove-duplicated-initiali.patch |   62 +
 ...vices-isp-Make-all-references-to-bcm.patch |  148 +
 ...-gpio-keys-Avoid-open-drain-warnings.patch |   29 +
 ...i_phy-Fix-typo-in-phy_get_cp_current.patch |   23 +
 ...-Make-use-of-intra-overlay-fragments.patch |   92 +
 ...i2c-tc358743-Fix-fallthrough-warning.patch |   20 +
 ...835-unicam-Fix-uninitialized-warning.patch |   21 +
 ...8_fb-Disable-FB-if-no-displays-found.patch |   34 +
 ...ys-sc16is752-spi1-Add-xtal-parameter.patch |   38 +
 ...ister-offset-when-sending-longer-CEC.patch |   42 +
 ...0-0713-vc4_hdmi-Fix-up-CEC-registers.patch |   43 +
 ...4_hdmi_regs-Add-Intr2-register-block.patch |  151 +
 ...Make-interrupt-mask-variant-specific.patch |  101 +
 .../950-0716-vc4_hdmi-Make-irq-shared.patch   |   22 +
 ...CEC-ref-clock-based-on-its-input-clo.patch |   89 +
 ...cec_available-flag-as-always-support.patch |   44 +
 ...tc358743-Use-intra-overlay-fragments.patch |   55 +
 ...s-Move-fixed-clock-nodes-to-the-root.patch |  326 ++
 ...-dts-Switch-to-discrete-ALSA-devices.patch |   84 +
 ...a-i2c-Add-IMX477-CMOS-sensor-binding.patch |  130 +
 ...dtoverlays-Add-IMX477-sensor-overlay.patch |  156 +
 ...2c-Add-driver-for-Sony-IMX477-sensor.patch | 2266 ++++++++++++++
 ...-Add-support-for-adaptive-frame-cont.patch |  182 ++
 ...uf-Remove-deleted-map-unmap-handlers.patch |   52 +
 ...udmabuf-use-cache_sgt_mapping-option.patch |   35 +
 ...inter-to-the-miscdevice-in-dma-buf-p.patch |   67 +
 ...-out-creating-destroying-scatter-tab.patch |   71 +
 ...t-begin_cpu_access-end_cpu_access-ho.patch |   90 +
 ...-0731-udmabuf-fix-dma-buf-cpu-access.patch |   60 +
 ...-dma-buf-Add-dma-buf-heaps-framework.patch |  525 ++++
 ...-0733-dma-buf-heaps-Add-heap-helpers.patch |  394 +++
 ...eaps-Add-system-heap-to-dmabuf-heaps.patch |  200 ++
 ...f-heaps-Add-CMA-heap-to-dmabuf-heaps.patch |  251 ++
 ...50-0736-kselftests-Add-dma-heap-test.patch |  453 +++
 ...e-_IOCTL_-for-userspace-IOCTL-identi.patch |   69 +
 ...move-redundant-heap-identifier-from-.patch |   28 +
 ...urce-leak-on-ENOTTY-error-return-pat.patch |   34 +
 ...he-symbol-dma_heap_ioctl_cmds-static.patch |   34 +
 ...ts-Enable-firmware-clocks-on-all-Pis.patch |   25 +
 ...835-unicam-Always-service-interrupts.patch |   51 +
 ...6is7xx-Fix-for-hardware-flow-control.patch |   70 +
 ...vc4-Fix-VIC-usage-with-Broadcast-RGB.patch |   58 +
 ...vices-mmal-vchiq-Update-parameters-l.patch |   28 +
 ...vices-bcm2835-codec-Request-headers-.patch |   29 +
 ...vices-bcm2835-codec-Avoid-fragmentin.patch |   32 +
 ...vices-bcm2835-camera-Request-headers.patch |   30 +
 ...s-Fix-audio-parameter-of-vc4-kms-v3d.patch |   25 +
 ...Switch-to-snd_soc_dai_set_bclk_ratio.patch |   38 +
 ...icam-Retain-packing-information-on-G.patch |   48 +
 ...752-zswap-Defer-zswap-initialisation.patch |  115 +
 ...e-dma-configuration-from-the-HVS-or-.patch |   54 +
 ...-as-an-acceptable-node-for-dma-range.patch |   27 +
 ...-Return-correct-result-on-sensor-id-.patch |   25 +
 ...chiq_arm-Clean-up-40-bit-DMA-support.patch |  154 +
 ...te-for-new-VCHIQ-BCM2711-DMA-support.patch |   67 +
 target/linux/generic/config-5.4               |   12 +
 416 files changed, 37487 insertions(+), 676 deletions(-)
 delete mode 100644 target/linux/bcm27xx/patches-5.4/950-0351-v3d_drv-Allow-clock-retrieval-by-name.patch
 rename target/linux/bcm27xx/patches-5.4/{950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch => 950-0351-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0355-clk-bcm2835-Disable-v3d-clock.patch => 950-0352-clk-bcm2835-Disable-v3d-clock.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch => 950-0353-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0357-arm-dts-Correct-Pi-4B-LED-values.patch => 950-0354-arm-dts-Correct-Pi-4B-LED-values.patch} (100%)
 delete mode 100644 target/linux/bcm27xx/patches-5.4/950-0354-clk-raspberrypi-Also-support-v3d-clock.patch
 rename target/linux/bcm27xx/patches-5.4/{950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch => 950-0355-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0359-arm-dts-2711-Add-pcie0-alias.patch => 950-0356-arm-dts-2711-Add-pcie0-alias.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch => 950-0357-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch => 950-0358-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch => 950-0359-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch => 950-0360-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0364-overlays-Make-mcp342x-run-time-compatible.patch => 950-0361-overlays-Make-mcp342x-run-time-compatible.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch => 950-0362-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch => 950-0363-sound-soc-only-first-codec-is-master-in-multicodec-s.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch => 950-0364-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0368-overlays-dht11-Allow-multiple-instantiation.patch => 950-0365-overlays-dht11-Allow-multiple-instantiation.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch => 950-0366-overlays-i2c-rtc-Add-pcf85363-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch => 950-0367-pinctrl-bcm2835-Remove-gpiochip-on-error.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch => 950-0368-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch => 950-0369-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch => 950-0370-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch => 950-0371-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch => 950-0372-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch => 950-0373-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch => 950-0374-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch => 950-0375-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch => 950-0376-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch => 950-0377-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch => 950-0378-net-bcmgenet-Add-RGMII_RXID-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch => 950-0379-ARM-dts-bcm2838-Backport-genet-from-upstream.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch => 950-0380-ARM-bcm-Backport-BCM2711-support-from-upstream.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch => 950-0381-hwrng-iproc-rng200-Add-support-for-BCM2711.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch => 950-0382-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch => 950-0383-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch => 950-0384-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch => 950-0385-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch => 950-0386-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch => 950-0387-driver-char-rpimem-Add-SPDX-licence-header.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch => 950-0388-driver-char-rpivid-Fix-access-to-freed-memory.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0392-add-BME680-to-i2c-sensor-overlay.patch => 950-0389-add-BME680-to-i2c-sensor-overlay.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch => 950-0390-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch => 950-0391-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch => 950-0392-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch => 950-0393-Add-universal-device-tree-overlay-for-SPI-devices.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch => 950-0394-sound-Add-the-HiFiBerry-DAC-HD-version.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch => 950-0395-Initialise-rpi-firmware-before-clk-bcm2835.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch => 950-0396-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0400-overlays-Use-preferred-compatible-strings.patch => 950-0397-overlays-Use-preferred-compatible-strings.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0401-tty-amba-pl011-Add-un-throttle-support.patch => 950-0398-tty-amba-pl011-Add-un-throttle-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0402-Fix-i2c-pwm-pca9685a-overlay.patch => 950-0399-Fix-i2c-pwm-pca9685a-overlay.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch => 950-0400-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch => 950-0401-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch => 950-0402-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch => 950-0403-pisound-Added-reading-Pisound-board-hardware-revisio.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch => 950-0404-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch => 950-0405-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch => 950-0406-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch => 950-0407-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch => 950-0408-tty-amba-pl011-Avoid-rare-write-when-full-error.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch => 950-0409-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0413-overlays-Correct-the-eth_led-colour-assignments.patch => 950-0410-overlays-Correct-the-eth_led-colour-assignments.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch => 950-0411-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch => 950-0412-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch => 950-0413-overlays-dwc2-Increase-RX-FIFO-size.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch => 950-0414-overlays-Fix-mcp23017-s-addr-parameter.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch => 950-0415-SQUASH-Fix-spi-driver-compiler-warnings.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch => 950-0416-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch => 950-0417-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch => 950-0418-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch => 950-0419-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch => 950-0420-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch => 950-0421-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch => 950-0422-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch => 950-0423-ARM-dts-Rebuild-downstream-DTS-files.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch => 950-0424-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch => 950-0425-thermal-brcmstb_thermal-Correct-SoC-name.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch => 950-0426-hwrng-iproc-rng200-Correct-SoC-name.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0430-ARM-dts-Correct-SoC-name.patch => 950-0427-ARM-dts-Correct-SoC-name.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch => 950-0428-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch => 950-0429-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch => 950-0430-staging-vchiq_arm-Add-a-matching-unregister-call.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch => 950-0431-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch => 950-0432-ARM-dts-overlays-Create-custom-clocks-in.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch => 950-0433-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch => 950-0434-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0438-of-overlay-Correct-symbol-path-fixups.patch => 950-0435-of-overlay-Correct-symbol-path-fixups.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch => 950-0436-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch => 950-0437-of-address-Introduce-of_get_next_dma_parent-helper.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch => 950-0438-of-address-Follow-DMA-parent-for-dma-coherent.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0442-of-Factor-out-addr-size-cells-parsing.patch => 950-0439-of-Factor-out-addr-size-cells-parsing.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch => 950-0440-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch => 950-0441-of-Make-of_dma_get_range-work-on-bus-nodes.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch => 950-0442-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch => 950-0443-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch => 950-0444-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch => 950-0445-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0449-resource-Add-a-resource_list_first_type-helper.patch => 950-0446-resource-Add-a-resource_list_first_type-helper.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch => 950-0447-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch => 950-0448-x86-PCI-sta2x11-use-default-DMA-address-translation.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch => 950-0449-PCI-of-Add-inbound-resource-parsing-to-helpers.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0453-dma-direct-unify-the-dma_capable-definitions.patch => 950-0450-dma-direct-unify-the-dma_capable-definitions.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch => 950-0451-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch => 950-0452-dma-direct-exclude-dma_direct_map_resource-from-the-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch => 950-0453-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch => 950-0454-ARM-dts-bcm2711-Enable-PCIe-controller.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch => 950-0455-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0459-PCI-brcmstb-Add-MSI-support.patch => 950-0456-PCI-brcmstb-Add-MSI-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch => 950-0457-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch => 950-0458-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch => 950-0459-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch => 950-0460-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch => 950-0461-overlays-imx219-Correct-link-frequency-to-match-the-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch => 950-0462-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch => 950-0463-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch => 950-0464-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch => 950-0465-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch => 950-0466-media-ov5647-Add-extra-10-bit-sensor-modes.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch => 950-0467-media-ov5647-change-defaults-to-better-match-raw-cam.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch => 950-0468-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch => 950-0469-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch => 950-0470-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch => 950-0471-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch => 950-0472-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch => 950-0473-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch => 950-0474-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch => 950-0475-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch => 950-0476-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch => 950-0477-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch => 950-0478-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch => 950-0479-drm-modes-parse_cmdline-Accept-extras-directly-after.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch => 950-0480-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch => 950-0481-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch => 950-0482-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch => 950-0483-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch => 950-0484-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch => 950-0485-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch => 950-0486-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch => 950-0487-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch => 950-0488-Reduce-noise-from-rpi-poe-hat-fan.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch => 950-0489-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch => 950-0490-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch => 950-0491-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0495-media-v4l2-common-add-pixel-encoding-support.patch => 950-0492-media-v4l2-common-add-pixel-encoding-support.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch => 950-0493-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch => 950-0494-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch => 950-0495-media-v4l2-mem2mem-support-held-capture-buffers.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch => 950-0496-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch => 950-0497-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch => 950-0498-media-v4l2-mem2mem-add-new_frame-detection.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch => 950-0499-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch => 950-0500-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch => 950-0501-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch => 950-0502-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch => 950-0503-media-uapi-hevc-Add-scaling-matrix-control.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0507-media-uapi-hevc-Add-segment-address-field.patch => 950-0504-media-uapi-hevc-Add-segment-address-field.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch => 950-0505-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch => 950-0506-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch => 950-0507-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch => 950-0508-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch => 950-0509-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch => 950-0510-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch} (100%)
 rename target/linux/bcm27xx/patches-5.4/{950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch => 950-0511-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch} (100%)
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0512-mmc-sdhci-Silence-MMC-warnings.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0513-dt-bindings-i2c-brcmstb-Convert-the-BRCMSTB-binding-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0514-dt-bindings-i2c-brcmstb-Add-BCM2711-BSC-AUTO-I2C-bin.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0515-i2c-brcmstb-Support-BCM2711-HDMI-BSC-controllers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0516-i2c-brcmstb-Allow-to-compile-it-on-BCM2835.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0517-dt-bindings-clock-Add-a-binding-for-the-RPi-Firmware.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0518-clk-bcm-rpi-Allow-the-driver-to-be-probed-by-DT.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0519-clk-bcm-rpi-Statically-init-clk_init_data.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0520-clk-bcm-rpi-Use-clk_hw_register-for-pllb_arm.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0521-clk-bcm-rpi-Remove-global-pllb_arm-clock-pointer.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0522-clk-bcm-rpi-Make-sure-pllb_arm-is-removed.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0523-clk-bcm-rpi-Remove-pllb_arm_lookup-global-pointer.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0524-clk-bcm-rpi-Switch-to-clk_hw_register_clkdev.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0525-clk-bcm-rpi-Make-sure-the-clkdev-lookup-is-removed.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0526-clk-bcm-rpi-Create-a-data-structure-for-the-clocks.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0527-clk-bcm-rpi-Add-clock-id-to-data.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0528-clk-bcm-rpi-Pass-the-clocks-data-to-the-firmware-fun.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0529-clk-bcm-rpi-Rename-is_prepared-function.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0530-clk-bcm-rpi-Split-pllb-clock-hooks.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Make-the-PLLB-registration-function-retu.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0532-clk-bcm-rpi-Add-DT-provider-for-the-clocks.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0533-clk-bcm-rpi-Discover-the-firmware-clocks.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0534-ARM-dts-bcm2711-Add-firmware-clocks-node.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0535-reset-Move-reset-simple-header-out-of-drivers-reset.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0536-reset-simple-Add-reset-callback.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0537-dt-bindings-clock-Add-BCM2711-DVP-binding.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0538-clk-bcm-Add-BCM2711-DVP-driver.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0539-ARM-dts-bcm2711-Add-HDMI-DVP.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0540-dt-bindings-display-Convert-VC4-bindings-to-schemas.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0541-dt-bindings-display-vc4-dpi-Add-missing-clock-names-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0542-dt-bindings-display-vc4-dsi-Add-missing-clock-proper.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0543-dt-bindings-display-vc4-hdmi-Add-missing-clock-names.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0544-dt-bindings-display-vc4-Document-BCM2711-VC5.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0545-drm-vc4-drv-Add-include-guards.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0546-drm-vc4-drv-Support-BCM2711.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0547-drm-vc4-drv-Add-support-for-the-BCM2711-HVS5.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0548-drm-vc4-plane-Improve-LBM-usage.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0549-drm-vc4-plane-Move-planes-creation-to-its-own-functi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0550-drm-vc4-plane-Move-additional-planes-creation-to-dri.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0551-drm-vc4-plane-Register-all-the-planes-at-once.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0552-drm-vc4-plane-Create-overlays-for-any-CRTC.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0553-drm-vc4-plane-Create-more-planes.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0554-drm-vc4-crtc-Rename-SoC-data-structures.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0555-drm-vc4-crtc-Move-crtc-state-to-common-header.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0556-drm-vc4-crtc-Deal-with-different-number-of-pixel-per.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0557-drm-vc4-crtc-Use-a-shared-interrupt.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0558-drm-vc4-crtc-Turn-static-const-variable-into-a-defin.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0559-drm-vc4-crtc-Move-the-cob-allocation-outside-of-bind.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0560-drm-vc4-crtc-Rename-HVS-channel-to-output.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0561-drm-vc4-crtc-Use-local-chan-variable.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0562-drm-vc4-crtc-Enable-and-disable-the-PV-in-atomic_ena.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0563-drm-vc4-crtc-Assign-output-to-channel-automatically.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0564-drm-vc4-crtc-Add-FIFO-depth-to-vc4_crtc_data.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0565-drm-vc4-crtc-Add-function-to-compute-FIFO-level-bits.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0566-drm-vc4-crtc-Rename-HDMI-encoder-type-to-HDMI0.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0567-drm-vc4-crtc-Add-HDMI1-encoder-type.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0568-drm-vc4-crtc-Remove-redundant-call-to-drm_crtc_enabl.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0569-drm-vc4-crtc-Disable-color-management-for-HVS5.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0570-dt-bindings-display-vc4-pv-Add-BCM2711-pixel-valves.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0571-drm-vc4-crtc-Add-BCM2711-pixelvalves.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0572-drm-vc4-hdmi-Use-debugfs-private-field.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0573-drm-vc4-hdmi-Move-structure-to-header.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0574-drm-vc4-hdmi-rework-connectors-and-encoders.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0575-drm-vc4-hdmi-Rename-hdmi-to-vc4_hdmi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0576-drm-vc4-hdmi-Move-accessors-to-vc4_hdmi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0577-drm-vc4-hdmi-Use-local-vc4_hdmi-directly.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0578-drm-vc4-hdmi-Add-container_of-macros-for-encoders-an.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0579-drm-vc4-hdmi-Pass-vc4_hdmi-to-CEC-code.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0580-drm-vc4-hdmi-Remove-vc4_dev-hdmi-pointer.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0581-drm-vc4-hdmi-Remove-vc4_hdmi_connector.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0582-drm-vc4-hdmi-Introduce-resource-init-and-variant.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0583-drm-vc4-hdmi-Implement-a-register-layout-abstraction.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0584-drm-vc4-hdmi-Add-reset-callback.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0585-drm-vc4-hdmi-Add-PHY-init-and-disable-function.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0586-drm-vc4-hdmi-Add-PHY-RNG-enable-disable-function.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0587-drm-vc4-hdmi-Add-a-CSC-setup-callback.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0588-drm-vc4-hdmi-Add-a-set_timings-callback.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0589-drm-vc4-hdmi-Add-HDMI-ID.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0590-drm-vc4-hdmi-Deal-with-multiple-debugfs-files.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0591-drm-vc4-hdmi-Add-an-audio-support-flag.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0592-drm-vc4-hdmi-Move-CEC-init-to-its-own-function.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0593-drm-vc4-hdmi-Add-CEC-support-flag.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0594-drm-vc4-hdmi-Remove-unused-CEC_CLOCK_DIV-define.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0595-drm-vc4-hdmi-Rename-drm_encoder-pointer-in-mode_vali.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0596-drm-vc4-hdmi-Adjust-HSM-clock-rate-depending-on-pixe.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0597-drm-vc4-hdmi-Support-the-BCM2711-HDMI-controllers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0598-dt-bindings-display-vc4-hdmi-Add-BCM2711-HDMI-contro.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0599-ARM-dts-bcm2711-Enable-the-display-pipeline.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0600-DOWNSTREAM-ARM-dts-rpi4-Disable-KMS-driver-by-defaul.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0601-dtoverlays-Add-Pi4-version-of-vc4-kms-v3d.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0602-drm-Checking-of-the-pitch-is-only-valid-for-linear-f.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0603-drm-vc4-Add-support-for-DRM_FORMAT_P030-to-vc4-plane.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0604-Fixup-P030-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0605-drm-vc4-The-check-for-assigned-HVS-channels-is-not-a.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0606-dt-Update-v3d-to-use-firmware_clocks.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0607-drm-vc4-Reset-audio-infoframe-on-encoder_enable-if-p.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0608-drm-vc4-Set-the-b-frame-marker-to-the-match-ALSA-s-d.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0609-dts-Add-reg-names-for-the-HDMI-registers-on-bcm2835.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0610-dt-Add-HDMI-audio-dma-values-to-bcm2711.dtsi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0611-drm-vc4-Use-reg-names-to-configure-HDMI-audio.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0612-drm-vc4-Add-audio-initialisation-for-Pi4.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0613-drm-vc4-Enable-audio-on-Pi4.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0614-drm-vc4-Alter-the-HDMI-state-machine-clock-calc-to-a.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0615-dtoverlays-Remove-comment-about-vc4-kms-v3d-locking-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0616-drm-vc4-Kick-the-core-clock-up-during-a-mode-change.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0617-drm-vc4-Fixup-for-firmware-KMS.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0618-drm-vc4-Fixup-plane-init-within-firmware-kms.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0619-drm-vc4-hdmi-Give-the-HDMI-audio-instances-different.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0620-i2c-brcmstb-The-interrupt-line-is-optional-so-use-pl.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0621-dt-Drop-I2C-for-Pi4-HDMI-interfaces-to-97.5kHz.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0622-overlays-Add-missing-rpi-poe-parameters.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0623-vc4_hdmi_phy-Fix-offset-calculation.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0624-overlays-Add-overlay_map.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0625-overlays-Formally-rename-deprecate-old-overlays.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0626-overlays-Add-vc4-kms-v3d-pi4-to-overlay_map.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0627-Add-upstream-and-upstream-pi4-to-overlay_map.patch
 rename target/linux/bcm27xx/patches-5.4/{950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch => 950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch} (81%)
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0629-Add-support-for-the-AudioInjector.net-Isolated-sound.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0630-overlays-Fix-dtc-warnings-in-i2c-gpio.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0631-kbuild-Disable-gcc-plugins.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0632-ASoC-ma120x0p-Add-96KHz-rate-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0633-arm64-mm-reserve-CMA-and-crashkernel-in-ZONE_DMA32.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0634-arm64-mm-Fix-initialisation-of-DMA-zones-on-non-NUMA.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0635-ARM-dts-bcm283x-Unify-CMA-configuration.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0636-dma-contiguous-CMA-give-precedence-to-cmdline.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0637-ARM-dts-Use-upstream-CMA-configuration.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0638-ARM-dts-overlays-Unify-overlay-CMA-handling.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0639-ARM-dts-bcm283x-Fix-vc4-s-firmware-bus-DMA-limitatio.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0640-ARM-dts-bcm2711-Restrict-CMA-to-first-768MB.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0641-ARM-dts-Extend-SCB-bus-address-range.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0642-dts-bcm2711-Move-emmc2-to-its-own-bus.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0643-drm-vc4-hdmi-Silence-pixel-clock-error-on-EPROBE_DEF.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0644-component-Silence-bind-error-on-EPROBE_DEFER.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0645-Fixes-a-problem-with-clock-settings-of-HiFiBerry-DAC.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0646-Documentation-media-Update-sub-device-API-intro.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0647-Documentation-media-Document-read-only-subdevice.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0648-media-v4l2-dev-Add-v4l2_device_register_ro_subdev_no.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0649-media-bcm2835-unicam-Driver-for-CCP2-CSI2-camera-int.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0650-media-uapi-v4l2-core-Add-sensor-ancillary-data-V4L2-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0651-media-uapi-Add-MEDIA_BUS_FMT_SENSOR_DATA-media-bus-f.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0652-media-bcm2835-unicam-Add-support-for-mulitple-device.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0653-media-bcm2835-unicam-Add-embedded-data-node.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0654-media-bcm2835-unicam-Use-dummy-buffer-if-none-have-b.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0655-spi-Force-CS_HIGH-if-GPIO-descriptors-are-used.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0656-media-i2c-imx219-Fix-power-sequence.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0657-media-i2c-imx219-Add-support-for-RAW8-bit-bayer-form.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0658-media-i2c-imx219-Add-support-for-cropped-640x480-res.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0659-media-i2c-imx219-Fix-a-bug-in-imx219_enum_frame_size.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0660-media-bcm2835-unicam-Disable-event-related-ioctls-on.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0661-media-bcm2835-unicam-Add-support-for-the-FRAME_SYNC-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0662-Revert-firmware-raspberrypi-register-clk-device.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0663-media-imx219-Advertise-embedded-data-node-on-media-p.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0664-dts-bcm2711-EMMC2-can-address-the-whole-first-GB.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0665-driver-char-rpivid-Remove-legacy-name-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0666-driver-char-rpivid-Don-t-map-more-than-wanted.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0667-dt-Implement-an-I2C-pinctrl-mux-for-BSC0.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0668-dtoverlays-Update-CSI-overlays-to-use-i2c_csi_dsi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0669-dt-Update-all-mainline-bcm283x-dt-files-for-i2c0-pin.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0670-ARM-dts-Create-bcm2708-rpi-b-rev1.dts.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0671-dts-bcm2711-set-size-cells-2.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0672-dts-bcm2711-add-High-Peripheral-mode-overlay.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0673-Revert-spi-spidev-Fix-CS-polarity-if-GPIO-descriptor.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0674-spi-use_gpio_descriptor-fixup-moved-to-spi_setup.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0675-overlays-rpivid-v4l2-also-needs-size-cells-2.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0676-media-bcm2835-unicam-Re-fetch-mbus-code-from-subdev-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0677-uapi-bcm2835-isp-Add-bcm2835-isp-uapi-header-file.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0678-media-uapi-v4l2-core-Add-ISP-statistics-output-V4L2-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0679-media-uapi-v4l-ctrls-Add-CID-base-for-the-bcm2835-is.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0680-staging-mmal-vchiq-Fix-formatting-errors-in-mmal_par.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0681-staging-vc04_services-ISP-Add-a-more-complex-ISP-pro.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0682-staging-vchiq-Load-bcm2835_isp-driver-from-vchiq.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0683-vc4_hvs-Mark-core-clock-as-optional.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0684-vc4_hdmi-BCM2835-requires-a-fixed-hsm-clock-for-CEC-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0685-media-i2c-imx219-Implement-get_selection.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0686-media-i2c-ov5647-Add-support-for-g_selection-to-refl.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0687-media-i2c-ov5467-Fixup-error-path-to-release-mutex.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0688-media-i2c-ov5647-Support-V4L2_CID_PIXEL_RATE.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0689-media-i2c-ov5647-Set-V4L2_SUBDEV_FL_HAS_EVENTS-flag.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0690-media-i2c-ov5647-Add-support-for-V4L2_CID_VBLANK.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0691-media-i2c-ov5647-Neither-analogue-gain-nor-exposure-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0692-media-i2c-ov5647-Use-member-names-in-mode-tables.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0693-media-i2c-ov5647-Advertise-the-correct-exposure-rang.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0694-media-i2c-imx219-Declare-that-the-driver-can-create-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0695-media-bcm2835-unicam-Add-support-for-VIDIOC_-S-G-_SE.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0696-media-bcm2835-unicam-Do-not-stop-streaming-in-unicam.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0697-media-bcm2835-unicam-Fix-reference-counting-in-unica.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0698-staging-vc04_services-ISP-Add-enum_framesizes-ioctl.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0699-SQUASH-spi-Demote-SPI_CS_HIGH-warning-to-KERN_DEBUG.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0700-bcm2835-dma-Add-proper-40-bit-DMA-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0701-ARM-dts-bcm2711-Allow-40-bit-DMA-for-SPI.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0702-overlays-Make-the-i2c-gpio-overlay-safe-again.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0703-staging-vc04_services-isp-Remove-duplicated-initiali.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0704-staging-vc04_services-isp-Make-all-references-to-bcm.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0705-overlays-gpio-keys-Avoid-open-drain-warnings.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0706-vc4_hdmi_phy-Fix-typo-in-phy_get_cp_current.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0707-overlays-Make-use-of-intra-overlay-fragments.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0708-media-i2c-tc358743-Fix-fallthrough-warning.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0709-media-bcm2835-unicam-Fix-uninitialized-warning.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0710-video-bcm2708_fb-Disable-FB-if-no-displays-found.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0711-overlays-sc16is752-spi1-Add-xtal-parameter.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0712-vc4_hdmi-Fix-register-offset-when-sending-longer-CEC.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0713-vc4_hdmi-Fix-up-CEC-registers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0714-vc4_hdmi_regs-Add-Intr2-register-block.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0715-vc4_hdmi_regs-Make-interrupt-mask-variant-specific.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0716-vc4_hdmi-Make-irq-shared.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0717-vc4_hdmi-Adjust-CEC-ref-clock-based-on-its-input-clo.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0718-vc4_hdmi-Remove-cec_available-flag-as-always-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0719-overlays-tc358743-Use-intra-overlay-fragments.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0720-overlays-Move-fixed-clock-nodes-to-the-root.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0721-raspberrypi-dts-Switch-to-discrete-ALSA-devices.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0722-dt-bindings-media-i2c-Add-IMX477-CMOS-sensor-binding.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0723-dtoverlays-Add-IMX477-sensor-overlay.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0724-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0725-media-i2c-imx477-Add-support-for-adaptive-frame-cont.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0726-udmabuf-Remove-deleted-map-unmap-handlers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0727-udmabuf-use-cache_sgt_mapping-option.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0728-udmabuf-add-a-pointer-to-the-miscdevice-in-dma-buf-p.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0729-udmabuf-separate-out-creating-destroying-scatter-tab.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0730-udmabuf-implement-begin_cpu_access-end_cpu_access-ho.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0731-udmabuf-fix-dma-buf-cpu-access.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0732-dma-buf-Add-dma-buf-heaps-framework.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0733-dma-buf-heaps-Add-heap-helpers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0734-dma-buf-heaps-Add-system-heap-to-dmabuf-heaps.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0735-dma-buf-heaps-Add-CMA-heap-to-dmabuf-heaps.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0736-kselftests-Add-dma-heap-test.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0737-dma-buf-heaps-Use-_IOCTL_-for-userspace-IOCTL-identi.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0738-dma-buf-heaps-Remove-redundant-heap-identifier-from-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0739-dma-buf-fix-resource-leak-on-ENOTTY-error-return-pat.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0740-dma-heap-Make-the-symbol-dma_heap_ioctl_cmds-static.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0741-ARM-dts-Enable-firmware-clocks-on-all-Pis.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0742-media-bcm2835-unicam-Always-service-interrupts.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0743-sc16is7xx-Fix-for-hardware-flow-control.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0744-drm-vc4-Fix-VIC-usage-with-Broadcast-RGB.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0745-staging-vc04_services-mmal-vchiq-Update-parameters-l.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0746-staging-vc04_services-bcm2835-codec-Request-headers-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0747-staging-vc04_services-bcm2835-codec-Avoid-fragmentin.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0748-staging-vc04_services-bcm2835-camera-Request-headers.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0749-overlays-Fix-audio-parameter-of-vc4-kms-v3d.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0750-Switch-to-snd_soc_dai_set_bclk_ratio.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0751-media-bcm2835-unicam-Retain-packing-information-on-G.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0752-zswap-Defer-zswap-initialisation.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0753-drm-vc4-Adopt-the-dma-configuration-from-the-HVS-or-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0754-drm-vc4-Add-FKMS-as-an-acceptable-node-for-dma-range.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0755-media-i2c-imx477-Return-correct-result-on-sensor-id-.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0756-staging-vchiq_arm-Clean-up-40-bit-DMA-support.patch
 create mode 100644 target/linux/bcm27xx/patches-5.4/950-0757-ARM-dts-Update-for-new-VCHIQ-BCM2711-DMA-support.patch

diff --git a/target/linux/bcm27xx/bcm2708/config-5.4 b/target/linux/bcm27xx/bcm2708/config-5.4
index 1f12a36ee72..c4802bef7dd 100644
--- a/target/linux/bcm27xx/bcm2708/config-5.4
+++ b/target/linux/bcm27xx/bcm2708/config-5.4
@@ -43,6 +43,7 @@ CONFIG_ARM_ERRATA_411920=y
 CONFIG_ARM_HAS_SG_CHAIN=y
 CONFIG_ARM_L1_CACHE_SHIFT=5
 CONFIG_ARM_PATCH_PHYS_VIRT=y
+# CONFIG_ARM_RASPBERRYPI_CPUFREQ is not set
 # CONFIG_ARM_SCMI_PROTOCOL is not set
 CONFIG_ARM_THUMB=y
 CONFIG_ARM_TIMER_SP804=y
@@ -79,7 +80,7 @@ CONFIG_CC_HAS_KASAN_GENERIC=y
 CONFIG_CLKDEV_LOOKUP=y
 CONFIG_CLKSRC_MMIO=y
 CONFIG_CLK_BCM2835=y
-# CONFIG_CLK_RASPBERRYPI is not set
+CONFIG_CLK_RASPBERRYPI=y
 CONFIG_CLONE_BACKWARDS=y
 CONFIG_CMA=y
 CONFIG_CMA_ALIGNMENT=8
@@ -136,6 +137,9 @@ CONFIG_DCACHE_WORD_ACCESS=y
 CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
 CONFIG_DMADEVICES=y
 CONFIG_DMA_BCM2708=y
 CONFIG_DMA_BCM2835=y
@@ -249,6 +253,7 @@ CONFIG_HZ_FIXED=0
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_BRCMSTB is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_INPUT=y
@@ -342,6 +347,7 @@ CONFIG_REGMAP_MMIO=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
 # CONFIG_RPIVID_MEM is not set
 CONFIG_SCSI=y
 # CONFIG_SCSI_LOWLEVEL is not set
diff --git a/target/linux/bcm27xx/bcm2709/config-5.4 b/target/linux/bcm27xx/bcm2709/config-5.4
index c1630c599d8..67019438e6f 100644
--- a/target/linux/bcm27xx/bcm2709/config-5.4
+++ b/target/linux/bcm27xx/bcm2709/config-5.4
@@ -183,6 +183,9 @@ CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
 CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
 CONFIG_DIMLIB=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
 CONFIG_DMADEVICES=y
 CONFIG_DMA_BCM2708=y
 CONFIG_DMA_BCM2835=y
@@ -317,6 +320,7 @@ CONFIG_HZ_FIXED=0
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_BRCMSTB is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_INPUT=y
@@ -441,6 +445,7 @@ CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_GPIO=y
 CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
 CONFIG_RFS_ACCEL=y
 # CONFIG_RPIVID_MEM is not set
 CONFIG_RPS=y
diff --git a/target/linux/bcm27xx/bcm2710/config-5.4 b/target/linux/bcm27xx/bcm2710/config-5.4
index 4a5b491b0fc..c83149212d0 100644
--- a/target/linux/bcm27xx/bcm2710/config-5.4
+++ b/target/linux/bcm27xx/bcm2710/config-5.4
@@ -224,6 +224,9 @@ CONFIG_CRYPTO_XTS=y
 CONFIG_DCACHE_WORD_ACCESS=y
 CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
 CONFIG_DMADEVICES=y
 CONFIG_DMA_BCM2708=y
 CONFIG_DMA_BCM2835=y
@@ -232,6 +235,7 @@ CONFIG_DMA_DIRECT_REMAP=y
 CONFIG_DMA_ENGINE=y
 CONFIG_DMA_OF=y
 CONFIG_DMA_REMAP=y
+CONFIG_DMA_SHARED_BUFFER=y
 CONFIG_DMA_VIRTUAL_CHANNELS=y
 CONFIG_DNOTIFY=y
 CONFIG_DRM_RCAR_WRITEBACK=y
@@ -372,6 +376,7 @@ CONFIG_HZ_250=y
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_BRCMSTB is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
@@ -513,6 +518,7 @@ CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_GPIO=y
 CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
 CONFIG_RFS_ACCEL=y
 CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
 # CONFIG_RPIVID_MEM is not set
diff --git a/target/linux/bcm27xx/bcm2711/config-5.4 b/target/linux/bcm27xx/bcm2711/config-5.4
index abf6e8844a1..edc04e6e627 100644
--- a/target/linux/bcm27xx/bcm2711/config-5.4
+++ b/target/linux/bcm27xx/bcm2711/config-5.4
@@ -229,6 +229,9 @@ CONFIG_DCACHE_WORD_ACCESS=y
 CONFIG_DEBUG_BUGVERBOSE=y
 CONFIG_DEBUG_INFO=y
 CONFIG_DIMLIB=y
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_CMA=y
+CONFIG_DMABUF_HEAPS_SYSTEM=y
 CONFIG_DMADEVICES=y
 CONFIG_DMA_BCM2708=y
 CONFIG_DMA_BCM2835=y
@@ -237,6 +240,7 @@ CONFIG_DMA_DIRECT_REMAP=y
 CONFIG_DMA_ENGINE=y
 CONFIG_DMA_OF=y
 CONFIG_DMA_REMAP=y
+CONFIG_DMA_SHARED_BUFFER=y
 CONFIG_DMA_VIRTUAL_CHANNELS=y
 CONFIG_DNOTIFY=y
 CONFIG_DRM_RCAR_WRITEBACK=y
@@ -378,6 +382,7 @@ CONFIG_HZ_250=y
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_BRCMSTB is not set
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
@@ -523,6 +528,7 @@ CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_GPIO=y
 CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
 CONFIG_RFS_ACCEL=y
 CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
 # CONFIG_RPIVID_MEM is not set
diff --git a/target/linux/bcm27xx/modules/sound.mk b/target/linux/bcm27xx/modules/sound.mk
index 8d449a51fa2..a4aeb629c34 100644
--- a/target/linux/bcm27xx/modules/sound.mk
+++ b/target/linux/bcm27xx/modules/sound.mk
@@ -268,6 +268,33 @@ endef
 $(eval $(call KernelPackage,sound-soc-allo-katana-codec))
 
 
+define KernelPackage/sound-soc-audioinjector-isolated-soundcard
+  TITLE:=Support for AudioInjector Isolated soundcard
+  KCONFIG:= \
+    CONFIG_SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD \
+    CONFIG_SND_SOC_CS4271 \
+    CONFIG_SND_SOC_CS4271_I2C
+  FILES:= \
+    $(LINUX_DIR)/sound/soc/bcm/snd-soc-audioinjector-isolated-soundcard.ko \
+    $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm8731.ko
+  AUTOLOAD:=$(call AutoLoad,68,snd-soc-cs4271.o \
+    snd-soc-cs4271-i2c \
+    snd-soc-audioinjector-isolated-soundcard)
+  DEPENDS:= \
+    kmod-sound-soc-bcm2835-i2s \
+    +kmod-i2c-bcm2835 \
+    +kmod-regmap-i2c \
+    +kmod-regmap-spi
+  $(call AddDepends/sound)
+endef
+
+define KernelPackage/sound-soc-audioinjector-isolated-soundcard/description
+  This package contains support for AudioInjector Isolated soundcard
+endef
+
+$(eval $(call KernelPackage,sound-soc-audioinjector-isolated-soundcard))
+
+
 define KernelPackage/sound-soc-audioinjector-octo-soundcard
   TITLE:=Support for AudioInjector Octo soundcard
   KCONFIG:= \
@@ -884,6 +911,28 @@ endef
 $(eval $(call KernelPackage,sound-soc-rpi-dac))
 
 
+define KernelPackage/sound-soc-merus-amp
+  TITLE:=Support for Infineon Merus Amp
+  KCONFIG:= \
+    CONFIG_SND_SOC_MA120X0P
+  FILES:= \
+    $(LINUX_DIR)/sound/soc/codecs/snd-soc-ma120x0p.ko
+  AUTOLOAD:=$(call AutoLoad,68,snd-soc-ma120x0p)
+  DEPENDS:= \
+    kmod-sound-soc-bcm2835-i2s \
+    +kmod-sound-soc-rpi-simple-soundcard \
+    +kmod-i2c-bcm2835 \
+    +kmod-regmap-i2c
+  $(call AddDepends/sound)
+endef
+
+define KernelPackage/sound-soc-merus-amp/description
+  This package contains support for Infineon Merus Amp
+endef
+
+$(eval $(call KernelPackage,sound-soc-merus-amp))
+
+
 define KernelPackage/sound-soc-rpi-proto
   TITLE:=Support for RPi-PROTO
   KCONFIG:= \
diff --git a/target/linux/bcm27xx/modules/video.mk b/target/linux/bcm27xx/modules/video.mk
index f1da7d83211..f7286cee3c7 100644
--- a/target/linux/bcm27xx/modules/video.mk
+++ b/target/linux/bcm27xx/modules/video.mk
@@ -33,12 +33,13 @@ define KernelPackage/drm-vc4
     +kmod-sound-soc-core
   KCONFIG:= \
     CONFIG_DRM_VC4 \
-    CONFIG_DRM_VC4_HDMI_CEC=n \
+    CONFIG_DRM_VC4_HDMI_CEC=y \
     CONFIG_DRM_V3D=n \
     CONFIG_DRM_TVE200=n
   FILES:= \
     $(LINUX_DIR)/drivers/gpu/drm/vc4/vc4.ko \
-    $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko
+    $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko \
+    $(LINUX_DIR)/drivers/media/cec/cec.ko
   AUTOLOAD:=$(call AutoProbe,vc4)
 endef
 
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 4529471b6c0..00000000000
--- 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0352-v3d_gem-Kick-the-clock-so-firmware-knows-we-are-usin.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0355-clk-bcm2835-Disable-v3d-clock.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0356-raspberrypi-cpufreq-Only-report-integer-pll-divisor-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0357-arm-dts-Correct-Pi-4B-LED-values.patch
rename to 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 b2a9363ad43..00000000000
--- 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0358-drm-v3d-Set-dma_mask-as-well-as-coherent_dma_mask.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0359-arm-dts-2711-Add-pcie0-alias.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0360-rpi-cirrus-wm5102-overlay-fix-pinctrl-configuration.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0361-staging-vchiq_arm-Set-up-dma-ranges-on-child-devices.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0362-staging-vchiq-Use-the-old-dma-controller-for-OF-conf.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0363-dwc_otg-checking-the-urb-transfer_buffer-too-early-3.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0364-overlays-Make-mcp342x-run-time-compatible.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0365-rpi-cirrus-wm5102-overlay-use-reset-gpios-instead-of.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0366-sound-soc-only-first-codec-is-master-in-multicodec-s.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0367-Allow-simultaneous-use-of-JustBoom-DAC-and-Digi.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0368-overlays-dht11-Allow-multiple-instantiation.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0369-overlays-i2c-rtc-Add-pcf85363-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0370-pinctrl-bcm2835-Remove-gpiochip-on-error.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0371-pinctrl-bcm2835-Change-init-order-for-gpio-hogs.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0372-Pisound-MIDI-communication-fixes-for-scaled-down-CPU.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0373-ARM-dts-bcm283x-Remove-simple-bus-from-fixed-clocks.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0374-ARM-dts-bcm283x-Move-system-timer-back-to-bcm283x.dt.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0375-ARM-dts-bcm283x-Move-pixelvalve-to-bcm2835-common.dt.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0376-ARM-dts-bcm2838-rpi-4-b-Fix-memory-node.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0377-ARM-dts-bcm2838-rpi-4-b-Backport-BT-part-from-upstre.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0378-ARM-dts-bcm2838-Backport-node-names-from-upstream.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0379-ARM-dts-bcm283x-Move-intc-label-to-bcm2835-common.dt.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0380-ARM-dts-bcm2838-Remove-always-on-from-armv7-timer.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0381-net-bcmgenet-Add-RGMII_RXID-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0382-ARM-dts-bcm2838-Backport-genet-from-upstream.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0383-ARM-bcm-Backport-BCM2711-support-from-upstream.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0384-hwrng-iproc-rng200-Add-support-for-BCM2711.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0385-ARM-dts-bcm2838-Add-upstream-RNG-compatible.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0386-driver-char-rpivid-Destroy-the-legacy-device-on-remo.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0387-driver-char-rpivid-Clean-up-error-handling-use-of-ER.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0388-driver-char-rpivid-Add-error-handling-to-the-legacy-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0389-driver-char-rpivid-Fix-coding-style-whitespace-issue.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0390-driver-char-rpimem-Add-SPDX-licence-header.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0391-driver-char-rpivid-Fix-access-to-freed-memory.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0392-add-BME680-to-i2c-sensor-overlay.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0393-dwc_otg-constrain-endpoint-max-packet-and-transfer-s.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0394-dwc_otg-fiq_fsm-pause-when-cancelling-split-transact.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0395-dwc_otg-fiq_fsm-add-a-barrier-on-entry-into-FIQ-hand.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0396-Add-universal-device-tree-overlay-for-SPI-devices.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0397-sound-Add-the-HiFiBerry-DAC-HD-version.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0398-Initialise-rpi-firmware-before-clk-bcm2835.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0399-Fix-master-mode-settings-of-HiFiBerry-DAC-ADC-PRO-ca.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0400-overlays-Use-preferred-compatible-strings.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0401-tty-amba-pl011-Add-un-throttle-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0402-Fix-i2c-pwm-pca9685a-overlay.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0403-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-PRO-sound-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0404-adds-LED-OFF-feature-to-HiFiBerry-DAC-ADC-sound-card.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0405-adds-LED-OFF-feature-to-HiFiBerry-DAC-DAC-PRO-sound-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0406-pisound-Added-reading-Pisound-board-hardware-revisio.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0407-mmc-sdhci-iproc-Fix-vmmc-regulators-on-iProc.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0408-ARM-dts-Declare-RPi-4B-SD-card-power-regulator.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0409-bcm2838.dtsi-Use-BCM2711-PCIe-compatible-string.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0410-ARM-dts-Remove-bcm2838-rpi-4-b.dts.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0411-tty-amba-pl011-Avoid-rare-write-when-full-error.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0412-usb-xhci-Raspberry-Pi-FW-loader-for-VIA-VL805.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0413-overlays-Correct-the-eth_led-colour-assignments.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0414-ARM-dts-Add-sd_poll_once-dtparam-to-bcm283x-2711.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0415-overlays-Add-ssd1306-spi-ssh1106-spi-ssd-1351-spi.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0416-overlays-dwc2-Increase-RX-FIFO-size.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0417-overlays-Fix-mcp23017-s-addr-parameter.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0418-SQUASH-Fix-spi-driver-compiler-warnings.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0419-overlays-add-hdmi-backlight-hwhack-gpio-overlay.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0420-ARM-dts-Revert-all-changes-to-upstream-dts-files.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0421-ARM-dts-Clean-out-downstream-BCM2711-2838-files.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0422-ARM-dts-Add-minimal-Raspberry-Pi-4-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0423-ARM-dts-bcm2711-force-CMA-into-first-GB-of-memory.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0424-ARM-dts-bcm2711-rpi-4-Enable-GENET-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0425-ARM-dts-bcm2711-fix-soc-s-node-dma-ranges.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0426-ARM-dts-Rebuild-downstream-DTS-files.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0427-staging-vchiq_arm-Fix-bcm2711-compatible-string.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0428-thermal-brcmstb_thermal-Correct-SoC-name.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0429-hwrng-iproc-rng200-Correct-SoC-name.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0430-ARM-dts-Correct-SoC-name.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0431-ARM-dts-Remove-CMA-allocation-from-Pi-4-dts.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0432-staging-vchiq_arm-Give-vchiq-children-DT-nodes.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0433-staging-vchiq_arm-Add-a-matching-unregister-call.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0434-ARM-dts-Move-audio-node-under-the-vchiq-parent.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch
rename to 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
similarity index 100%
rename from target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch
rename to 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 00000000000..41fa6cb3d2c
--- /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 00000000000..01bdfee303e
--- /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 00000000000..2716a13d25d
--- /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 00000000000..76ce7403da9
--- /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 00000000000..a21035b69be
--- /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 00000000000..f3587060d18
--- /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 00000000000..d622d9ded55
--- /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 00000000000..5c7cb2b4375
--- /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 00000000000..55f86ae3407
--- /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 00000000000..32fc94c675f
--- /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 00000000000..425830d5c43
--- /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 00000000000..e193b7906ad
--- /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 00000000000..e4a129f2336
--- /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 00000000000..7c887795ee7
--- /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 00000000000..85b6f4a6e21
--- /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 00000000000..09f023e09ce
--- /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 00000000000..7822ff0d119
--- /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 00000000000..1a6c9813de0
--- /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 00000000000..38720b89bdc
--- /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 00000000000..633cf187825
--- /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 00000000000..99fdf8fbd61
--- /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 00000000000..eaa4a811413
--- /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 00000000000..098903d2e11
--- /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 00000000000..3df4fde4394
--- /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 00000000000..035c9d6aa0a
--- /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 00000000000..ca87cbac839
--- /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 00000000000..23105057a9f
--- /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 00000000000..c4d230e7308
--- /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 00000000000..3e47c475ffc
--- /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 00000000000..2150f0abecb
--- /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 00000000000..4cb313f1529
--- /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 00000000000..5ef4eaeab9f
--- /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 00000000000..2499dee0c93
--- /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 00000000000..16f8b0ad820
--- /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 00000000000..00bac3a2b93
--- /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 00000000000..0f6259e347d
--- /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 00000000000..df7a98fc548
--- /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 00000000000..7c3c4708498
--- /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 00000000000..b2e9f15647b
--- /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 00000000000..e917410b864
--- /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 00000000000..54d04e59e15
--- /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 00000000000..08383e37084
--- /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 00000000000..8b8eeea7374
--- /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 00000000000..6108f867877
--- /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 00000000000..5949e616f9c
--- /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 00000000000..b517b5414bb
--- /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 00000000000..3deefc361f0
--- /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 00000000000..83c49132a37
--- /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 00000000000..cf854fceb88
--- /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 00000000000..6f77bace017
--- /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 00000000000..c5f06f20ea8
--- /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 00000000000..d470f3b7f07
--- /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 00000000000..e920a0be647
--- /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 00000000000..77952c0eea9
--- /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 00000000000..17362dafe35
--- /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 00000000000..00ad755492b
--- /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 00000000000..5704952569b
--- /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 00000000000..bd487df5c44
--- /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 00000000000..6bf6ab74c43
--- /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 00000000000..3ff57ef3df3
--- /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 00000000000..89d8aae28d0
--- /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 00000000000..430fc0459ea
--- /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 00000000000..71125e3e25d
--- /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 00000000000..75c44a564a8
--- /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 00000000000..53296ecb4b3
--- /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 00000000000..9b60fbb72bc
--- /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 00000000000..1d70ca851cc
--- /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 00000000000..b0abafc6998
--- /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 00000000000..ff9fa1a1580
--- /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 00000000000..4ef3d69b2cb
--- /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 00000000000..82fae402465
--- /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 00000000000..eaeca92bfd3
--- /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 00000000000..53dd3e44053
--- /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 00000000000..6db26d40aad
--- /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 00000000000..cea17a621e1
--- /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 00000000000..c9650bca080
--- /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 00000000000..a39a8ac1095
--- /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 00000000000..b6de732379d
--- /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 00000000000..746eff91d51
--- /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 00000000000..cc6cd868876
--- /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 00000000000..533eb89e7a2
--- /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 00000000000..33ecf44a27c
--- /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 00000000000..4befec6c050
--- /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 00000000000..5c0ab9768b3
--- /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 00000000000..4dba81b0353
--- /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 00000000000..d511f1e212a
--- /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 00000000000..c41e063fde9
--- /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 00000000000..aae9b9e00d2
--- /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 00000000000..b27d3553405
--- /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 00000000000..1c46fa44850
--- /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 00000000000..3f0aa4aa02d
--- /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 00000000000..9d83d8a3dde
--- /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 00000000000..a24127e6f28
--- /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 00000000000..00a65a9b111
--- /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 00000000000..b67f38782fe
--- /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 00000000000..56106a42743
--- /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 00000000000..4827f960231
--- /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 00000000000..65e206c1813
--- /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 00000000000..9a9fd06eebc
--- /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 00000000000..2504a07083e
--- /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 00000000000..6ec6850866d
--- /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 00000000000..78340ced705
--- /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 00000000000..f576b8624e1
--- /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 00000000000..a05bf055ff4
--- /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 00000000000..871d86a7a98
--- /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 00000000000..62080bffec5
--- /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 00000000000..8edba988f2b
--- /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 00000000000..a7b5b5fad3c
--- /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 00000000000..89285ad25c1
--- /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 00000000000..cd18cd1309a
--- /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 00000000000..716678f078e
--- /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 00000000000..83d8d25e29f
--- /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 00000000000..20f548328c4
--- /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 00000000000..edcb3792afc
--- /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 00000000000..4fd5879ec8b
--- /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 00000000000..e76367b9da7
--- /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
similarity index 81%
rename from target/linux/bcm27xx/patches-5.4/950-0353-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch
rename to target/linux/bcm27xx/patches-5.4/950-0628-clk-raspberrypi-Allow-cpufreq-driver-to-also-adjust-.patch
index 31978c761a0..c0422f4ed3b 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 00000000000..e35806962eb
--- /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 00000000000..2da57b81de0
--- /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 00000000000..66091ca591d
--- /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 00000000000..a5aee43aef3
--- /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 00000000000..137a2fa4a02
--- /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 00000000000..654864d52da
--- /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 00000000000..a0dfcb33251
--- /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 00000000000..d3354bb9e10
--- /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 00000000000..fb4fe893c00
--- /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 00000000000..3c0f1e4388c
--- /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 00000000000..31840e5437d
--- /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 00000000000..9a1e29e7d19
--- /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 00000000000..8eb2b16b102
--- /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 00000000000..52cf03a3828
--- /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 00000000000..1b1cb143ae0
--- /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 00000000000..f7b1217527e
--- /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 00000000000..f1427ae71fd
--- /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 00000000000..f843a83a7d8
--- /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 00000000000..1983334ae15
--- /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 00000000000..47f2551ec78
--- /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 00000000000..e142f598ad6
--- /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 00000000000..de8f1d209a8
--- /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 00000000000..65162cc5d51
--- /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 00000000000..315feff5d3d
--- /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 00000000000..a163a6f1a5c
--- /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 00000000000..836ced8a044
--- /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 00000000000..43d10486def
--- /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 00000000000..b85d9178785
--- /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 00000000000..552516bd67d
--- /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 00000000000..3ad377dfbee
--- /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 00000000000..ef8eff6c006
--- /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 00000000000..cb36ce2d969
--- /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 00000000000..544d597a356
--- /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 00000000000..01533d1f3f8
--- /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 00000000000..c4c25d15126
--- /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 00000000000..4c5e50636c8
--- /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 00000000000..a3896be439f
--- /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 00000000000..afc1a5a6a86
--- /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 00000000000..002ad8686e1
--- /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 00000000000..f6b6340c7f9
--- /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 00000000000..f5e4e4b008d
--- /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 00000000000..42067a7816b
--- /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 00000000000..fa5a2d4f546
--- /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 00000000000..e24f5de0006
--- /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 00000000000..fa01cd85326
--- /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 00000000000..8068ecc9c4a
--- /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 00000000000..4a1757dd443
--- /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 00000000000..4d43c97e56c
--- /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 00000000000..9f41783b460
--- /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 00000000000..1af97e83510
--- /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 00000000000..96e6e124af9
--- /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 00000000000..f428ce5e9fd
--- /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 00000000000..38015cc98a1
--- /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 00000000000..70fe392a3be
--- /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 00000000000..816ca3c589f
--- /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 00000000000..0ea80ae6c80
--- /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 00000000000..cb7a0af6052
--- /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 00000000000..66715a108e0
--- /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 00000000000..d8a01047392
--- /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 00000000000..ce16832d35e
--- /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 00000000000..b7a3f19753f
--- /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 00000000000..85d63cf1a3e
--- /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 00000000000..039809e94ed
--- /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 00000000000..d8ef700a593
--- /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 00000000000..9b874485a8a
--- /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 00000000000..fa700a81a3b
--- /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 00000000000..b30d106f6ed
--- /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 00000000000..74abba4ac92
--- /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 00000000000..ba979632ae1
--- /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 00000000000..1673bde514e
--- /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 00000000000..2be5524a07b
--- /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 00000000000..3fb47589ee3
--- /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 00000000000..ac6543fe4fc
--- /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 00000000000..537ca5dc557
--- /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 00000000000..d2b0bf1f2f4
--- /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 00000000000..97ca47bcd05
--- /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 00000000000..579b3b61b61
--- /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 00000000000..45429d506dc
--- /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 00000000000..628bec3f332
--- /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 00000000000..b0e74e5e1b0
--- /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 00000000000..2186d7b8022
--- /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 00000000000..90cb5e276ee
--- /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 00000000000..582c32aa906
--- /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 00000000000..d6aa46a4f4c
--- /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 00000000000..a00aee15bd9
--- /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 00000000000..d750e55abb1
--- /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 00000000000..b92d1dbac92
--- /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 00000000000..dc0fec6c7a4
--- /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 00000000000..da99772696b
--- /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 00000000000..25d5fd95079
--- /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 00000000000..5bfa6bc60a4
--- /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 00000000000..aa19ca11d52
--- /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 00000000000..c5c302f8320
--- /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 00000000000..eb5430bbaea
--- /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 00000000000..0d5ea3c188d
--- /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 00000000000..2a271cc6416
--- /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 00000000000..1da5dea3265
--- /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 00000000000..bc7b5bea911
--- /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 00000000000..166055556fd
--- /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 00000000000..68feae3d486
--- /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 00000000000..5a0a59ced1a
--- /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 00000000000..79f6a673873
--- /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 00000000000..14cfe8aed34
--- /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 00000000000..03d5d0c85ba
--- /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 00000000000..10eb46ca7b0
--- /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 00000000000..55c24508211
--- /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 00000000000..d1828702b4f
--- /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 00000000000..ff4dda9a637
--- /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 00000000000..a9498315256
--- /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 00000000000..288f468a78b
--- /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 00000000000..89b66956e08
--- /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 00000000000..900637ddeb8
--- /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 00000000000..f319bdca9f4
--- /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 00000000000..378824c495d
--- /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 00000000000..3ff77bcea97
--- /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 00000000000..611e5db743d
--- /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 00000000000..fd878ef737e
--- /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 00000000000..7698f822d31
--- /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 00000000000..da273ca65bd
--- /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 00000000000..5d400e25305
--- /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 00000000000..4e69e417104
--- /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 00000000000..b104f00efca
--- /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 00000000000..1e0051be30f
--- /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 00000000000..124a1d95cd9
--- /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 00000000000..2c9449df0fe
--- /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 00000000000..127c5aef119
--- /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 00000000000..4585d2d8dd3
--- /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 00000000000..1dac504877f
--- /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 00000000000..947fb73a39b
--- /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>;
+ };
diff --git a/target/linux/generic/config-5.4 b/target/linux/generic/config-5.4
index 325caec40ed..3f3f2a05c92 100644
--- a/target/linux/generic/config-5.4
+++ b/target/linux/generic/config-5.4
@@ -1346,10 +1346,12 @@ CONFIG_DQL=y
 # CONFIG_DRM_NXP_PTN3460 is not set
 # CONFIG_DRM_OMAP is not set
 # CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
 # CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
 # CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
 # CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
 # CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
 # CONFIG_DRM_PANEL_LG_LB035Q02 is not set
 # CONFIG_DRM_PANEL_LG_LG4573 is not set
 # CONFIG_DRM_PANEL_LVDS is not set
@@ -1357,10 +1359,15 @@ CONFIG_DQL=y
 # CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
 # CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
 # CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
 # CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
 # CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
 # CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
 # CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
 # CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
 # CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
 # CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
@@ -1369,11 +1376,13 @@ CONFIG_DQL=y
 # CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
 # CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
 # CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
 # CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
 # CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
 # CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
 # CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
 # CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
 # CONFIG_DRM_PANFROST is not set
 # CONFIG_DRM_PARADE_PS8622 is not set
 # CONFIG_DRM_PL111 is not set
@@ -1397,6 +1406,7 @@ CONFIG_DQL=y
 # CONFIG_DRM_TOSHIBA_TC358767 is not set
 # CONFIG_DRM_UDL is not set
 # CONFIG_DRM_VBOXVIDEO is not set
+# CONFIG_DRM_VC4_HDMI_CEC is not set
 # CONFIG_DRM_VGEM is not set
 # CONFIG_DRM_VIRTIO_GPU is not set
 # CONFIG_DRM_VKMS is not set
@@ -5139,6 +5149,7 @@ CONFIG_SND_PROC_FS=y
 CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y
 # CONFIG_SND_SOC_JZ4725B_CODEC is not set
 # CONFIG_SND_SOC_JZ4740_CODEC is not set
+# CONFIG_SND_SOC_MA120X0P is not set
 # CONFIG_SND_SOC_MAX9759 is not set
 # CONFIG_SND_SOC_MAX98088 is not set
 # CONFIG_SND_SOC_MAX98357A is not set
@@ -5678,6 +5689,7 @@ CONFIG_TMPFS_XATTR=y
 # CONFIG_TOUCHSCREEN_PENMOUNT is not set
 # CONFIG_TOUCHSCREEN_PIXCIR is not set
 # CONFIG_TOUCHSCREEN_PROPERTIES is not set
+# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set
 # CONFIG_TOUCHSCREEN_RM_TS is not set
 # CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
 # CONFIG_TOUCHSCREEN_RPI_FT5406 is not set
-- 
GitLab