瀏覽代碼

bcm27xx: update 6.1 patches to latest version

Add support for BCM2712 (Raspberry Pi 5).
https://github.com/raspberrypi/linux/commit/3bb5880ab3dd31f75c07c3c33bf29c5d469b28f3
Patches were generated from the diff between linux kernel branch linux-6.1.y
and rpi-6.1.y from raspberry pi kernel source:
- git format-patch linux-6.1.y...rpi-6.1.y

Build system: x86_64
Build-tested: bcm2708, bcm2709, bcm2710, bcm2711
Run-tested: bcm2710/RPi3B, bcm2711/RPi4B

Signed-off-by: Marty Jones <[email protected]>
[Remove applied and reverted patches, squash patches and config commits]
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Marty Jones 1 年之前
父節點
當前提交
2e715fb4fc
共有 100 個文件被更改,包括 41933 次插入74 次删除
  1. 2 5
      target/linux/bcm27xx/bcm2708/config-6.1
  2. 2 5
      target/linux/bcm27xx/bcm2709/config-6.1
  3. 2 8
      target/linux/bcm27xx/bcm2710/config-6.1
  4. 2 8
      target/linux/bcm27xx/bcm2711/config-6.1
  5. 0 41
      target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch
  6. 306 0
      target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch
  7. 28 0
      target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch
  8. 79 0
      target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch
  9. 41 0
      target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch
  10. 23 0
      target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch
  11. 25 0
      target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch
  12. 308 0
      target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch
  13. 137 0
      target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch
  14. 86 0
      target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch
  15. 44 0
      target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch
  16. 105 0
      target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch
  17. 163 0
      target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch
  18. 24 0
      target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch
  19. 120 0
      target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch
  20. 1455 0
      target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch
  21. 329 0
      target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch
  22. 21 0
      target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch
  23. 48 0
      target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch
  24. 68 0
      target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch
  25. 25 0
      target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch
  26. 78 0
      target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch
  27. 162 0
      target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch
  28. 40 0
      target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch
  29. 26 0
      target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch
  30. 21 0
      target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch
  31. 32 0
      target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch
  32. 61 0
      target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch
  33. 21 0
      target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch
  34. 29 0
      target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch
  35. 102 0
      target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch
  36. 25 0
      target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch
  37. 50 0
      target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch
  38. 58 0
      target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch
  39. 36 0
      target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch
  40. 50 0
      target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch
  41. 27 0
      target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch
  42. 286 0
      target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch
  43. 192 0
      target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch
  44. 63 0
      target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch
  45. 15 7
      target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch
  46. 39 0
      target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch
  47. 47 0
      target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch
  48. 26 0
      target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch
  49. 7672 0
      target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch
  50. 282 0
      target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch
  51. 20 0
      target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch
  52. 1324 0
      target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch
  53. 498 0
      target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch
  54. 90 0
      target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch
  55. 3788 0
      target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch
  56. 386 0
      target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch
  57. 47 0
      target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch
  58. 418 0
      target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch
  59. 384 0
      target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch
  60. 37 0
      target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch
  61. 1108 0
      target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch
  62. 42 0
      target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch
  63. 39 0
      target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch
  64. 249 0
      target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch
  65. 442 0
      target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch
  66. 65 0
      target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch
  67. 2208 0
      target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch
  68. 60 0
      target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch
  69. 1666 0
      target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch
  70. 129 0
      target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch
  71. 83 0
      target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch
  72. 83 0
      target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch
  73. 641 0
      target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch
  74. 76 0
      target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch
  75. 355 0
      target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch
  76. 64 0
      target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch
  77. 292 0
      target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch
  78. 2678 0
      target/linux/bcm27xx/patches-6.1/950-0885-drm-Add-RP1-DSI-driver.patch
  79. 1552 0
      target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch
  80. 3078 0
      target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch
  81. 75 0
      target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch
  82. 4527 0
      target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch
  83. 38 0
      target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch
  84. 29 0
      target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch
  85. 59 0
      target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch
  86. 81 0
      target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch
  87. 128 0
      target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch
  88. 88 0
      target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch
  89. 304 0
      target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch
  90. 343 0
      target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch
  91. 69 0
      target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch
  92. 76 0
      target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch
  93. 150 0
      target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch
  94. 55 0
      target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch
  95. 672 0
      target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch
  96. 24 0
      target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch
  97. 19 0
      target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch
  98. 64 0
      target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch
  99. 19 0
      target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch
  100. 328 0
      target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch

+ 2 - 5
target/linux/bcm27xx/bcm2708/config-6.1

@@ -30,7 +30,6 @@ CONFIG_ARM_UNWIND=y
 CONFIG_AUTO_ZRELADDR=y
 CONFIG_BCM2708_VCMEM=y
 # CONFIG_BCM2711_THERMAL is not set
-CONFIG_BCM2835_DEVGPIOMEM=y
 CONFIG_BCM2835_FAST_MEMCPY=y
 CONFIG_BCM2835_MBOX=y
 CONFIG_BCM2835_POWER=y
@@ -151,7 +150,6 @@ CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_FILLRECT=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
 CONFIG_FB_SIMPLE=y
 CONFIG_FIQ=y
 CONFIG_FIXED_PHY=y
@@ -239,8 +237,6 @@ CONFIG_MDIO_DEVRES=y
 CONFIG_MEMFD_CREATE=y
 CONFIG_MEMORY_ISOLATION=y
 CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
 CONFIG_MFD_SYSCON=y
 CONFIG_MIGHT_HAVE_CACHE_L2X0=y
 CONFIG_MIGRATION=y
@@ -266,6 +262,7 @@ CONFIG_NO_HZ=y
 CONFIG_NO_HZ_COMMON=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
 CONFIG_OF=y
 CONFIG_OF_ADDRESS=y
 CONFIG_OF_CONFIGFS=y
@@ -296,7 +293,6 @@ CONFIG_PM_GENERIC_DOMAINS_OF=y
 CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
 CONFIG_PM_OPP=y
 CONFIG_PM_SLEEP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
 CONFIG_POWER_SUPPLY=y
 CONFIG_PREEMPT_NONE_BUILD=y
 CONFIG_PRINTK_TIME=y
@@ -306,6 +302,7 @@ CONFIG_PWM_BCM2835=y
 CONFIG_PWM_SYSFS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
 CONFIG_RASPBERRYPI_POWER=y
 CONFIG_RATIONAL=y
 # CONFIG_RAVE_SP_CORE is not set

+ 2 - 5
target/linux/bcm27xx/bcm2709/config-6.1

@@ -39,7 +39,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y
 CONFIG_AUTO_ZRELADDR=y
 CONFIG_BCM2708_VCMEM=y
 CONFIG_BCM2711_THERMAL=y
-CONFIG_BCM2835_DEVGPIOMEM=y
 CONFIG_BCM2835_MBOX=y
 CONFIG_BCM2835_POWER=y
 # CONFIG_BCM2835_SMI is not set
@@ -186,7 +185,6 @@ CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_FILLRECT=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
 CONFIG_FB_SIMPLE=y
 CONFIG_FIQ=y
 CONFIG_FIXED_PHY=y
@@ -297,8 +295,6 @@ CONFIG_MDIO_DEVRES=y
 CONFIG_MEMFD_CREATE=y
 CONFIG_MEMORY_ISOLATION=y
 CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
 CONFIG_MFD_SYSCON=y
 CONFIG_MICROCHIP_PHY=y
 CONFIG_MIGHT_HAVE_CACHE_L2X0=y
@@ -332,6 +328,7 @@ CONFIG_NO_HZ_COMMON=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_NR_CPUS=4
 CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
 CONFIG_OF=y
 CONFIG_OF_ADDRESS=y
 CONFIG_OF_CONFIGFS=y
@@ -374,7 +371,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
 CONFIG_PM_OPP=y
 CONFIG_PM_SLEEP=y
 CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
 CONFIG_POWER_SUPPLY=y
 CONFIG_PPS=y
 CONFIG_PREEMPT_NONE_BUILD=y
@@ -387,6 +383,7 @@ CONFIG_PWM_SYSFS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RAS=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
 CONFIG_RASPBERRYPI_POWER=y
 CONFIG_RATIONAL=y
 # CONFIG_RAVE_SP_CORE is not set

+ 2 - 8
target/linux/bcm27xx/bcm2710/config-6.1

@@ -60,7 +60,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y
 CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
 CONFIG_BCM2708_VCMEM=y
 # CONFIG_BCM2711_THERMAL is not set
-CONFIG_BCM2835_DEVGPIOMEM=y
 CONFIG_BCM2835_MBOX=y
 CONFIG_BCM2835_POWER=y
 # CONFIG_BCM2835_SMI is not set
@@ -152,7 +151,6 @@ CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
 CONFIG_CRYPTO_LIB_SHA1=y
 CONFIG_CRYPTO_LIB_SHA256=y
 CONFIG_CRYPTO_LIB_UTILS=y
-# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set
 CONFIG_CRYPTO_RNG=y
 CONFIG_CRYPTO_RNG2=y
 CONFIG_CRYPTO_RNG_DEFAULT=y
@@ -161,8 +159,6 @@ CONFIG_CRYPTO_SHA256=y
 CONFIG_CRYPTO_SHA256_ARM64=y
 CONFIG_CRYPTO_SHA512=y
 CONFIG_CRYPTO_SHA512_ARM64=y
-# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set
-# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set
 CONFIG_CRYPTO_XTS=y
 CONFIG_DCACHE_WORD_ACCESS=y
 CONFIG_DEBUG_BUGVERBOSE=y
@@ -195,7 +191,6 @@ CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_FILLRECT=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
 CONFIG_FB_SIMPLE=y
 CONFIG_FIXED_PHY=y
 CONFIG_FIX_EARLYCON_MEM=y
@@ -299,8 +294,6 @@ CONFIG_MDIO_DEVRES=y
 CONFIG_MEMFD_CREATE=y
 CONFIG_MEMORY_ISOLATION=y
 CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
 CONFIG_MFD_SYSCON=y
 CONFIG_MICROCHIP_PHY=y
 CONFIG_MIGRATION=y
@@ -332,6 +325,7 @@ CONFIG_NO_HZ_COMMON=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_NR_CPUS=4
 CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
 CONFIG_OF=y
 CONFIG_OF_ADDRESS=y
 CONFIG_OF_CONFIGFS=y
@@ -368,7 +362,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
 CONFIG_PM_OPP=y
 CONFIG_PM_SLEEP=y
 CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
 CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
 CONFIG_POWER_RESET=y
 CONFIG_POWER_SUPPLY=y
@@ -382,6 +375,7 @@ CONFIG_QUEUED_RWLOCKS=y
 CONFIG_QUEUED_SPINLOCKS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
 CONFIG_RASPBERRYPI_POWER=y
 CONFIG_RATIONAL=y
 # CONFIG_RAVE_SP_CORE is not set

+ 2 - 8
target/linux/bcm27xx/bcm2711/config-6.1

@@ -54,7 +54,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y
 CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
 CONFIG_BCM2708_VCMEM=y
 CONFIG_BCM2711_THERMAL=y
-CONFIG_BCM2835_DEVGPIOMEM=y
 CONFIG_BCM2835_MBOX=y
 CONFIG_BCM2835_POWER=y
 # CONFIG_BCM2835_SMI is not set
@@ -150,7 +149,6 @@ CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
 CONFIG_CRYPTO_LIB_SHA1=y
 CONFIG_CRYPTO_LIB_SHA256=y
 CONFIG_CRYPTO_LIB_UTILS=y
-# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set
 CONFIG_CRYPTO_RNG=y
 CONFIG_CRYPTO_RNG2=y
 CONFIG_CRYPTO_RNG_DEFAULT=y
@@ -159,8 +157,6 @@ CONFIG_CRYPTO_SHA256=y
 CONFIG_CRYPTO_SHA256_ARM64=y
 CONFIG_CRYPTO_SHA512=y
 CONFIG_CRYPTO_SHA512_ARM64=y
-# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set
-# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set
 CONFIG_CRYPTO_XTS=y
 CONFIG_DCACHE_WORD_ACCESS=y
 CONFIG_DEBUG_BUGVERBOSE=y
@@ -194,7 +190,6 @@ CONFIG_FB_CFB_COPYAREA=y
 CONFIG_FB_CFB_FILLRECT=y
 CONFIG_FB_CFB_IMAGEBLIT=y
 CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
 CONFIG_FB_SIMPLE=y
 CONFIG_FIXED_PHY=y
 CONFIG_FIX_EARLYCON_MEM=y
@@ -300,8 +295,6 @@ CONFIG_MDIO_DEVRES=y
 CONFIG_MEMFD_CREATE=y
 CONFIG_MEMORY_ISOLATION=y
 CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
 CONFIG_MFD_SYSCON=y
 CONFIG_MIGRATION=y
 CONFIG_MMC=y
@@ -333,6 +326,7 @@ CONFIG_NO_HZ_COMMON=y
 CONFIG_NO_HZ_IDLE=y
 CONFIG_NR_CPUS=4
 CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
 CONFIG_OF=y
 CONFIG_OF_ADDRESS=y
 CONFIG_OF_CONFIGFS=y
@@ -372,7 +366,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
 CONFIG_PM_OPP=y
 CONFIG_PM_SLEEP=y
 CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
 CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
 CONFIG_POWER_RESET=y
 CONFIG_POWER_SUPPLY=y
@@ -389,6 +382,7 @@ CONFIG_QUEUED_SPINLOCKS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RAS=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
 CONFIG_RASPBERRYPI_POWER=y
 CONFIG_RATIONAL=y
 # CONFIG_RAVE_SP_CORE is not set

+ 0 - 41
target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch

@@ -1,41 +0,0 @@
-From dffb648dffeab7246040a30b7d1669387d1e767e Mon Sep 17 00:00:00 2001
-From: Phil Elwell <[email protected]>
-Date: Tue, 25 Apr 2023 11:49:41 +0100
-Subject: [PATCH] Bluetooth: hci_sync: Add fallback-bd-address prop
-
-The kernel Bluetooth framework understands that devices may not
-be programmed with valid Bluetooth addresses. It also has the ability
-to override a Bluetooth address with the value of the local-bd-address
-DT property, but it ignores the validity of the existing address when
-doing so.
-
-Add a new boolean property, fallback-bd-address, which indicates that
-the given local-bd-address property should only be used if the device
-does not already have a valid BDADDR.
-
-Signed-off-by: Phil Elwell <[email protected]>
----
- net/bluetooth/hci_sync.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
---- a/net/bluetooth/hci_sync.c
-+++ b/net/bluetooth/hci_sync.c
-@@ -4630,6 +4630,7 @@ static const struct {
-  */
- static int hci_dev_setup_sync(struct hci_dev *hdev)
- {
-+	struct fwnode_handle *fwnode = dev_fwnode(hdev->dev.parent);
- 	int ret = 0;
- 	bool invalid_bdaddr;
- 	size_t i;
-@@ -4658,7 +4659,9 @@ static int hci_dev_setup_sync(struct hci
- 
- 	if (!ret) {
- 		if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks) &&
--		    !bacmp(&hdev->public_addr, BDADDR_ANY))
-+		    !bacmp(&hdev->public_addr, BDADDR_ANY) &&
-+		    (invalid_bdaddr ||
-+		     !fwnode_property_present(fwnode, "fallback-bd-address")))
- 			hci_dev_get_bd_addr_from_property(hdev);
- 
- 		if ((invalid_bdaddr ||

+ 306 - 0
target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch

@@ -0,0 +1,306 @@
+From 3ad8e28669e0058e3cec482a47215e50e33f2574 Mon Sep 17 00:00:00 2001
+From: Vinay Varma <[email protected]>
+Date: Sun, 11 Jun 2023 23:45:03 +0800
+Subject: [PATCH] media: i2c: imx219: fix binning and rate_factor for 480p and
+ 1232p
+
+At a high FPS with RAW10, there is frame corruption for 480p because the
+rate_factor of 2 is used with the normal 2x2 bining [1]. This commit
+ties the rate_factor to the selected binning mode. For the 480p mode,
+analog 2x2 binning mode with a rate_factor of 2 is always used. For the
+1232p mode the normal 2x2 binning mode is used for RAW10 while analog
+2x2 binning mode is used for RAW8.
+
+[1] https://github.com/raspberrypi/linux/issues/5493
+
+Signed-off-by: Vinay Varma <[email protected]>
+---
+ drivers/media/i2c/imx219.c | 143 ++++++++++++++++++++++++++-----------
+ 1 file changed, 100 insertions(+), 43 deletions(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -136,6 +136,18 @@ enum pad_types {
+ 	NUM_PADS
+ };
+ 
++enum binning_mode {
++	BINNING_NONE,
++	BINNING_DIGITAL_2x2,
++	BINNING_ANALOG_2x2,
++};
++
++enum binning_bit_depths {
++	BINNING_IDX_8_BIT,
++	BINNING_IDX_10_BIT,
++	BINNING_IDX_MAX
++};
++
+ struct imx219_reg {
+ 	u16 address;
+ 	u8 val;
+@@ -162,11 +174,8 @@ struct imx219_mode {
+ 	/* Default register values */
+ 	struct imx219_reg_list reg_list;
+ 
+-	/* 2x2 binning is used */
+-	bool binning;
+-
+-	/* Relative pixel clock rate factor for the mode. */
+-	unsigned int rate_factor;
++	/* binning mode based on format code */
++	enum binning_mode binning[BINNING_IDX_MAX];
+ };
+ 
+ static const struct imx219_reg imx219_common_regs[] = {
+@@ -404,8 +413,10 @@ static const struct imx219_mode supporte
+ 			.num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
+ 			.regs = mode_3280x2464_regs,
+ 		},
+-		.binning = false,
+-		.rate_factor = 1,
++		.binning = {
++			[BINNING_IDX_8_BIT] = BINNING_NONE,
++			[BINNING_IDX_10_BIT] = BINNING_NONE,
++		},
+ 	},
+ 	{
+ 		/* 1080P 30fps cropped */
+@@ -422,8 +433,10 @@ static const struct imx219_mode supporte
+ 			.num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
+ 			.regs = mode_1920_1080_regs,
+ 		},
+-		.binning = false,
+-		.rate_factor = 1,
++		.binning = {
++			[BINNING_IDX_8_BIT] = BINNING_NONE,
++			[BINNING_IDX_10_BIT] = BINNING_NONE,
++		},
+ 	},
+ 	{
+ 		/* 2x2 binned 30fps mode */
+@@ -440,8 +453,10 @@ static const struct imx219_mode supporte
+ 			.num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
+ 			.regs = mode_1640_1232_regs,
+ 		},
+-		.binning = true,
+-		.rate_factor = 1,
++		.binning = {
++			[BINNING_IDX_8_BIT] = BINNING_ANALOG_2x2,
++			[BINNING_IDX_10_BIT] = BINNING_DIGITAL_2x2,
++		},
+ 	},
+ 	{
+ 		/* 640x480 30fps mode */
+@@ -458,12 +473,10 @@ static const struct imx219_mode supporte
+ 			.num_of_regs = ARRAY_SIZE(mode_640_480_regs),
+ 			.regs = mode_640_480_regs,
+ 		},
+-		.binning = true,
+-		/*
+-		 * This mode uses a special 2x2 binning that doubles the
+-		 * internal pixel clock rate.
+-		 */
+-		.rate_factor = 2,
++		.binning = {
++			[BINNING_IDX_8_BIT] = BINNING_ANALOG_2x2,
++			[BINNING_IDX_10_BIT] = BINNING_ANALOG_2x2,
++		},
+ 	},
+ };
+ 
+@@ -652,12 +665,51 @@ static int imx219_open(struct v4l2_subde
+ 	return 0;
+ }
+ 
++static int imx219_resolve_binning(struct imx219 *imx219,
++				  enum binning_mode *binning)
++{
++	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:
++		*binning = imx219->mode->binning[BINNING_IDX_8_BIT];
++		return 0;
++
++	case MEDIA_BUS_FMT_SRGGB10_1X10:
++	case MEDIA_BUS_FMT_SGRBG10_1X10:
++	case MEDIA_BUS_FMT_SGBRG10_1X10:
++	case MEDIA_BUS_FMT_SBGGR10_1X10:
++		*binning = imx219->mode->binning[BINNING_IDX_10_BIT];
++		return 0;
++	}
++	return -EINVAL;
++}
++
++static int imx219_get_rate_factor(struct imx219 *imx219)
++{
++	enum binning_mode binning = BINNING_NONE;
++	int ret = imx219_resolve_binning(imx219, &binning);
++
++	if (ret < 0)
++		return ret;
++	switch (binning) {
++	case BINNING_NONE:
++	case BINNING_DIGITAL_2x2:
++		return 1;
++	case BINNING_ANALOG_2x2:
++		return 2;
++	}
++	return -EINVAL;
++}
++
+ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ 	struct imx219 *imx219 =
+ 		container_of(ctrl->handler, struct imx219, ctrl_handler);
+ 	struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+ 	int ret;
++	int rate_factor;
+ 
+ 	if (ctrl->id == V4L2_CID_VBLANK) {
+ 		int exposure_max, exposure_def;
+@@ -679,6 +731,10 @@ static int imx219_set_ctrl(struct v4l2_c
+ 	if (pm_runtime_get_if_in_use(&client->dev) == 0)
+ 		return 0;
+ 
++	rate_factor = imx219_get_rate_factor(imx219);
++	if (rate_factor < 0)
++		return rate_factor;
++
+ 	switch (ctrl->id) {
+ 	case V4L2_CID_ANALOGUE_GAIN:
+ 		ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
+@@ -687,7 +743,7 @@ static int imx219_set_ctrl(struct v4l2_c
+ 	case V4L2_CID_EXPOSURE:
+ 		ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
+ 				       IMX219_REG_VALUE_16BIT,
+-				       ctrl->val / imx219->mode->rate_factor);
++				       ctrl->val / rate_factor);
+ 		break;
+ 	case V4L2_CID_DIGITAL_GAIN:
+ 		ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
+@@ -708,7 +764,7 @@ static int imx219_set_ctrl(struct v4l2_c
+ 		ret = imx219_write_reg(imx219, IMX219_REG_VTS,
+ 				       IMX219_REG_VALUE_16BIT,
+ 				       (imx219->mode->height + ctrl->val) /
+-						imx219->mode->rate_factor);
++						rate_factor);
+ 		break;
+ 	case V4L2_CID_HBLANK:
+ 		ret = imx219_write_reg(imx219, IMX219_REG_HTS,
+@@ -890,7 +946,7 @@ static int imx219_set_pad_format(struct
+ 	struct imx219 *imx219 = to_imx219(sd);
+ 	const struct imx219_mode *mode;
+ 	struct v4l2_mbus_framefmt *framefmt;
+-	int exposure_max, exposure_def, hblank, pixel_rate;
++	int exposure_max, exposure_def, hblank, pixel_rate, rate_factor;
+ 	unsigned int i;
+ 
+ 	if (fmt->pad >= NUM_PADS)
+@@ -924,6 +980,9 @@ static int imx219_set_pad_format(struct
+ 
+ 			imx219->fmt = fmt->format;
+ 			imx219->mode = mode;
++			rate_factor = imx219_get_rate_factor(imx219);
++			if (rate_factor < 0)
++				return rate_factor;
+ 			/* Update limits and set FPS to default */
+ 			__v4l2_ctrl_modify_range(imx219->vblank,
+ 						 IMX219_VBLANK_MIN,
+@@ -957,8 +1016,7 @@ static int imx219_set_pad_format(struct
+ 			__v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+ 
+ 			/* Scale the pixel rate based on the mode specific factor */
+-			pixel_rate =
+-				IMX219_PIXEL_RATE * imx219->mode->rate_factor;
++			pixel_rate = IMX219_PIXEL_RATE * rate_factor;
+ 			__v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
+ 						 pixel_rate, 1, pixel_rate);
+ 		}
+@@ -1001,30 +1059,25 @@ static int imx219_set_framefmt(struct im
+ 
+ static int imx219_set_binning(struct imx219 *imx219)
+ {
+-	if (!imx219->mode->binning) {
++	enum binning_mode binning = BINNING_NONE;
++	int ret = imx219_resolve_binning(imx219, &binning);
++
++	if (ret < 0)
++		return ret;
++	switch (binning) {
++	case BINNING_NONE:
+ 		return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ 					IMX219_REG_VALUE_16BIT,
+ 					IMX219_BINNING_NONE);
+-	}
+-
+-	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:
++	case BINNING_DIGITAL_2x2:
+ 		return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ 					IMX219_REG_VALUE_16BIT,
+-					IMX219_BINNING_2X2_ANALOG);
+-
+-	case MEDIA_BUS_FMT_SRGGB10_1X10:
+-	case MEDIA_BUS_FMT_SGRBG10_1X10:
+-	case MEDIA_BUS_FMT_SGBRG10_1X10:
+-	case MEDIA_BUS_FMT_SBGGR10_1X10:
++					IMX219_BINNING_2X2);
++	case BINNING_ANALOG_2x2:
+ 		return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ 					IMX219_REG_VALUE_16BIT,
+-					IMX219_BINNING_2X2);
++					IMX219_BINNING_2X2_ANALOG);
+ 	}
+-
+ 	return -EINVAL;
+ }
+ 
+@@ -1342,7 +1395,7 @@ static int imx219_init_controls(struct i
+ 	struct v4l2_ctrl_handler *ctrl_hdlr;
+ 	unsigned int height = imx219->mode->height;
+ 	struct v4l2_fwnode_device_properties props;
+-	int exposure_max, exposure_def, hblank, pixel_rate;
++	int exposure_max, exposure_def, hblank, pixel_rate, rate_factor;
+ 	int i, ret;
+ 
+ 	ctrl_hdlr = &imx219->ctrl_handler;
+@@ -1353,8 +1406,12 @@ static int imx219_init_controls(struct i
+ 	mutex_init(&imx219->mutex);
+ 	ctrl_hdlr->lock = &imx219->mutex;
+ 
++	rate_factor = imx219_get_rate_factor(imx219);
++	if (rate_factor < 0)
++		return rate_factor;
++
+ 	/* By default, PIXEL_RATE is read only */
+-	pixel_rate = IMX219_PIXEL_RATE * imx219->mode->rate_factor;
++	pixel_rate = IMX219_PIXEL_RATE * rate_factor;
+ 	imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+ 					       V4L2_CID_PIXEL_RATE,
+ 					       pixel_rate, pixel_rate,
+@@ -1576,6 +1633,9 @@ static int imx219_probe(struct i2c_clien
+ 		goto error_power_off;
+ 	usleep_range(100, 110);
+ 
++	/* Initialize default format */
++	imx219_set_default_format(imx219);
++
+ 	ret = imx219_init_controls(imx219);
+ 	if (ret)
+ 		goto error_power_off;
+@@ -1590,9 +1650,6 @@ static int imx219_probe(struct i2c_clien
+ 	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, NUM_PADS, imx219->pad);
+ 	if (ret) {
+ 		dev_err(dev, "failed to init entity pads: %d\n", ret);

+ 28 - 0
target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch

@@ -0,0 +1,28 @@
+From 52039b6ffb6e78c2f77319b167dceab9aa51d13f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 13 Jun 2023 16:12:54 +0100
+Subject: [PATCH] serial: sc16is7xx: Read modem line state at startup
+
+This patch sets the driver modem line state to the actual line state
+at driver startup.
+
+See: https://github.com/raspberrypi/linux/issues/5501
+
+Signed-off-by: Earl Schmidt <[email protected]>
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/tty/serial/sc16is7xx.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -1221,6 +1221,9 @@ static int sc16is7xx_startup(struct uart
+ 	      SC16IS7XX_IER_MSI_BIT;
+ 	sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
+ 
++	/* Initialize the Modem Control signals to current status */
++	one->old_mctrl = sc16is7xx_get_hwmctrl(port);
++
+ 	/* Enable modem status polling */
+ 	spin_lock_irqsave(&port->lock, flags);
+ 	sc16is7xx_enable_ms(port);

+ 79 - 0
target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch

@@ -0,0 +1,79 @@
+From 6ef818eed60db70e9caf6bdf74cc1f9943994226 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Fri, 16 Jun 2023 16:24:19 +0100
+Subject: [PATCH] drivers: media: bcm2835_unicam: Improve frame sequence count
+ handling
+
+Ensure that the frame sequence counter is incremented only if a previous
+frame start interrupt has occurred, or a frame start + frame end has
+occurred simultaneously.
+
+This corresponds the sequence number with the actual number of frames
+produced by the sensor, not the number of frame buffers dequeued back
+to userland.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c   | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -522,6 +522,7 @@ struct unicam_device {
+ 	/* subdevice async Notifier */
+ 	struct v4l2_async_notifier notifier;
+ 	unsigned int sequence;
++	bool frame_started;
+ 
+ 	/* ptr to  sub device */
+ 	struct v4l2_subdev *sensor;
+@@ -914,6 +915,8 @@ static irqreturn_t unicam_isr(int irq, v
+ 	 * buffer forever.
+ 	 */
+ 	if (fe) {
++		bool inc_seq = unicam->frame_started;
++
+ 		/*
+ 		 * Ensure we have swapped buffers already as we can't
+ 		 * stop the peripheral. If no buffer is available, use a
+@@ -949,11 +952,23 @@ static irqreturn_t unicam_isr(int irq, v
+ 				unicam_process_buffer_complete(node, sequence);
+ 				node->cur_frm = node->next_frm;
+ 				node->next_frm = NULL;
++				inc_seq = true;
+ 			} else {
+ 				node->cur_frm = node->next_frm;
+ 			}
+ 		}
+-		unicam->sequence++;
++
++		/*
++		 * Increment the sequence number conditionally on either a FS
++		 * having already occurred, or in the FE + FS condition as
++		 * caught in the FE handler above. This ensures the sequence
++		 * number corresponds to the frames generated by the sensor, not
++		 * the frames dequeued to userland.
++		 */
++		if (inc_seq) {
++			unicam->sequence++;
++			unicam->frame_started = false;
++		}
+ 	}
+ 
+ 	if (ista & UNICAM_FSI) {
+@@ -996,6 +1011,7 @@ static irqreturn_t unicam_isr(int irq, v
+ 		}
+ 
+ 		unicam_queue_event_sof(unicam);
++		unicam->frame_started = true;
+ 	}
+ 
+ 	/*
+@@ -2600,6 +2616,7 @@ static int unicam_start_streaming(struct
+ 			vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ 	}
+ 
++	dev->frame_started = false;
+ 	unicam_start_rx(dev, buffer_addr);
+ 
+ 	ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);

+ 41 - 0
target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch

@@ -0,0 +1,41 @@
+From bd8e59b0456870997fb917bcd3b3b696e78d4ac2 Mon Sep 17 00:00:00 2001
+From: 6by9 <[email protected]>
+Date: Mon, 19 Jun 2023 16:02:36 +0100
+Subject: [PATCH] dtoverlays: Fix pitft[28|35] overlays for 6.1 driver change.
+ (#5508)
+
+The overlays configured both irq-gpio and an interrupts/
+interrupt-parent configuration for the stmpe MFD device.
+
+irq-gpio was reworked in 6.1 and has issues with the configuration
+as provided. Removing it and using the interrupts/interrupt-parent
+configuration works fine, so do that.
+
+See: https://forums.raspberrypi.com/viewtopic.php?t=351394
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts | 1 -
+ arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts | 1 -
+ 2 files changed, 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts
+@@ -68,7 +68,6 @@
+ 				reg = <1>;
+ 
+ 				spi-max-frequency = <500000>;
+-				irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+ 				interrupts = <24 2>; /* high-to-low edge triggered */
+ 				interrupt-parent = <&gpio>;
+ 				interrupt-controller;
+--- a/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts
+@@ -68,7 +68,6 @@
+ 				reg = <1>;
+ 
+ 				spi-max-frequency = <500000>;
+-				irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+ 				interrupts = <24 2>; /* high-to-low edge triggered */
+ 				interrupt-parent = <&gpio>;
+ 				interrupt-controller;

+ 23 - 0
target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch

@@ -0,0 +1,23 @@
+From 713a7ef9d73fca0f7fed122cb854d930b7a6ba5a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Wed, 21 Jun 2023 08:45:02 +0100
+Subject: [PATCH] driver: media: i2c: imx477: Re-enable temperature sensor
+
+The temperature sensor enable register write got lost at some point.
+Re-enable it.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/i2c/imx477.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -167,6 +167,7 @@ struct imx477_mode {
+ static const struct imx477_reg mode_common_regs[] = {
+ 	{0x0136, 0x18},
+ 	{0x0137, 0x00},
++	{0x0138, 0x01},
+ 	{0xe000, 0x00},
+ 	{0xe07a, 0x01},
+ 	{0x0808, 0x02},

+ 25 - 0
target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch

@@ -0,0 +1,25 @@
+From d4c3133378b377ee519ea50247339cd61221fc47 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 21 Jun 2023 09:20:36 +0100
+Subject: [PATCH] overlays: allo-katana-dac-audio: Reduce I2C clock
+
+Higher speeds have been shown to cause data corruption on a Pi 4,
+possibly due to clock-stretching.
+
+See: https://github.com/raspberrypi/linux/issues/5511
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
+@@ -30,6 +30,7 @@
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
+ 			status = "okay";
++			clock-frequency = <50000>;
+ 
+ 			allo-katana-codec@30 {
+ 				#sound-dai-cells = <0>;

+ 308 - 0
target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch

@@ -0,0 +1,308 @@
+From 76c457e7e2920342637b1955fbaadf2aae282f05 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 23 Jun 2023 09:48:59 +0100
+Subject: [PATCH] overlays: jedec-spi-nor: Add speed parameter
+
+Add a speed parameter to the jedec-spi-nor overlay to allow much
+faster accesses, taking the opportunity to simplify the internals.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README             |   8 +-
+ .../dts/overlays/jedec-spi-nor-overlay.dts    | 245 +++---------------
+ 2 files changed, 41 insertions(+), 212 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2557,9 +2557,11 @@ Name:   jedec-spi-nor
+ Info:   Adds support for JEDEC-compliant SPI NOR flash devices.  (Note: The
+         "jedec,spi-nor" kernel driver was formerly known as "m25p80".)
+ Load:   dtoverlay=jedec-spi-nor,<param>=<val>
+-Params: flash-spi<n>-<m>        Enables flash device on SPI<n>, CS#<m>.
+-        flash-fastr-spi<n>-<m>  Enables flash device with fast read capability
+-                                on SPI<n>, CS#<m>.
++Params: spi<n>-<m>              Enable flash device on SPI<n>, CS#<m>
++        fastr                   Add fast read capability to the flash device
++        speed                   Maximum SPI frequency (Hz)
++        flash-spi<n>-<m>        Same as spi<n>-<m> (deprecated)
++        flash-fastr-spi<n>-<m>  Same as spi<n>->m>,fastr (deprecated)
+ 
+ 
+ Name:   justboom-both
+--- a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
+@@ -3,6 +3,7 @@
+ // dtparams:
+ //     flash-spi<n>-<m>        - Enables flash device on SPI<n>, CS#<m>.
+ //     flash-fastr-spi<n>-<m>  - Enables flash device with fast read capability on SPI<n>, CS#<m>.
++//     speed                   - Set the SPI clock speed in Hz
+ //
+ // If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+ //
+@@ -79,50 +80,23 @@
+ 		};
+ 	};
+ 
+-	// enable flash on spi0.0
++	// Enable fast read for device
++	// Use default active low interrupt signalling.
+ 	fragment@8 {
+-		target = <&spi0>;
++		target = <&spi_nor>;
+ 		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_00: spi_nor@0 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <0>;
+-				spi-max-frequency = <500000>;
+-			};
++			m25p,fast-read;
+ 		};
+ 	};
+ 
+-	// enable flash on spi0.1
+-	fragment@9 {
++	payload: fragment@100 {
+ 		target = <&spi0>;
+-		__dormant__ {
++		__overlay__ {
+ 			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_01: spi_nor@1 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <1>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
++			#address-cells = <1>;
++			#size-cells = <0>;
+ 
+-	// enable flash on spi1.0
+-	fragment@10 {
+-		target = <&spi1>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_10: spi_nor@0 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
++			spi_nor: spi_nor@0 {
+ 				compatible = "jedec,spi-nor";
+ 				reg = <0>;
+ 				spi-max-frequency = <500000>;
+@@ -130,180 +104,33 @@
+ 		};
+ 	};
+ 
+-	// enable flash on spi1.1
+-	fragment@11 {
+-		target = <&spi1>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_11: spi_nor@1 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <1>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
+-
+-	// enable flash on spi1.2
+-	fragment@12 {
+-		target = <&spi1>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_12: spi_nor@2 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <2>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
+-
+-	// enable flash on spi2.0
+-	fragment@13 {
+-		target = <&spi2>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_20: spi_nor@0 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <0>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
+-
+-	// enable flash on spi2.1
+-	fragment@14 {
+-		target = <&spi2>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_21: spi_nor@1 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <1>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
+-
+-	// enable flash on spi2.2
+-	fragment@15 {
+-		target = <&spi2>;
+-		__dormant__ {
+-			status = "okay";
+-                        #address-cells = <1>;
+-                        #size-cells = <0>;
+-			spi_nor_22: spi_nor@2 {
+-				#address-cells = <1>;
+-				#size-cells = <1>;
+-				compatible = "jedec,spi-nor";
+-				reg = <2>;
+-				spi-max-frequency = <500000>;
+-			};
+-		};
+-	};
+-
+-	// Enable fast read for device on spi0.0.
+-	// Use default active low interrupt signalling.
+-	fragment@16 {
+-		target = <&spi_nor_00>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi0.1.
+-	// Use default active low interrupt signalling.
+-	fragment@17 {
+-		target = <&spi_nor_01>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi1.0.
+-	// Use default active low interrupt signalling.
+-	fragment@18 {
+-		target = <&spi_nor_10>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi1.1.
+-	// Use default active low interrupt signalling.
+-	fragment@19 {
+-		target = <&spi_nor_11>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi1.2.
+-	// Use default active low interrupt signalling.
+-	fragment@20 {
+-		target = <&spi_nor_12>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi2.0.
+-	// Use default active low interrupt signalling.
+-	fragment@21 {
+-		target = <&spi_nor_20>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi2.1.
+-	// Use default active low interrupt signalling.
+-	fragment@22 {
+-		target = <&spi_nor_21>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+-	// Enable fast read for device on spi2.2.
+-	// Use default active low interrupt signalling.
+-	fragment@23 {
+-		target = <&spi_nor_22>;
+-		__dormant__ {
+-			m25p,fast-read;
+-		};
+-	};
+-
+ 	__overrides__ {
+-		flash-spi0-0       = <0>,"+0+8";
+-		flash-spi0-1       = <0>,"+1+9";
+-		flash-spi1-0       = <0>,"+2+10";
+-		flash-spi1-1       = <0>,"+3+11";
+-		flash-spi1-2       = <0>,"+4+12";
+-		flash-spi2-0       = <0>,"+5+13";
+-		flash-spi2-1       = <0>,"+6+14";
+-		flash-spi2-2       = <0>,"+7+15";
+-		flash-fastr-spi0-0 = <0>,"+0+8+16";
+-		flash-fastr-spi0-1 = <0>,"+1+9+17";
+-		flash-fastr-spi1-0 = <0>,"+2+10+18";
+-		flash-fastr-spi1-1 = <0>,"+3+11+19";
+-		flash-fastr-spi1-2 = <0>,"+4+12+20";
+-		flash-fastr-spi2-0 = <0>,"+5+13+21";
+-		flash-fastr-spi2-1 = <0>,"+6+14+22";
+-		flash-fastr-spi2-2 = <0>,"+7+15+23";
++		spi0-0             = <0>,"+0", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++		spi0-1             = <0>,"+1", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++		spi1-0             = <0>,"+2", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++		spi1-1             = <0>,"+3", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++		spi1-2             = <0>,"+4", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++		spi2-0             = <0>,"+5", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++		spi2-1             = <0>,"+6", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++		spi2-2             = <0>,"+7", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++		flash-spi0-0       = <0>,"+0", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++		flash-spi0-1       = <0>,"+1", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++		flash-spi1-0       = <0>,"+2", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++		flash-spi1-1       = <0>,"+3", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++		flash-spi1-2       = <0>,"+4", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++		flash-spi2-0       = <0>,"+5", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++		flash-spi2-1       = <0>,"+6", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++		flash-spi2-2       = <0>,"+7", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++		flash-fastr-spi0-0 = <0>,"+0+8", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++		flash-fastr-spi0-1 = <0>,"+1+8", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++		flash-fastr-spi1-0 = <0>,"+2+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++		flash-fastr-spi1-1 = <0>,"+3+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++		flash-fastr-spi1-2 = <0>,"+4+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++		flash-fastr-spi2-0 = <0>,"+5+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++		flash-fastr-spi2-1 = <0>,"+6+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++		flash-fastr-spi2-2 = <0>,"+7+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++		fastr              = <0>,"+8";
++		speed              = <&spi_nor>, "spi-max-frequency:0";
+ 	};
+ };
+ 

+ 137 - 0
target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch

@@ -0,0 +1,137 @@
+From e866f9fc7c6dd6af1e74ce6fa50db9ba21acae5e Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <[email protected]>
+Date: Sat, 24 Jun 2023 18:52:16 +0200
+Subject: [PATCH] ALSA: pcm: fix ELD constraints for (E)AC3, DTS(-HD) and MLP
+ formats
+
+commit 04b49b90caeed0b5544ff616d654900d27d403b6 upstream.
+
+The SADs of compressed formats contain the channel and sample rate
+info of the audio data inside the compressed stream, but when
+building constraints we must use the rates and channels used to
+transport the compressed streams.
+
+eg 48kHz 6ch EAC3 needs to be transmitted as a 2ch 192kHz stream.
+
+This patch fixes the constraints for the common AC3 and DTS formats,
+the constraints for the less common MPEG, DSD etc formats are copied
+directly from the info in the SADs as before as I don't have the specs
+and equipment to test those.
+
+Signed-off-by: Matthias Reichl <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Takashi Iwai <[email protected]>
+---
+ sound/core/pcm_drm_eld.c | 73 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 70 insertions(+), 3 deletions(-)
+
+--- a/sound/core/pcm_drm_eld.c
++++ b/sound/core/pcm_drm_eld.c
+@@ -2,11 +2,25 @@
+ /*
+  *  PCM DRM helpers
+  */
++#include <linux/bitfield.h>
+ #include <linux/export.h>
++#include <linux/hdmi.h>
+ #include <drm/drm_edid.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_drm_eld.h>
+ 
++#define SAD0_CHANNELS_MASK	GENMASK(2, 0) /* max number of channels - 1 */
++#define SAD0_FORMAT_MASK	GENMASK(6, 3) /* audio format */
++
++#define SAD1_RATE_MASK		GENMASK(6, 0) /* bitfield of supported rates */
++#define SAD1_RATE_32000_MASK	BIT(0)
++#define SAD1_RATE_44100_MASK	BIT(1)
++#define SAD1_RATE_48000_MASK	BIT(2)
++#define SAD1_RATE_88200_MASK	BIT(3)
++#define SAD1_RATE_96000_MASK	BIT(4)
++#define SAD1_RATE_176400_MASK	BIT(5)
++#define SAD1_RATE_192000_MASK	BIT(6)
++
+ static const unsigned int eld_rates[] = {
+ 	32000,
+ 	44100,
+@@ -17,9 +31,62 @@ static const unsigned int eld_rates[] =
+ 	192000,
+ };
+ 
++static unsigned int map_rate_families(const u8 *sad,
++				      unsigned int mask_32000,
++				      unsigned int mask_44100,
++				      unsigned int mask_48000)
++{
++	unsigned int rate_mask = 0;
++
++	if (sad[1] & SAD1_RATE_32000_MASK)
++		rate_mask |= mask_32000;
++	if (sad[1] & (SAD1_RATE_44100_MASK | SAD1_RATE_88200_MASK | SAD1_RATE_176400_MASK))
++		rate_mask |= mask_44100;
++	if (sad[1] & (SAD1_RATE_48000_MASK | SAD1_RATE_96000_MASK | SAD1_RATE_192000_MASK))
++		rate_mask |= mask_48000;
++	return rate_mask;
++}
++
++static unsigned int sad_rate_mask(const u8 *sad)
++{
++	switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
++	case HDMI_AUDIO_CODING_TYPE_PCM:
++		return sad[1] & SAD1_RATE_MASK;
++	case HDMI_AUDIO_CODING_TYPE_AC3:
++	case HDMI_AUDIO_CODING_TYPE_DTS:
++		return map_rate_families(sad,
++					 SAD1_RATE_32000_MASK,
++					 SAD1_RATE_44100_MASK,
++					 SAD1_RATE_48000_MASK);
++	case HDMI_AUDIO_CODING_TYPE_EAC3:
++	case HDMI_AUDIO_CODING_TYPE_DTS_HD:
++	case HDMI_AUDIO_CODING_TYPE_MLP:
++		return map_rate_families(sad,
++					 0,
++					 SAD1_RATE_176400_MASK,
++					 SAD1_RATE_192000_MASK);
++	default:
++		/* TODO adjust for other compressed formats as well */
++		return sad[1] & SAD1_RATE_MASK;
++	}
++}
++
+ static unsigned int sad_max_channels(const u8 *sad)
+ {
+-	return 1 + (sad[0] & 7);
++	switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
++	case HDMI_AUDIO_CODING_TYPE_PCM:
++		return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
++	case HDMI_AUDIO_CODING_TYPE_AC3:
++	case HDMI_AUDIO_CODING_TYPE_DTS:
++	case HDMI_AUDIO_CODING_TYPE_EAC3:
++		return 2;
++	case HDMI_AUDIO_CODING_TYPE_DTS_HD:
++	case HDMI_AUDIO_CODING_TYPE_MLP:
++		return 8;
++	default:
++		/* TODO adjust for other compressed formats as well */
++		return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
++	}
+ }
+ 
+ static int eld_limit_rates(struct snd_pcm_hw_params *params,
+@@ -42,7 +109,7 @@ static int eld_limit_rates(struct snd_pc
+ 			 * requested number of channels.
+ 			 */
+ 			if (c->min <= max_channels)
+-				rate_mask |= sad[1];
++				rate_mask |= sad_rate_mask(sad);
+ 		}
+ 	}
+ 
+@@ -70,7 +137,7 @@ static int eld_limit_channels(struct snd
+ 				rate_mask |= BIT(i);
+ 
+ 		for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
+-			if (rate_mask & sad[1])
++			if (rate_mask & sad_rate_mask(sad))
+ 				t.max = max(t.max, sad_max_channels(sad));
+ 	}
+ 

+ 86 - 0
target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch

@@ -0,0 +1,86 @@
+From 3f388718331b5ce2acd34730448db001759868aa Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <[email protected]>
+Date: Sat, 24 Jun 2023 18:52:32 +0200
+Subject: [PATCH] ASoC: hdmi-codec: fix channel info for compressed formats
+
+commit 4e0871333661d2ec0ed3dc00a945c2160eccae77 upstream.
+
+According to CTA 861 the channel/speaker allocation info in the
+audio infoframe only applies to uncompressed (PCM) audio streams.
+
+The channel count info should indicate the number of channels
+in the transmitted audio, which usually won't match the number of
+channels used to transmit the compressed bitstream.
+
+Some devices (eg some Sony TVs) will refuse to decode compressed
+audio if these values are not set correctly.
+
+To fix this we can simply set the channel count to 0 (which means
+"refer to stream header") and set the channel/speaker allocation to 0
+as well (which would mean stereo FL/FR for PCM, a safe value all sinks
+will support) when transmitting compressed audio.
+
+Signed-off-by: Matthias Reichl <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Takashi Iwai <[email protected]>
+---
+ sound/soc/codecs/hdmi-codec.c | 36 +++++++++++++++++++++++------------
+ 1 file changed, 24 insertions(+), 12 deletions(-)
+
+--- a/sound/soc/codecs/hdmi-codec.c
++++ b/sound/soc/codecs/hdmi-codec.c
+@@ -484,31 +484,43 @@ static int hdmi_codec_fill_codec_params(
+ 					struct hdmi_codec_params *hp)
+ {
+ 	struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+-	int idx;
++	int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
++	u8 ca_id = 0;
++	bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO);
++
++	if (pcm_audio) {
++		/* Select a channel allocation that matches with ELD and pcm channels */
++		idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
++
++		if (idx < 0) {
++			dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
++				idx);
++			hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
++			return idx;
++		}
+ 
+-	/* Select a channel allocation that matches with ELD and pcm channels */
+-	idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+-	if (idx < 0) {
+-		dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+-			idx);
+-		hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+-		return idx;
++		ca_id = hdmi_codec_channel_alloc[idx].ca_id;
+ 	}
+ 
+ 	memset(hp, 0, sizeof(*hp));
+ 
+ 	hdmi_audio_infoframe_init(&hp->cea);
+-	hp->cea.channels = channels;
++
++	if (pcm_audio)
++		hp->cea.channels = channels;
++	else
++		hp->cea.channels = 0;
++
+ 	hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ 	hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ 	hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+-	hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
++	hp->cea.channel_allocation = ca_id;
+ 
+ 	hp->sample_width = sample_width;
+ 	hp->sample_rate = sample_rate;
+ 	hp->channels = channels;
+ 
+-	hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
++	hcp->chmap_idx = idx;
+ 
+ 	return 0;
+ }

+ 44 - 0
target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch

@@ -0,0 +1,44 @@
+From 9c5a7f04cab6b020389d7c5af155b1ee7f46537d Mon Sep 17 00:00:00 2001
+From: Lee Jackson <[email protected]>
+Date: Thu, 4 May 2023 11:14:04 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Modify the line length of 1280x720
+ resolution
+
+Arducam 64MP has specific requirements for the line length, and if these
+conditions are not met, the camera will not function properly. Under the
+previous configuration, once a stream off operation is performed, the
+camera will not output any data, even if a stream on operation is
+performed. This prevents us from switching from 1280x720 to another
+resolution.
+
+Signed-off-by: Lee Jackson <[email protected]>
+---
+ drivers/media/i2c/arducam_64mp.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -1063,10 +1063,10 @@ static const struct arducam_64mp_reg mod
+ 
+ /* 720p 120fps mode */
+ static const struct arducam_64mp_reg mode_1280x720_regs[] = {
+-	{0x0342, 0x1d},
+-	{0x0343, 0xc4},
+-	{0x0340, 0x03},
+-	{0x0341, 0xd8},
++	{0x0342, 0x1b},
++	{0x0343, 0x08},
++	{0x0340, 0x04},
++	{0x0341, 0x3b},
+ 	{0x0344, 0x08},
+ 	{0x0345, 0x10},
+ 	{0x0346, 0x07},
+@@ -1209,7 +1209,7 @@ static const struct arducam_64mp_mode su
+ 	}, {
+ 		.width = 1280,
+ 		.height = 720,
+-		.line_length_pix = 0x1dc4,
++		.line_length_pix = 0x1b08,
+ 		.crop = {
+ 			.left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT + 2064,
+ 			.top = ARDUCAM_64MP_PIXEL_ARRAY_TOP + 2032,

+ 105 - 0
target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch

@@ -0,0 +1,105 @@
+From 7b3d0124c5cf462d5be0b0d4e558002b74750911 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <[email protected]>
+Date: Fri, 5 May 2023 14:36:15 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Add 8000x6000 resolution
+
+Added 8000x6000 10-bit (cropped) @ 3fps mode for Arducam 64MP
+
+Signed-off-by: Lee Jackson <[email protected]>
+---
+ drivers/media/i2c/arducam_64mp.c | 77 ++++++++++++++++++++++++++++++++
+ 1 file changed, 77 insertions(+)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -849,6 +849,65 @@ static const struct arducam_64mp_reg mod
+ 	{0x020f, 0x00},
+ };
+ 
++/* 48 mpix 3.0fps */
++static const struct arducam_64mp_reg mode_8000x6000_regs[] = {
++	{0x0342, 0xb6},
++	{0x0343, 0xb2},
++	{0x0340, 0x19},
++	{0x0341, 0x0e},
++	{0x0344, 0x02},
++	{0x0345, 0x70},
++	{0x0346, 0x01},
++	{0x0347, 0xd8},
++	{0x0348, 0x21},
++	{0x0349, 0xaf},
++	{0x034a, 0x19},
++	{0x034b, 0x47},
++	{0x0900, 0x00},
++	{0x0901, 0x11},
++	{0x0902, 0x0a},
++	{0x30d8, 0x00},
++	{0x3200, 0x01},
++	{0x3201, 0x01},
++	{0x0408, 0x00},
++	{0x0409, 0x00},
++	{0x040a, 0x00},
++	{0x040b, 0x00},
++	{0x040c, 0x1f},
++	{0x040d, 0x40},
++	{0x040e, 0x17},
++	{0x040f, 0x70},
++	{0x034c, 0x1f},
++	{0x034d, 0x40},
++	{0x034e, 0x17},
++	{0x034f, 0x70},
++	{0x30d9, 0x01},
++	{0x32d5, 0x01},
++	{0x32d6, 0x00},
++	{0x401e, 0x00},
++	{0x40b8, 0x04},
++	{0x40b9, 0x20},
++	{0x40bc, 0x02},
++	{0x40bd, 0x58},
++	{0x40be, 0x02},
++	{0x40bf, 0x58},
++	{0x41a4, 0x00},
++	{0x5a09, 0x01},
++	{0x5a17, 0x01},
++	{0x5a25, 0x01},
++	{0x5a33, 0x01},
++	{0x98d7, 0x14},
++	{0x98d8, 0x14},
++	{0x98d9, 0x00},
++	{0x99c4, 0x00},
++	{0x0202, 0x03},
++	{0x0203, 0xe8},
++	{0x0204, 0x00},
++	{0x0205, 0x00},
++	{0x020e, 0x01},
++	{0x020f, 0x00},
++};
++
+ /* 16 mpix 10fps */
+ static const struct arducam_64mp_reg mode_4624x3472_regs[] = {
+ 	{0x0342, 0x63},
+@@ -1135,6 +1194,24 @@ static const struct arducam_64mp_mode su
+ 			.regs = mode_9152x6944_regs,
+ 		}
+ 	}, {
++		.width = 8000,
++		.height = 6000,
++		.line_length_pix = 0xb6b2,
++		.crop = {
++			.left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT + 624,
++			.top = ARDUCAM_64MP_PIXEL_ARRAY_TOP + 472,
++			.width = 9248,
++			.height = 6944,
++		},
++		.timeperframe_default = {
++			.numerator = 100,
++			.denominator = 300
++		},
++		.reg_list = {
++			.num_of_regs = ARRAY_SIZE(mode_8000x6000_regs),
++			.regs = mode_8000x6000_regs,
++		}
++	}, {
+ 		.width = 4624,
+ 		.height = 3472,
+ 		.line_length_pix = 0x6397,

+ 163 - 0
target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch

@@ -0,0 +1,163 @@
+From b9d2d1862aa5b798cecb87a95d970ad34a4aebc0 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <[email protected]>
+Date: Tue, 30 May 2023 15:50:05 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Add PDAF support
+
+Enable PDAF output for all modes, and also need to modify Embedded Line
+Width to 11560 * 3 (two lines of Embedded Data + one line of PDAF).
+
+Signed-off-by: Lee Jackson <[email protected]>
+---
+ drivers/media/i2c/arducam_64mp.c | 64 ++++++++++++++++++++++++++++++--
+ 1 file changed, 61 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -95,7 +95,7 @@
+ #define ARDUCAM_64MP_TEST_PATTERN_GB_DEFAULT	0
+ 
+ /* Embedded metadata stream structure */
+-#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH 16384
++#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (11560 * 3)
+ #define ARDUCAM_64MP_NUM_EMBEDDED_LINES 1
+ 
+ enum pad_types {
+@@ -144,6 +144,7 @@ struct arducam_64mp_mode {
+ };
+ 
+ static const struct arducam_64mp_reg mode_common_regs[] = {
++	{0x0100, 0x00},
+ 	{0x0136, 0x18},
+ 	{0x0137, 0x00},
+ 	{0x33F0, 0x01},
+@@ -788,6 +789,7 @@ static const struct arducam_64mp_reg mod
+ 	{0x3092, 0x01},
+ 	{0x3093, 0x00},
+ 	{0x0350, 0x00},
++	{0x3419, 0x00},
+ };
+ 
+ /* 64 mpix 2.7fps */
+@@ -847,6 +849,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x0205, 0x00},
+ 	{0x020e, 0x01},
+ 	{0x020f, 0x00},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x02},
++	{0x341f, 0x3c},
++	{0x3420, 0x02},
++	{0x3421, 0x42},
+ };
+ 
+ /* 48 mpix 3.0fps */
+@@ -906,6 +916,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x0205, 0x00},
+ 	{0x020e, 0x01},
+ 	{0x020f, 0x00},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x01},
++	{0x341f, 0xf4},
++	{0x3420, 0x01},
++	{0x3421, 0xf4},
+ };
+ 
+ /* 16 mpix 10fps */
+@@ -959,6 +977,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x98d8, 0x8c},
+ 	{0x98d9, 0x0a},
+ 	{0x99c4, 0x16},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x01},
++	{0x341f, 0x21},
++	{0x3420, 0x01},
++	{0x3421, 0x21},
+ };
+ 
+ /* 4k 20fps mode */
+@@ -1012,6 +1038,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x98d8, 0x8c},
+ 	{0x98d9, 0x0a},
+ 	{0x99c4, 0x16},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x00},
++	{0x341f, 0xf0},
++	{0x3420, 0x00},
++	{0x3421, 0xb4},
+ };
+ 
+ /* 4x4 binned 30fps mode */
+@@ -1031,7 +1065,7 @@ static const struct arducam_64mp_reg mod
+ 	{0x0900, 0x01},
+ 	{0x0901, 0x44},
+ 	{0x0902, 0x08},
+-	{0x30d8, 0x00},
++	{0x30d8, 0x04},
+ 	{0x3200, 0x43},
+ 	{0x3201, 0x43},
+ 	{0x0408, 0x00},
+@@ -1046,7 +1080,7 @@ static const struct arducam_64mp_reg mod
+ 	{0x034d, 0x08},
+ 	{0x034e, 0x06},
+ 	{0x034f, 0xc8},
+-	{0x30d9, 0x01},
++	{0x30d9, 0x00},
+ 	{0x32d5, 0x00},
+ 	{0x32d6, 0x00},
+ 	{0x401e, 0x00},
+@@ -1065,6 +1099,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x98d8, 0x8c},
+ 	{0x98d9, 0x0a},
+ 	{0x99c4, 0x16},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x00},
++	{0x341f, 0x90},
++	{0x3420, 0x00},
++	{0x3421, 0x90},
+ };
+ 
+ /* 1080p 60fps mode */
+@@ -1118,6 +1160,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x98d8, 0x8c},
+ 	{0x98d9, 0x0a},
+ 	{0x99c4, 0x16},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x00},
++	{0x341f, 0x78},
++	{0x3420, 0x00},
++	{0x3421, 0x5a},
+ };
+ 
+ /* 720p 120fps mode */
+@@ -1171,6 +1221,14 @@ static const struct arducam_64mp_reg mod
+ 	{0x98d8, 0x8c},
+ 	{0x98d9, 0x0a},
+ 	{0x99c4, 0x16},
++	{0x341a, 0x00},
++	{0x341b, 0x00},
++	{0x341c, 0x00},
++	{0x341d, 0x00},
++	{0x341e, 0x00},
++	{0x341f, 0x50},
++	{0x3420, 0x00},
++	{0x3421, 0x3c},
+ };
+ 
+ /* Mode configs */

+ 24 - 0
target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch

@@ -0,0 +1,24 @@
+From 6f4106f7a7fdcbc03290008713915b4122988c90 Mon Sep 17 00:00:00 2001
+From: James Hughes <[email protected]>
+Date: Wed, 5 Jul 2023 15:43:30 +0100
+Subject: [PATCH] overlays: audremap: Document CM4 40&41 restriction
+
+Update audremap information to state pins 40,41 are not available on the CM4.
+
+Signed-off-by: James Hughes ([email protected])
+---
+ arch/arm/boot/dts/overlays/README | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -703,7 +703,8 @@ Params: swap_lr                 Reverse
+                                 nothing on BCM2711 (default off)
+         pins_12_13              Select GPIOs 12 & 13 (default)
+         pins_18_19              Select GPIOs 18 & 19
+-        pins_40_41              Select GPIOs 40 & 41
++        pins_40_41              Select GPIOs 40 & 41 (not available on CM4, used
++                                for other purposes)
+         pins_40_45              Select GPIOs 40 & 45 (don't use on BCM2711 - the
+                                 pins are on different controllers)
+ 

+ 120 - 0
target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch

@@ -0,0 +1,120 @@
+From 1d15e6a34222cc8d8eb1050e7a3e276b0348be41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 3 Jul 2023 11:04:56 +0100
+Subject: [PATCH] fixup! Allow mac address to be set in smsc95xx
+
+usbnet: smsc95xx: Fix indentation of smsc95xx_is_macaddr_param()
+
+smsc95xx_is_macaddr_param() is incorrectly indented, it uses 7 spaces
+instead of tabs.  Fix it.
+
+Fixes: aac7b105788e ("Allow mac address to be set in smsc95xx")
+Signed-off-by: Philipp Rosenberger <[email protected]>
+[lukas: fix netif_dbg() indentation as well, wordsmith commit message]
+Signed-off-by: Lukas Wunner <[email protected]>
+
+usbnet: smsc95xx: Simplify MAC address parsing
+
+Parsing the MAC address provided on the kernel command line can be
+simplified quite a bit by taking advantage of the kernel's built-in
+mac_pton() helper.
+
+Likewise emitting the MAC address can be simplified with the %pM
+format string conversion.
+
+Signed-off-by: Lukas Wunner <[email protected]>
+
+usbnet: smsc95xx: Fix style issues in smsc95xx_is_macaddr_param()
+
+It is bad practice to have a function named ..._is_...() which has side
+effects.  So drop the 'is' from the name.
+
+Per kernel convention return 0 on success and a negative errno on
+failure.
+
+Validate the MAC address retrieved from the command line.
+
+Signed-off-by: Philipp Rosenberger <[email protected]>
+[lukas: leave 2nd function parameter unchanged, wordsmith commit message]
+Signed-off-by: Lukas Wunner <[email protected]>
+---
+ drivers/net/usb/smsc95xx.c | 61 +++++++++++---------------------------
+ 1 file changed, 17 insertions(+), 44 deletions(-)
+
+--- a/drivers/net/usb/smsc95xx.c
++++ b/drivers/net/usb/smsc95xx.c
+@@ -814,49 +814,18 @@ static int smsc95xx_ioctl(struct net_dev
+ }
+ 
+ /* Check the macaddr module parameter for a MAC address */
+-static int smsc95xx_is_macaddr_param(struct usbnet *dev, struct net_device *nd)
++static int smsc95xx_macaddr_param(struct usbnet *dev, struct net_device *nd)
+ {
+-       int i, j, got_num, num;
+-       u8 mtbl[ETH_ALEN];
++	u8 mtbl[ETH_ALEN];
+ 
+-       if (macaddr[0] == ':')
+-               return 0;
+-
+-       i = 0;
+-       j = 0;
+-       num = 0;
+-       got_num = 0;
+-       while (j < ETH_ALEN) {
+-               if (macaddr[i] && macaddr[i] != ':') {
+-                       got_num++;
+-                       if ('0' <= macaddr[i] && macaddr[i] <= '9')
+-                               num = num * 16 + macaddr[i] - '0';
+-                       else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
+-                               num = num * 16 + 10 + macaddr[i] - 'A';
+-                       else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
+-                               num = num * 16 + 10 + macaddr[i] - 'a';
+-                       else
+-                               break;
+-                       i++;
+-               } else if (got_num == 2) {
+-                       mtbl[j++] = (u8) num;
+-                       num = 0;
+-                       got_num = 0;
+-                       i++;
+-               } else {
+-                       break;
+-               }
+-       }
+-
+-       if (j == ETH_ALEN) {
+-               netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
+-               "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
+-                                               mtbl[3], mtbl[4], mtbl[5]);
+-	       dev_addr_mod(nd, 0, mtbl, ETH_ALEN);
+-               return 1;
+-       } else {
+-               return 0;
+-       }
++	if (mac_pton(macaddr, mtbl)) {
++		netif_dbg(dev, ifup, dev->net,
++			  "Overriding MAC address with: %pM\n", mtbl);
++		dev_addr_mod(nd, 0, mtbl, ETH_ALEN);
++		return 0;
++	} else {
++		return -EINVAL;
++	}
+ }
+ 
+ static void smsc95xx_init_mac_address(struct usbnet *dev)
+@@ -883,8 +852,12 @@ static void smsc95xx_init_mac_address(st
+ 	}
+ 
+ 	/* Check module parameters */
+-	if (smsc95xx_is_macaddr_param(dev, dev->net))
+-		return;
++	if (smsc95xx_macaddr_param(dev, dev->net) == 0) {
++		if (is_valid_ether_addr(dev->net->dev_addr)) {
++			netif_dbg(dev, ifup, dev->net, "MAC address read from module parameter\n");
++			return;
++		}
++	}
+ 
+ 	/* no useful static MAC address found. generate a random one */
+ 	eth_hw_addr_random(dev->net);

+ 1455 - 0
target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch

@@ -0,0 +1,1455 @@
+From a2d2745c311baa588fb0fffbe38076294f06b7c0 Mon Sep 17 00:00:00 2001
+From: Nicolai Buchwitz <[email protected]>
+Date: Wed, 12 Jul 2023 11:30:42 +0200
+Subject: [PATCH] cfg80211: ship debian certificates as hex files
+
+Loading the regulatory database from the debian files fails with
+
+"loaded regulatory.db is malformed or signature is missing/invalid"
+
+due to missing certificates.  Add these debian-specific certificates
+from upstream to fix this error. See #5536 for details.
+
+The certificates have been imported as following:
+
+patch -p1 <<<$(
+curl https://salsa.debian.org/kernel-team/linux/-/raw/\
+master/debian/patches/debian/\
+wireless-add-debian-wireless-regdb-certificates.patch
+)
+
+Signed-off-by: Nicolai Buchwitz <[email protected]>
+---
+ net/wireless/certs/debian.hex | 1426 +++++++++++++++++++++++++++++++++
+ 1 file changed, 1426 insertions(+)
+ create mode 100644 net/wireless/certs/debian.hex
+
+--- /dev/null
++++ b/net/wireless/certs/debian.hex
+@@ -0,0 +1,1426 @@
++0x30,
++0x82,
++0x02,
++0xbd,
++0x30,
++0x82,
++0x01,
++0xa5,
++0x02,
++0x14,
++0x57,
++0x7e,
++0x02,
++0x1c,
++0xb9,
++0x80,
++0xe0,
++0xe8,
++0x20,
++0x82,
++0x1b,
++0xa7,
++0xb5,
++0x4b,
++0x49,
++0x61,
++0xb8,
++0xb4,
++0xfa,
++0xdf,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x30,
++0x1a,
++0x31,
++0x18,
++0x30,
++0x16,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x0f,
++0x62,
++0x65,
++0x6e,
++0x68,
++0x40,
++0x64,
++0x65,
++0x62,
++0x69,
++0x61,
++0x6e,
++0x2e,
++0x6f,
++0x72,
++0x67,
++0x30,
++0x20,
++0x17,
++0x0d,
++0x32,
++0x30,
++0x30,
++0x31,
++0x33,
++0x30,
++0x31,
++0x33,
++0x32,
++0x36,
++0x31,
++0x33,
++0x5a,
++0x18,
++0x0f,
++0x32,
++0x31,
++0x32,
++0x30,
++0x30,
++0x31,
++0x30,
++0x36,
++0x31,
++0x33,
++0x32,
++0x36,
++0x31,
++0x33,
++0x5a,
++0x30,
++0x1a,
++0x31,
++0x18,
++0x30,
++0x16,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x0f,
++0x62,
++0x65,
++0x6e,
++0x68,
++0x40,
++0x64,
++0x65,
++0x62,
++0x69,
++0x61,
++0x6e,
++0x2e,
++0x6f,
++0x72,
++0x67,
++0x30,
++0x82,
++0x01,
++0x22,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x01,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x0f,
++0x00,
++0x30,
++0x82,
++0x01,
++0x0a,
++0x02,
++0x82,
++0x01,
++0x01,
++0x00,
++0x9d,
++0xe1,
++0x77,
++0xa0,
++0x24,
++0xa0,
++0xd5,
++0x79,
++0x65,
++0x3a,
++0x07,
++0x90,
++0xc9,
++0xf6,
++0xa5,
++0xa6,
++0x1f,
++0x84,
++0x1c,
++0x23,
++0x07,
++0x4b,
++0x4f,
++0xa5,
++0x03,
++0xc6,
++0x0f,
++0xf7,
++0x54,
++0xd5,
++0x8b,
++0x7e,
++0x79,
++0x81,
++0x00,
++0xd2,
++0xe9,
++0x3d,
++0xf4,
++0x97,
++0xfe,
++0x84,
++0xcd,
++0x55,
++0xbd,
++0xc9,
++0x8f,
++0x21,
++0x57,
++0x88,
++0x06,
++0x39,
++0x90,
++0x66,
++0x41,
++0x26,
++0x79,
++0x2c,
++0xca,
++0x3f,
++0x95,
++0x87,
++0x01,
++0x11,
++0x2f,
++0x2f,
++0xb0,
++0xe1,
++0x0b,
++0x43,
++0xfc,
++0x5f,
++0x2f,
++0x4f,
++0x67,
++0x04,
++0xdb,
++0x4d,
++0xb7,
++0x72,
++0x4d,
++0xd1,
++0xc5,
++0x76,
++0x73,
++0x4d,
++0x91,
++0x69,
++0xb0,
++0x71,
++0x17,
++0x36,
++0xea,
++0xab,
++0x0a,
++0x3a,
++0xcd,
++0x95,
++0x9b,
++0x76,
++0x1b,
++0x8e,
++0x21,
++0x17,
++0x8f,
++0xc5,
++0x02,
++0xbf,
++0x24,
++0xc7,
++0xc0,
++0x40,
++0xb1,
++0x3b,
++0xc4,
++0x80,
++0x7c,
++0x71,
++0xa5,
++0x51,
++0xdc,
++0xf7,
++0x3a,
++0x58,
++0x7f,
++0xb1,
++0x07,
++0x81,
++0x8a,
++0x10,
++0xd1,
++0xf6,
++0x93,
++0x17,
++0x71,
++0xe0,
++0xfa,
++0x51,
++0x79,
++0x15,
++0xd4,
++0xd7,
++0x8f,
++0xad,
++0xbd,
++0x6f,
++0x38,
++0xe1,
++0x26,
++0x7d,
++0xbc,
++0xf0,
++0x3e,
++0x80,
++0x89,
++0xb4,
++0xec,
++0x8e,
++0x69,
++0x90,
++0xdb,
++0x97,
++0x8a,
++0xf0,
++0x23,
++0x23,
++0x83,
++0x82,
++0x3b,
++0x6a,
++0xb1,
++0xac,
++0xeb,
++0xe7,
++0x99,
++0x74,
++0x2a,
++0x35,
++0x8e,
++0xa9,
++0x64,
++0xfd,
++0x46,
++0x9e,
++0xe8,
++0xe5,
++0x48,
++0x61,
++0x31,
++0x6e,
++0xe6,
++0xfc,
++0x19,
++0x18,
++0x54,
++0xc3,
++0x1b,
++0x4f,
++0xd6,
++0x00,
++0x44,
++0x87,
++0x1c,
++0x37,
++0x45,
++0xea,
++0xf5,
++0xc9,
++0xcb,
++0x0f,
++0x0c,
++0x55,
++0xec,
++0xcf,
++0x6a,
++0xc2,
++0x45,
++0x26,
++0x23,
++0xa2,
++0x31,
++0x52,
++0x4d,
++0xee,
++0x21,
++0x7d,
++0xfd,
++0x58,
++0x72,
++0xc2,
++0x28,
++0xc5,
++0x8e,
++0xa9,
++0xd0,
++0xee,
++0x01,
++0x77,
++0x08,
++0xa5,
++0xf0,
++0x22,
++0x2b,
++0x47,
++0x79,
++0x2b,
++0xcf,
++0x9a,
++0x46,
++0xb5,
++0x8f,
++0xfd,
++0x64,
++0xa2,
++0xb5,
++0xed,
++0x02,
++0x03,
++0x01,
++0x00,
++0x01,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x01,
++0x00,
++0x20,
++0x44,
++0xfe,
++0xa9,
++0x9e,
++0xdd,
++0x9b,
++0xea,
++0xce,
++0x25,
++0x75,
++0x08,
++0xf0,
++0x2b,
++0x53,
++0xf7,
++0x5a,
++0x36,
++0x1c,
++0x4a,
++0x23,
++0x7f,
++0xd0,
++0x41,
++0x3c,
++0x12,
++0x2b,
++0xb9,
++0x80,
++0x4e,
++0x8a,
++0x15,
++0x5d,
++0x1f,
++0x40,
++0xa7,
++0x26,
++0x28,
++0x32,
++0xc3,
++0x5b,
++0x06,
++0x28,
++0x2d,
++0x3d,
++0x08,
++0x09,
++0x1e,
++0x01,
++0xe9,
++0x67,
++0xe3,
++0x33,
++0xe6,
++0x15,
++0x45,
++0x39,
++0xee,
++0x17,
++0x83,
++0xdb,
++0x42,
++0xff,
++0x7f,
++0x35,
++0xf4,
++0xac,
++0x16,
++0xdb,
++0xba,
++0xb8,
++0x1a,
++0x20,
++0x21,
++0x41,
++0xff,
++0xf3,
++0x92,
++0xff,
++0x65,
++0x6e,
++0x29,
++0x16,
++0xd0,
++0xbf,
++0x8d,
++0xdf,
++0x48,
++0x2c,
++0x73,
++0x36,
++0x7f,
++0x22,
++0xe6,
++0xee,
++0x78,
++0xb4,
++0x63,
++0x83,
++0x0e,
++0x39,
++0xeb,
++0xaf,
++0x10,
++0x2a,
++0x90,
++0xd3,
++0xfc,
++0xe6,
++0xc3,
++0x8f,
++0x97,
++0x5b,
++0x76,
++0xbf,
++0x9b,
++0xf5,
++0x98,
++0xd2,
++0x53,
++0x06,
++0x8b,
++0xf8,
++0xa4,
++0x04,
++0x9b,
++0x1b,
++0x62,
++0x6a,
++0x9d,
++0xac,
++0xe6,
++0x4b,
++0x0d,
++0xc9,
++0xd7,
++0x56,
++0x63,
++0x15,
++0x01,
++0x38,
++0x8c,
++0xbe,
++0xf1,
++0x44,
++0xc4,
++0x38,
++0x27,
++0xe0,
++0xcf,
++0x72,
++0xd6,
++0x3d,
++0xe4,
++0xf7,
++0x4b,
++0x3b,
++0xd2,
++0xb1,
++0x0c,
++0xd5,
++0x83,
++0x6d,
++0x1e,
++0x10,
++0x04,
++0x69,
++0x29,
++0x88,
++0x69,
++0xe0,
++0x7d,
++0xd7,
++0xdb,
++0xb4,
++0x59,
++0x72,
++0x8d,
++0x9d,
++0x3c,
++0x43,
++0xaf,
++0xc6,
++0x7d,
++0xb7,
++0x21,
++0x15,
++0x52,
++0x8a,
++0xe9,
++0x9b,
++0x6b,
++0x2e,
++0xe8,
++0x27,
++0x3c,
++0x3f,
++0x2d,
++0x84,
++0xfb,
++0x9a,
++0x22,
++0x0a,
++0x9f,
++0x6a,
++0x25,
++0xe6,
++0x39,
++0xe4,
++0x74,
++0x73,
++0xb6,
++0x2a,
++0x70,
++0xaa,
++0x1d,
++0xcb,
++0xcc,
++0xd4,
++0xa0,
++0x1b,
++0x26,
++0x71,
++0x63,
++0x04,
++0xc5,
++0x12,
++0x21,
++0x48,
++0xba,
++0x92,
++0x27,
++0x06,
++0xa8,
++0x3e,
++0x6d,
++0xa1,
++0x43,
++0xa5,
++0xd2,
++0x2a,
++0xf7,
++0xca,
++0xc4,
++0x26,
++0xe8,
++0x5b,
++0x1f,
++0xe4,
++0xdc,
++0x89,
++0xdc,
++0x1f,
++0x04,
++0x79,
++0x3f,
++0x30,
++0x82,
++0x02,
++0xcd,
++0x30,
++0x82,
++0x01,
++0xb5,
++0x02,
++0x14,
++0x3a,
++0xbb,
++0xc6,
++0xec,
++0x14,
++0x6e,
++0x09,
++0xd1,
++0xb6,
++0x01,
++0x6a,
++0xb9,
++0xd6,
++0xcf,
++0x71,
++0xdd,
++0x23,
++0x3f,
++0x03,
++0x28,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x30,
++0x22,
++0x31,
++0x20,
++0x30,
++0x1e,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x17,
++0x72,
++0x6f,
++0x6d,
++0x61,
++0x69,
++0x6e,
++0x2e,
++0x70,
++0x65,
++0x72,
++0x69,
++0x65,
++0x72,
++0x40,
++0x67,
++0x6d,
++0x61,
++0x69,
++0x6c,
++0x2e,
++0x63,
++0x6f,
++0x6d,
++0x30,
++0x20,
++0x17,
++0x0d,
++0x32,
++0x30,
++0x30,
++0x32,
++0x32,
++0x34,
++0x31,
++0x39,
++0x30,
++0x31,
++0x34,
++0x34,
++0x5a,
++0x18,
++0x0f,
++0x32,
++0x31,
++0x32,
++0x30,
++0x30,
++0x31,
++0x33,
++0x31,
++0x31,
++0x39,
++0x30,
++0x31,
++0x34,
++0x34,
++0x5a,
++0x30,
++0x22,
++0x31,
++0x20,
++0x30,
++0x1e,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x17,
++0x72,
++0x6f,
++0x6d,
++0x61,
++0x69,
++0x6e,
++0x2e,
++0x70,
++0x65,
++0x72,
++0x69,
++0x65,
++0x72,
++0x40,
++0x67,
++0x6d,
++0x61,
++0x69,
++0x6c,
++0x2e,
++0x63,
++0x6f,
++0x6d,
++0x30,
++0x82,
++0x01,
++0x22,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x01,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x0f,
++0x00,
++0x30,
++0x82,
++0x01,
++0x0a,
++0x02,
++0x82,
++0x01,
++0x01,
++0x00,
++0xf0,
++0xb8,
++0x4f,
++0x3f,
++0x70,
++0x78,
++0xf8,
++0x74,
++0x45,
++0xa2,
++0x28,
++0xaf,
++0x04,
++0x75,
++0x04,
++0xa3,
++0xf3,
++0xa7,
++0xc7,
++0x04,
++0xac,
++0xb6,
++0xe1,
++0xfc,
++0xe1,
++0xc0,
++0x3d,
++0xe0,
++0x26,
++0x90,
++0x8a,
++0x45,
++0x60,
++0xc4,
++0x75,
++0xf3,
++0x1a,
++0x33,
++0x37,
++0x56,
++0x7d,
++0x30,
++0x07,
++0x75,
++0x0e,
++0xa6,
++0x79,
++0x06,
++0x95,
++0x9d,
++0x17,
++0x3c,
++0x09,
++0xa9,
++0x7f,
++0xab,
++0x95,
++0x5d,
++0xed,
++0xe0,
++0x75,
++0x26,
++0x2f,
++0x65,
++0x65,
++0xcd,
++0x61,
++0xb1,
++0x33,
++0x27,
++0x67,
++0x41,
++0xa1,
++0x01,
++0x13,
++0xe9,
++0x13,
++0x6a,
++0x6d,
++0x4e,
++0x98,
++0xe1,
++0x9e,
++0x7b,
++0x0b,
++0x5b,
++0x44,
++0xef,
++0x68,
++0x5a,
++0x6f,
++0x7d,
++0x97,
++0xa1,
++0x33,
++0x22,
++0x97,
++0x12,
++0x21,
++0x09,
++0x8f,
++0x90,
++0xe0,
++0x25,
++0x94,
++0xdd,
++0x8a,
++0x3a,
++0xf7,
++0x4a,
++0x60,
++0x04,
++0x26,
++0x6d,
++0x00,
++0x82,
++0xe4,
++0xcf,
++0x64,
++0x1c,
++0x79,
++0x15,
++0x24,
++0xf2,
++0x42,
++0x86,
++0xf5,
++0x10,
++0x86,
++0xac,
++0x20,
++0x88,
++0x90,
++0x87,
++0xdf,
++0x8c,
++0x37,
++0x7c,
++0xbf,
++0x35,
++0xd5,
++0x6f,
++0x9f,
++0x77,
++0xc3,
++0xcd,
++0x69,
++0x25,
++0x06,
++0xc2,
++0x65,
++0x51,
++0x71,
++0x89,
++0x7f,
++0x6e,
++0x4d,
++0xe5,
++0xd5,
++0x8a,
++0x36,
++0x1a,
++0xad,
++0xc1,
++0x18,
++0xd6,
++0x14,
++0x42,
++0x87,
++0xf0,
++0x93,
++0x83,
++0xf1,
++0x99,
++0x74,
++0xc4,
++0x13,
++0xaa,
++0x3b,
++0x66,
++0x85,
++0x6f,
++0xe0,
++0xbc,
++0x5f,
++0xb6,
++0x40,
++0xa6,
++0x41,
++0x06,
++0x0a,
++0xba,
++0x0e,
++0xe9,
++0x32,
++0x44,
++0x10,
++0x39,
++0x53,
++0xcd,
++0xbf,
++0xf3,
++0xd3,
++0x26,
++0xf6,
++0xb6,
++0x2b,
++0x40,
++0x2e,
++0xb9,
++0x88,
++0xc1,
++0xf4,
++0xe3,
++0xa0,
++0x28,
++0x77,
++0x4f,
++0xba,
++0xa8,
++0xca,
++0x9c,
++0x05,
++0xba,
++0x88,
++0x96,
++0x99,
++0x54,
++0x89,
++0xa2,
++0x8d,
++0xf3,
++0x73,
++0xa1,
++0x8c,
++0x4a,
++0xa8,
++0x71,
++0xee,
++0x2e,
++0xd2,
++0x83,
++0x14,
++0x48,
++0xbd,
++0x98,
++0xc6,
++0xce,
++0xdc,
++0xa8,
++0xa3,
++0x97,
++0x2e,
++0x40,
++0x16,
++0x2f,
++0x02,
++0x03,
++0x01,
++0x00,
++0x01,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x01,
++0x00,
++0x76,
++0x5d,
++0x03,
++0x3d,
++0xb6,
++0x96,
++0x00,
++0x1b,
++0x6e,
++0x0c,
++0xdd,
++0xbb,
++0xc8,
++0xdf,
++0xbc,
++0xeb,
++0x6c,
++0x01,
++0x40,
++0x1a,
++0x2b,
++0x07,
++0x60,
++0xa1,
++0x1a,
++0xe1,
++0x43,
++0x57,
++0xfa,
++0xbe,
++0xde,
++0xbb,
++0x8f,
++0x73,
++0xf3,
++0x92,
++0xa2,
++0xaa,
++0x83,
++0x01,
++0xc1,
++0x17,
++0xe4,
++0x9d,
++0x09,
++0x41,
++0xe0,
++0x32,
++0x33,
++0x97,
++0x4b,
++0xf2,
++0xdc,
++0x0f,
++0x8b,
++0xa8,
++0xb8,
++0x5a,
++0x04,
++0x86,
++0xf6,
++0x71,
++0xa1,
++0x97,
++0xd0,
++0x54,
++0x56,
++0x10,
++0x8e,
++0x54,
++0x99,
++0x0d,
++0x2a,
++0xa9,
++0xaf,
++0x1b,
++0x55,
++0x59,
++0x06,
++0x2b,
++0xa4,
++0x5f,
++0xb1,
++0x54,
++0xa6,
++0xec,
++0xc7,
++0xd6,
++0x43,
++0xee,
++0x86,
++0x2c,
++0x9b,
++0x18,
++0x9d,
++0x8f,
++0x00,
++0x82,
++0xc1,
++0x88,
++0x61,
++0x16,
++0x85,
++0x3c,
++0x17,
++0x56,
++0xfe,
++0x6a,
++0xa0,
++0x7a,
++0x68,
++0xc5,
++0x7b,
++0x3d,
++0x3c,
++0xb6,
++0x13,
++0x18,
++0x99,
++0x6d,
++0x74,
++0x65,
++0x13,
++0x67,
++0xb7,
++0xfc,
++0x5a,
++0x44,
++0x48,
++0x72,
++0xa0,
++0x73,
++0xb8,
++0xff,
++0x02,
++0x9d,
++0x7c,
++0x5b,
++0xf9,
++0x7c,
++0x75,
++0x0a,
++0x3c,
++0x81,
++0x80,
++0x3c,
++0x41,
++0xf2,
++0xd5,
++0xfa,
++0x3d,
++0x1f,
++0xe3,
++0xda,
++0x8c,
++0xa5,
++0x17,
++0x1f,
++0x53,
++0x1a,
++0x75,
++0xad,
++0x4e,
++0x11,
++0x1c,
++0x07,
++0xec,
++0x0a,
++0x69,
++0xfd,
++0x33,
++0xfa,
++0x32,
++0x7e,
++0x66,
++0xf5,
++0x29,
++0xe8,
++0x4d,
++0x8a,
++0xfa,
++0x0d,
++0x4b,
++0x68,
++0xc3,
++0x95,
++0x11,
++0xba,
++0x6f,
++0x1e,
++0x07,
++0x8c,
++0x85,
++0xc7,
++0xc7,
++0xc9,
++0xc1,
++0x30,
++0xa3,
++0x70,
++0xb0,
++0xa1,
++0xe0,
++0xd5,
++0x85,
++0x15,
++0x94,
++0x77,
++0xc1,
++0x1c,
++0x91,
++0xf1,
++0x5f,
++0x50,
++0xcd,
++0x2c,
++0x57,
++0x4b,
++0x22,
++0x4f,
++0xee,
++0x95,
++0xd7,
++0xa7,
++0xa4,
++0x59,
++0x62,
++0xae,
++0xb9,
++0xbf,
++0xd7,
++0x63,
++0x5a,
++0x04,
++0xfc,
++0x24,
++0x11,
++0xae,
++0x34,
++0x4b,
++0xf4,
++0x0c,
++0x9f,
++0x0b,
++0x59,
++0x7d,
++0x27,
++0x39,
++0x54,
++0x69,
++0x4f,
++0xfd,
++0x6e,
++0x44,
++0x9f,
++0x21,

+ 329 - 0
target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch

@@ -0,0 +1,329 @@
+From 3ece03b1575b0c3a0989e372aaaa4557ae74dc89 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 20 Jul 2023 11:28:20 +0100
+Subject: [PATCH] fixup! Add support for all the downstream rpi sound card
+ drivers
+
+Replace the Allo Dac clock driver with an extension of the
+HiFiBerry clock driver that it cloned.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/clk/Makefile               |   1 -
+ drivers/clk/clk-allo-dac.c         | 161 -----------------------------
+ drivers/clk/clk-hifiberry-dacpro.c |  57 ++++++----
+ sound/soc/bcm/Kconfig              |   1 +
+ 4 files changed, 40 insertions(+), 180 deletions(-)
+ delete mode 100644 drivers/clk/clk-allo-dac.c
+
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -19,7 +19,6 @@ endif
+ 
+ # hardware specific clock types
+ # please keep this section sorted lexicographically by file path name
+-obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC)	+= clk-allo-dac.o
+ obj-$(CONFIG_COMMON_CLK_APPLE_NCO)  	+= clk-apple-nco.o
+ obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
+ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)	+= clk-axi-clkgen.o
+--- a/drivers/clk/clk-allo-dac.c
++++ /dev/null
+@@ -1,161 +0,0 @@
+-/*
+- * Clock Driver for Allo DAC
+- *
+- * Author:	Baswaraj K <[email protected]>
+- *		Copyright 2016
+- *		based on code by Stuart MacLean
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * version 2 as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful, but
+- * WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+- * General Public License for more details.
+- */
+-
+-#include <linux/clk-provider.h>
+-#include <linux/clkdev.h>
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/slab.h>
+-#include <linux/platform_device.h>
+-
+-/* Clock rate of CLK44EN attached to GPIO6 pin */
+-#define CLK_44EN_RATE 45158400UL
+-/* Clock rate of CLK48EN attached to GPIO3 pin */
+-#define CLK_48EN_RATE 49152000UL
+-
+-/**
+- * struct allo_dac_clk - Common struct to the Allo DAC
+- * @hw: clk_hw for the common clk framework
+- * @mode: 0 => CLK44EN, 1 => CLK48EN
+- */
+-struct clk_allo_hw {
+-	struct clk_hw hw;
+-	uint8_t mode;
+-};
+-
+-#define to_allo_clk(_hw) container_of(_hw, struct clk_allo_hw, hw)
+-
+-static const struct of_device_id clk_allo_dac_dt_ids[] = {
+-	{ .compatible = "allo,dac-clk",},
+-	{ }
+-};
+-MODULE_DEVICE_TABLE(of, clk_allo_dac_dt_ids);
+-
+-static unsigned long clk_allo_dac_recalc_rate(struct clk_hw *hw,
+-	unsigned long parent_rate)
+-{
+-	return (to_allo_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+-		CLK_48EN_RATE;
+-}
+-
+-static long clk_allo_dac_round_rate(struct clk_hw *hw,
+-	unsigned long rate, unsigned long *parent_rate)
+-{
+-	long actual_rate;
+-
+-	if (rate <= CLK_44EN_RATE) {
+-		actual_rate = (long)CLK_44EN_RATE;
+-	} else if (rate >= CLK_48EN_RATE) {
+-		actual_rate = (long)CLK_48EN_RATE;
+-	} else {
+-		long diff44Rate = (long)(rate - CLK_44EN_RATE);
+-		long diff48Rate = (long)(CLK_48EN_RATE - rate);
+-
+-		if (diff44Rate < diff48Rate)
+-			actual_rate = (long)CLK_44EN_RATE;
+-		else
+-			actual_rate = (long)CLK_48EN_RATE;
+-	}
+-	return actual_rate;
+-}
+-
+-
+-static int clk_allo_dac_set_rate(struct clk_hw *hw,
+-	unsigned long rate, unsigned long parent_rate)
+-{
+-	unsigned long actual_rate;
+-	struct clk_allo_hw *clk = to_allo_clk(hw);
+-
+-	actual_rate = (unsigned long)clk_allo_dac_round_rate(hw, rate,
+-		&parent_rate);
+-	clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
+-	return 0;
+-}
+-
+-
+-const struct clk_ops clk_allo_dac_rate_ops = {
+-	.recalc_rate = clk_allo_dac_recalc_rate,
+-	.round_rate = clk_allo_dac_round_rate,
+-	.set_rate = clk_allo_dac_set_rate,
+-};
+-
+-static int clk_allo_dac_probe(struct platform_device *pdev)
+-{
+-	int ret;
+-	struct clk_allo_hw *proclk;
+-	struct clk *clk;
+-	struct device *dev;
+-	struct clk_init_data init;
+-
+-	dev = &pdev->dev;
+-
+-	proclk = kzalloc(sizeof(struct clk_allo_hw), GFP_KERNEL);
+-	if (!proclk)
+-		return -ENOMEM;
+-
+-	init.name = "clk-allo-dac";
+-	init.ops = &clk_allo_dac_rate_ops;
+-	init.flags = 0;
+-	init.parent_names = NULL;
+-	init.num_parents = 0;
+-
+-	proclk->mode = 0;
+-	proclk->hw.init = &init;
+-
+-	clk = devm_clk_register(dev, &proclk->hw);
+-	if (!IS_ERR(clk)) {
+-		ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+-			clk);
+-	} else {
+-		dev_err(dev, "Fail to register clock driver\n");
+-		kfree(proclk);
+-		ret = PTR_ERR(clk);
+-	}
+-	return ret;
+-}
+-
+-static int clk_allo_dac_remove(struct platform_device *pdev)
+-{
+-	of_clk_del_provider(pdev->dev.of_node);
+-	return 0;
+-}
+-
+-static struct platform_driver clk_allo_dac_driver = {
+-	.probe = clk_allo_dac_probe,
+-	.remove = clk_allo_dac_remove,
+-	.driver = {
+-		.name = "clk-allo-dac",
+-		.of_match_table = clk_allo_dac_dt_ids,
+-	},
+-};
+-
+-static int __init clk_allo_dac_init(void)
+-{
+-	return platform_driver_register(&clk_allo_dac_driver);
+-}
+-core_initcall(clk_allo_dac_init);
+-
+-static void __exit clk_allo_dac_exit(void)
+-{
+-	platform_driver_unregister(&clk_allo_dac_driver);
+-}
+-module_exit(clk_allo_dac_exit);
+-
+-MODULE_DESCRIPTION("Allo DAC clock driver");
+-MODULE_LICENSE("GPL v2");
+-MODULE_ALIAS("platform:clk-allo-dac");
+--- a/drivers/clk/clk-hifiberry-dacpro.c
++++ b/drivers/clk/clk-hifiberry-dacpro.c
+@@ -22,10 +22,12 @@
+ #include <linux/slab.h>
+ #include <linux/platform_device.h>
+ 
+-/* Clock rate of CLK44EN attached to GPIO6 pin */
+-#define CLK_44EN_RATE 22579200UL
+-/* Clock rate of CLK48EN attached to GPIO3 pin */
+-#define CLK_48EN_RATE 24576000UL
++struct ext_clk_rates {
++	/* Clock rate of CLK44EN attached to GPIO6 pin */
++	unsigned long clk_44en;
++	/* Clock rate of CLK48EN attached to GPIO3 pin */
++	unsigned long clk_48en;
++};
+ 
+ /**
+  * struct hifiberry_dacpro_clk - Common struct to the HiFiBerry DAC Pro
+@@ -35,12 +37,24 @@
+ struct clk_hifiberry_hw {
+ 	struct clk_hw hw;
+ 	uint8_t mode;
++	struct ext_clk_rates clk_rates;
+ };
+ 
+ #define to_hifiberry_clk(_hw) container_of(_hw, struct clk_hifiberry_hw, hw)
+ 
++static const struct ext_clk_rates hifiberry_dacpro_clks = {
++	.clk_44en = 22579200UL,
++	.clk_48en = 24576000UL,
++};
++
++static const struct ext_clk_rates allo_dac_clks = {
++	.clk_44en = 45158400UL,
++	.clk_48en = 49152000UL,
++};
++
+ static const struct of_device_id clk_hifiberry_dacpro_dt_ids[] = {
+-	{ .compatible = "hifiberry,dacpro-clk",},
++	{ .compatible = "hifiberry,dacpro-clk", &hifiberry_dacpro_clks },
++	{ .compatible = "allo,dac-clk", &allo_dac_clks },
+ 	{ }
+ };
+ MODULE_DEVICE_TABLE(of, clk_hifiberry_dacpro_dt_ids);
+@@ -48,27 +62,29 @@ MODULE_DEVICE_TABLE(of, clk_hifiberry_da
+ static unsigned long clk_hifiberry_dacpro_recalc_rate(struct clk_hw *hw,
+ 	unsigned long parent_rate)
+ {
+-	return (to_hifiberry_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+-		CLK_48EN_RATE;
++	struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
++	return (clk->mode == 0) ? clk->clk_rates.clk_44en :
++		clk->clk_rates.clk_48en;
+ }
+ 
+ static long clk_hifiberry_dacpro_round_rate(struct clk_hw *hw,
+ 	unsigned long rate, unsigned long *parent_rate)
+ {
++	struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
+ 	long actual_rate;
+ 
+-	if (rate <= CLK_44EN_RATE) {
+-		actual_rate = (long)CLK_44EN_RATE;
+-	} else if (rate >= CLK_48EN_RATE) {
+-		actual_rate = (long)CLK_48EN_RATE;
++	if (rate <= clk->clk_rates.clk_44en) {
++		actual_rate = (long)clk->clk_rates.clk_44en;
++	} else if (rate >= clk->clk_rates.clk_48en) {
++		actual_rate = (long)clk->clk_rates.clk_48en;
+ 	} else {
+-		long diff44Rate = (long)(rate - CLK_44EN_RATE);
+-		long diff48Rate = (long)(CLK_48EN_RATE - rate);
++		long diff44Rate = (long)(rate - clk->clk_rates.clk_44en);
++		long diff48Rate = (long)(clk->clk_rates.clk_48en - rate);
+ 
+ 		if (diff44Rate < diff48Rate)
+-			actual_rate = (long)CLK_44EN_RATE;
++			actual_rate = (long)clk->clk_rates.clk_44en;
+ 		else
+-			actual_rate = (long)CLK_48EN_RATE;
++			actual_rate = (long)clk->clk_rates.clk_48en;
+ 	}
+ 	return actual_rate;
+ }
+@@ -77,12 +93,12 @@ static long clk_hifiberry_dacpro_round_r
+ static int clk_hifiberry_dacpro_set_rate(struct clk_hw *hw,
+ 	unsigned long rate, unsigned long parent_rate)
+ {
+-	unsigned long actual_rate;
+ 	struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
++	unsigned long actual_rate;
+ 
+ 	actual_rate = (unsigned long)clk_hifiberry_dacpro_round_rate(hw, rate,
+ 		&parent_rate);
+-	clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
++	clk->mode = (actual_rate == clk->clk_rates.clk_44en) ? 0 : 1;
+ 	return 0;
+ }
+ 
+@@ -95,13 +111,17 @@ const struct clk_ops clk_hifiberry_dacpr
+ 
+ static int clk_hifiberry_dacpro_probe(struct platform_device *pdev)
+ {
+-	int ret;
++	const struct of_device_id *of_id;
+ 	struct clk_hifiberry_hw *proclk;
+ 	struct clk *clk;
+ 	struct device *dev;
+ 	struct clk_init_data init;
++	int ret;
+ 
+ 	dev = &pdev->dev;
++	of_id = of_match_node(clk_hifiberry_dacpro_dt_ids, dev->of_node);
++	if (!of_id)
++		return -EINVAL;
+ 
+ 	proclk = kzalloc(sizeof(struct clk_hifiberry_hw), GFP_KERNEL);
+ 	if (!proclk)
+@@ -115,6 +135,7 @@ static int clk_hifiberry_dacpro_probe(st
+ 
+ 	proclk->mode = 0;
+ 	proclk->hw.init = &init;
++	memcpy(&proclk->clk_rates, of_id->data, sizeof(proclk->clk_rates));
+ 
+ 	clk = devm_clk_register(dev, &proclk->hw);
+ 	if (!IS_ERR(clk)) {
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -271,6 +271,7 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+ 	tristate "Support for Allo Boss DAC"
+ 	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x_I2C
++	select COMMON_CLK_HIFIBERRY_DACPRO
+ 	help
+ 	  Say Y or M if you want to add support for Allo Boss DAC.
+ 

+ 21 - 0
target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch

@@ -0,0 +1,21 @@
+From 2addf7045f2b4866ab819f48e4d32f5734a32134 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Thu, 20 Jul 2023 15:15:27 +0100
+Subject: [PATCH] fixup! drm/tc358762: Set the pre_enable_upstream_first flag
+ to configure DSI host
+
+---
+ drivers/gpu/drm/bridge/tc358762.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/bridge/tc358762.c
++++ b/drivers/gpu/drm/bridge/tc358762.c
+@@ -229,7 +229,7 @@ static int tc358762_probe(struct mipi_ds
+ 	ctx->bridge.funcs = &tc358762_bridge_funcs;
+ 	ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
+ 	ctx->bridge.of_node = dev->of_node;
+-	ctx->bridge.pre_enable_upstream_first = true;
++	ctx->bridge.pre_enable_prev_first = true;
+ 
+ 	drm_bridge_add(&ctx->bridge);
+ 

+ 48 - 0
target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch

@@ -0,0 +1,48 @@
+From b84b8a9ad2046a855a7044b6368def01ddd5de6e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 21 Jul 2023 16:50:56 +0100
+Subject: [PATCH] rpi sound cards: Fix Codec Zero rate switching
+
+The Raspberry Pi Codec Zero (and IQaudIO Codec) don't notify the DA7213
+codec when it needs to change PLL frequencies. As a result, audio can
+be played at the wrong rate - play a 48kHz sound immediately after a
+44.1kHz sound to see the effect, but in some configurations the codec
+can lock into the wrong state and always get some rates wrong.
+
+Add the necessary notification to fix the issue.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/bcm/iqaudio-codec.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/sound/soc/bcm/iqaudio-codec.c
++++ b/sound/soc/bcm/iqaudio-codec.c
+@@ -143,6 +143,7 @@ static int snd_rpi_iqaudio_codec_hw_para
+ 					   struct snd_pcm_hw_params *params)
+ {
+ 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
++	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ 	unsigned int samplerate = params_rate(params);
+ 
+ 	switch (samplerate) {
+@@ -152,15 +153,17 @@ static int snd_rpi_iqaudio_codec_hw_para
+ 	case 48000:
+ 	case 96000:
+ 		pll_out = DA7213_PLL_FREQ_OUT_98304000;
+-		return 0;
++		break;
+ 	case 44100:
+ 	case 88200:
+ 		pll_out = DA7213_PLL_FREQ_OUT_90316800;
+-		return 0;
++		break;
+ 	default:
+ 		dev_err(rtd->dev,"Unsupported samplerate %d\n", samplerate);
+ 		return -EINVAL;
+ 	}
++
++	return snd_soc_dai_set_pll(codec_dai, 0, DA7213_SYSCLK_PLL, 0, pll_out);
+ }
+ 
+ static const struct snd_soc_ops snd_rpi_iqaudio_codec_ops = {

+ 68 - 0
target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch

@@ -0,0 +1,68 @@
+From 31822340129e3c4030500d7f30ce4d19bbf9dd40 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 24 Jul 2023 17:34:47 +0100
+Subject: [PATCH] overlays: Add trickle-voltage-mv parameter to RTCs
+
+The RV3032 RTC requires an additional DT property to enable trickle
+charging. Add a parameter - trickle-voltage-mv - to the i2c-rtc
+and i2c-rtc-gpio overlays to set it.
+
+See: https://github.com/raspberrypi/linux/issues/5547
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README              | 12 ++++++++----
+ arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi |  2 ++
+ 2 files changed, 10 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1957,13 +1957,15 @@ Params: abx80x                  Select o
+                                 "schottky" (ABx80x and RV1805 only)
+ 
+         trickle-resistor-ohms   Resistor value for trickle charge (DS1339,
+-                                ABx80x, RV1805, RV3028)
++                                ABx80x, BQ32000, RV1805, RV3028, RV3032)
++
++        trickle-voltage-mv      Charge pump voltage for trickle charge (RV3032)
+ 
+         wakeup-source           Specify that the RTC can be used as a wakeup
+                                 source
+ 
+         backup-switchover-mode  Backup power supply switch mode. Must be 0 for
+-                                off or 1 for Vdd < VBackup (RV3028 only)
++                                off or 1 for Vdd < VBackup (RV3028, RV3032)
+ 
+ 
+ Name:   i2c-rtc-gpio
+@@ -2027,13 +2029,15 @@ Params: abx80x                  Select o
+                                 "schottky" (ABx80x and RV1805 only)
+ 
+         trickle-resistor-ohms   Resistor value for trickle charge (DS1339,
+-                                ABx80x, RV1805, RV3028)
++                                ABx80x, BQ32000, RV1805, RV3028, RV3032)
++
++        trickle-voltage-mv      Charge pump voltage for trickle charge (RV3032)
+ 
+         wakeup-source           Specify that the RTC can be used as a wakeup
+                                 source
+ 
+         backup-switchover-mode  Backup power supply switch mode. Must be 0 for
+-                                off or 1 for Vdd < VBackup (RV3028 only)
++                                off or 1 for Vdd < VBackup (RV3028, RV3032)
+ 
+         i2c_gpio_sda            GPIO used for I2C data (default "23")
+ 
+--- a/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi
+@@ -339,8 +339,10 @@
+ 					<&ds1340>,"trickle-resistor-ohms:0",
+ 					<&abx80x>,"abracon,tc-resistor:0",
+ 					<&rv3028>,"trickle-resistor-ohms:0",
++					<&rv3032>,"trickle-resistor-ohms:0",
+ 					<&rv1805>,"abracon,tc-resistor:0",
+ 					<&bq32000>,"abracon,tc-resistor:0";
++		trickle-voltage-mv = <&rv3032>,"trickle-voltage-millivolts:0";
+ 		backup-switchover-mode = <&rv3028>,"backup-switchover-mode:0";
+ 		wakeup-source = <&ds1339>,"wakeup-source?",
+ 				<&ds3231>,"wakeup-source?",

+ 25 - 0
target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch

@@ -0,0 +1,25 @@
+From 5fb3b300557d6a6902e7321f42fdabb8c09eef54 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Fri, 28 Jul 2023 12:00:40 +0100
+Subject: [PATCH] drivers: media: imx296: Add standby delay during probe
+
+Add a 2-5ms delay when coming out of standby and before reading the
+sensor info register durning probe, as instructed by the datasheet. This
+standby delay is already present when the sensor starts streaming.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/i2c/imx296.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -1022,6 +1022,8 @@ static int imx296_identify_model(struct
+ 		return ret;
+ 	}
+ 
++	usleep_range(2000, 5000);
++
+ 	ret = imx296_read(sensor, IMX296_SENSOR_INFO);
+ 	if (ret < 0) {
+ 		dev_err(sensor->dev, "failed to read sensor information (%d)\n",

+ 78 - 0
target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch

@@ -0,0 +1,78 @@
+From e1016d61e3dcb058932e8ec5072f2c4bbb05fcb7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Sun, 30 Jul 2023 18:27:03 +0100
+Subject: [PATCH] overlays: Add bmp380 to i2c-sensor overlay
+
+Add support for the BMP380 pressor sensor to the i2c-sensor overlay.
+
+See: https://github.com/raspberrypi/linux/issues/5558
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README             |  7 +++++--
+ .../boot/dts/overlays/i2c-sensor-common.dtsi  | 19 ++++++++++++++++++-
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2052,8 +2052,8 @@ Info:   Adds support for a number of I2C
+         light level and chemical sensors on i2c_arm
+ Load:   dtoverlay=i2c-sensor,<param>=<val>
+ Params: addr                    Set the address for the BH1750, BME280, BME680,
+-                                BMP280, CCS811, DS1621, HDC100X, JC42, LM75,
+-                                MCP980x, MPU6050, MPU9250, MS5637, MS5803,
++                                BMP280, BMP380, CCS811, DS1621, HDC100X, JC42,
++                                LM75, MCP980x, MPU6050, MPU9250, MS5637, MS5803,
+                                 MS5805, MS5837, MS8607, SHT3x or TMP102
+ 
+         aht10                   Select the Aosong AHT10 temperature and humidity
+@@ -2075,6 +2075,9 @@ Params: addr                    Set the
+         bmp280                  Select the Bosch Sensortronic BMP280
+                                 Valid addresses 0x76-0x77, default 0x76
+ 
++        bmp380                  Select the Bosch Sensortronic BMP380
++                                Valid addresses 0x76-0x77, default 0x76
++
+         bno055                  Select the Bosch Sensortronic BNO055 IMU
+                                 Valid address 0x28-0x29, default 0x29
+ 
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -493,11 +493,27 @@
+ 		};
+ 	};
+ 
++	fragment@33 {
++		target = <&i2cbus>;
++		__dormant__ {
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "okay";
++
++			bmp380: bmp380@76 {
++				compatible = "bosch,bmp380";
++				reg = <0x76>;
++				status = "okay";
++			};
++		};
++	};
++
+ 	__overrides__ {
+ 		bme280 = <0>,"+0";
+ 		bmp085 = <0>,"+1";
+ 		bmp180 = <0>,"+2";
+ 		bmp280 = <0>,"+3";
++		bmp380 = <0>,"+33";
+ 		htu21 = <0>,"+4";
+ 		lm75 = <0>,"+5";
+ 		lm75addr = <&lm75>,"reg:0";
+@@ -535,7 +551,8 @@
+ 			<&ms5637>,"reg:0", <&ms5803>,"reg:0", <&ms5805>,"reg:0",
+ 			<&ms5837>,"reg:0", <&ms8607>,"reg:0",
+ 			<&mpu6050>,"reg:0", <&mpu9250>,"reg:0",
+-			<&bno055>,"reg:0", <&sht4x>,"reg:0";
++			<&bno055>,"reg:0", <&sht4x>,"reg:0",
++			<&bmp380>,"reg:0";
+ 		int_pin = <&max30102>, "interrupts:0",
+ 			<&mpu6050>, "interrupts:0",
+ 			<&mpu9250>, "interrupts:0";

+ 162 - 0
target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch

@@ -0,0 +1,162 @@
+From 4b729a06b15fc5ee3694dcc62346dcb718ae4290 Mon Sep 17 00:00:00 2001
+From: Oliver Hartkopp <[email protected]>
+Date: Sun, 26 Mar 2023 13:59:11 +0200
+Subject: [PATCH] can: isotp: add module parameter for maximum pdu size
+
+commit 96d1c81e6a0478535342dff6c730adb076cd84e8 upstream.
+
+With ISO 15765-2:2016 the PDU size is not limited to 2^12 - 1 (4095)
+bytes but can be represented as a 32 bit unsigned integer value which
+allows 2^32 - 1 bytes (~4GB). The use-cases like automotive unified
+diagnostic services (UDS) and flashing of ECUs still use the small
+static buffers which are provided at socket creation time.
+
+When a use-case requires to transfer PDUs up to 1025 kByte the maximum
+PDU size can now be extended by setting the module parameter
+max_pdu_size. The extended size buffers are only allocated on a
+per-socket/connection base when needed at run-time.
+
+changes since v2: https://lore.kernel.org/all/[email protected]
+- use ARRAY_SIZE() to reference DEFAULT_MAX_PDU_SIZE only at one place
+
+changes since v1: https://lore.kernel.org/all/[email protected]
+- limit the minimum 'max_pdu_size' to 4095 to maintain the classic
+  behavior before ISO 15765-2:2016
+
+Link: https://github.com/raspberrypi/linux/issues/5371
+Signed-off-by: Oliver Hartkopp <[email protected]>
+Link: https://lore.kernel.org/all/[email protected]
+Signed-off-by: Marc Kleine-Budde <[email protected]>
+---
+ net/can/isotp.c | 65 ++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 56 insertions(+), 9 deletions(-)
+
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -85,10 +85,21 @@ MODULE_ALIAS("can-proto-6");
+ 
+ /* ISO 15765-2:2016 supports more than 4095 byte per ISO PDU as the FF_DL can
+  * take full 32 bit values (4 Gbyte). We would need some good concept to handle
+- * this between user space and kernel space. For now increase the static buffer
+- * to something about 64 kbyte to be able to test this new functionality.
++ * this between user space and kernel space. For now set the static buffer to
++ * something about 8 kbyte to be able to test this new functionality.
+  */
+-#define MAX_MSG_LENGTH 66000
++#define DEFAULT_MAX_PDU_SIZE 8300
++
++/* maximum PDU size before ISO 15765-2:2016 extension was 4095 */
++#define MAX_12BIT_PDU_SIZE 4095
++
++/* limit the isotp pdu size from the optional module parameter to 1MByte */
++#define MAX_PDU_SIZE (1025 * 1024U)
++
++static unsigned int max_pdu_size __read_mostly = DEFAULT_MAX_PDU_SIZE;
++module_param(max_pdu_size, uint, 0444);
++MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu size (default "
++		 __stringify(DEFAULT_MAX_PDU_SIZE) ")");
+ 
+ /* N_PCI type values in bits 7-4 of N_PCI bytes */
+ #define N_PCI_SF 0x00	/* single frame */
+@@ -124,13 +135,15 @@ enum {
+ };
+ 
+ struct tpcon {
+-	unsigned int idx;
++	u8 *buf;
++	unsigned int buflen;
+ 	unsigned int len;
++	unsigned int idx;
+ 	u32 state;
+ 	u8 bs;
+ 	u8 sn;
+ 	u8 ll_dl;
+-	u8 buf[MAX_MSG_LENGTH + 1];
++	u8 sbuf[DEFAULT_MAX_PDU_SIZE];
+ };
+ 
+ struct isotp_sock {
+@@ -498,7 +511,17 @@ static int isotp_rcv_ff(struct sock *sk,
+ 	if (so->rx.len + ae + off + ff_pci_sz < so->rx.ll_dl)
+ 		return 1;
+ 
+-	if (so->rx.len > MAX_MSG_LENGTH) {
++	/* PDU size > default => try max_pdu_size */
++	if (so->rx.len > so->rx.buflen && so->rx.buflen < max_pdu_size) {
++		u8 *newbuf = kmalloc(max_pdu_size, GFP_ATOMIC);
++
++		if (newbuf) {
++			so->rx.buf = newbuf;
++			so->rx.buflen = max_pdu_size;
++		}
++	}
++
++	if (so->rx.len > so->rx.buflen) {
+ 		/* send FC frame with overflow status */
+ 		isotp_send_fc(sk, ae, ISOTP_FC_OVFLW);
+ 		return 1;
+@@ -802,7 +825,7 @@ static void isotp_create_fframe(struct c
+ 		cf->data[0] = so->opt.ext_address;
+ 
+ 	/* create N_PCI bytes with 12/32 bit FF_DL data length */
+-	if (so->tx.len > 4095) {
++	if (so->tx.len > MAX_12BIT_PDU_SIZE) {
+ 		/* use 32 bit FF_DL notation */
+ 		cf->data[ae] = N_PCI_FF;
+ 		cf->data[ae + 1] = 0;
+@@ -939,7 +962,17 @@ static int isotp_sendmsg(struct socket *
+ 			goto err_event_drop;
+ 	}
+ 
+-	if (!size || size > MAX_MSG_LENGTH) {
++	/* PDU size > default => try max_pdu_size */
++	if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) {
++		u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL);
++
++		if (newbuf) {
++			so->tx.buf = newbuf;
++			so->tx.buflen = max_pdu_size;
++		}
++	}
++
++	if (!size || size > so->tx.buflen) {
+ 		err = -EINVAL;
+ 		goto err_out_drop;
+ 	}
+@@ -1194,6 +1227,12 @@ static int isotp_release(struct socket *
+ 	so->ifindex = 0;
+ 	so->bound = 0;
+ 
++	if (so->rx.buf != so->rx.sbuf)
++		kfree(so->rx.buf);
++
++	if (so->tx.buf != so->tx.sbuf)
++		kfree(so->tx.buf);
++
+ 	sock_orphan(sk);
+ 	sock->sk = NULL;
+ 
+@@ -1588,6 +1627,11 @@ static int isotp_init(struct sock *sk)
+ 	so->rx.state = ISOTP_IDLE;
+ 	so->tx.state = ISOTP_IDLE;
+ 
++	so->rx.buf = so->rx.sbuf;
++	so->tx.buf = so->tx.sbuf;
++	so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
++	so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
++
+ 	hrtimer_init(&so->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ 	so->rxtimer.function = isotp_rx_timer_handler;
+ 	hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+@@ -1670,7 +1714,10 @@ static __init int isotp_module_init(void
+ {
+ 	int err;
+ 
+-	pr_info("can: isotp protocol\n");
++	max_pdu_size = max_t(unsigned int, max_pdu_size, MAX_12BIT_PDU_SIZE);
++	max_pdu_size = min_t(unsigned int, max_pdu_size, MAX_PDU_SIZE);
++
++	pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
+ 
+ 	err = can_proto_register(&isotp_can_proto);
+ 	if (err < 0)

+ 40 - 0
target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch

@@ -0,0 +1,40 @@
+From e1b03ea9e84320e6bf36a1486abaebbceadd7fc7 Mon Sep 17 00:00:00 2001
+From: Ben Benson <[email protected]>
+Date: Fri, 21 Jul 2023 15:59:51 +0100
+Subject: [PATCH] drivers: media: imx296: Updated imx296 driver for external
+ trigger
+
+Updated imx296 driver to support external trigger mode via XTR pin.
+Added module parameter to control this mode.
+
+Signed-off-by: Ben Benson <[email protected]>
+---
+ drivers/media/i2c/imx296.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -20,6 +20,10 @@
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-subdev.h>
+ 
++static int trigger_mode;
++module_param(trigger_mode, int, 0644);
++MODULE_PARM_DESC(trigger_mode, "Set trigger mode: 0=default, 1=XTRIG");
++
+ #define IMX296_PIXEL_ARRAY_WIDTH			1456
+ #define IMX296_PIXEL_ARRAY_HEIGHT			1088
+ 
+@@ -645,6 +649,12 @@ static int imx296_stream_on(struct imx29
+ 
+ 	imx296_write(sensor, IMX296_CTRL00, 0, &ret);
+ 	usleep_range(2000, 5000);
++
++	if (trigger_mode == 1) {
++		imx296_write(sensor, IMX296_CTRL0B, IMX296_CTRL0B_TRIGEN, &ret);
++		imx296_write(sensor, IMX296_LOWLAGTRG,  IMX296_LOWLAGTRG_FAST, &ret);
++	}
++
+ 	imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+ 
+ 	/* vflip and hflip cannot change during streaming */

+ 26 - 0
target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch

@@ -0,0 +1,26 @@
+From 74bc238e86e62109c74d8f229dc105bf3818b4a7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 2 Aug 2023 14:35:32 +0100
+Subject: [PATCH] media: dt-bindings: imx258: Fix alternate compatible strings
+
+Multiple compatible strings must appear as an enum.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ Documentation/devicetree/bindings/media/i2c/imx258.yaml | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/Documentation/devicetree/bindings/media/i2c/imx258.yaml
++++ b/Documentation/devicetree/bindings/media/i2c/imx258.yaml
+@@ -19,8 +19,9 @@ description: |-
+ 
+ properties:
+   compatible:
+-    const: sony,imx258
+-    const: sony,imx258-pdaf
++    enum:
++      - sony,imx258
++      - sony,imx258-pdaf
+ 
+   assigned-clocks: true
+   assigned-clock-parents: true

+ 21 - 0
target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch

@@ -0,0 +1,21 @@
+From 282819aead0166af415b780241dc2def4caee7f4 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <[email protected]>
+Date: Mon, 3 Jul 2023 18:12:01 +0000
+Subject: [PATCH] char: broadcom: vc_mem: Fix preprocessor conditional
+
+Signed-off-by: Alexander Winkowski <[email protected]>
+---
+ drivers/char/broadcom/vc_mem.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/char/broadcom/vc_mem.c
++++ b/drivers/char/broadcom/vc_mem.c
+@@ -353,7 +353,7 @@ vc_mem_exit(void)
+ 	pr_debug("%s: called\n", __func__);
+ 
+ 	if (vc_mem_inited) {
+-#if CONFIG_DEBUG_FS
++#ifdef CONFIG_DEBUG_FS
+ 		vc_mem_debugfs_deinit();
+ #endif
+ 		device_destroy(vc_mem_class, vc_mem_devnum);

+ 32 - 0
target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch

@@ -0,0 +1,32 @@
+From ec61075a786c455444a1d5df338a41bacfce0bb1 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <[email protected]>
+Date: Mon, 3 Jul 2023 18:23:02 +0000
+Subject: [PATCH] drivers: dwc_otg: Fix fallthrough warnings
+
+Signed-off-by: Alexander Winkowski <[email protected]>
+---
+ drivers/usb/host/dwc_otg/dwc_otg_hcd.c      | 1 +
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+@@ -2049,6 +2049,7 @@ int fiq_fsm_queue_split_transaction(dwc_
+ 			} else {
+ 				st->fsm = FIQ_PER_SSPLIT_QUEUED;
+ 			}
++			break;
+ 		default:
+ 			break;
+ 	}
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+@@ -402,7 +402,7 @@ int32_t dwc_otg_hcd_handle_rx_status_q_l
+ 			hc->xfer_count += grxsts.b.bcnt;
+ 			hc->xfer_buff += grxsts.b.bcnt;
+ 		}
+-
++		break;
+ 	case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+ 	case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+ 	case DWC_GRXSTS_PKTSTS_CH_HALTED:

+ 61 - 0
target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch

@@ -0,0 +1,61 @@
+From 2dd2f36d10961e3819ff0525ae2567e601973826 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <[email protected]>
+Date: Mon, 3 Jul 2023 18:29:37 +0000
+Subject: [PATCH] vc04_services/vc-sm-cma: Switch one-bit bitfields to bool
+
+Clang 16 warns:
+
+../drivers/staging/vc04_services/vc-sm-cma/vc_sm.c:816:19: warning: implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1 [-Wsingle-bit-bitfield-constant-conversion]
+        buffer->imported = 1;
+                         ^ ~
+../drivers/staging/vc04_services/vc-sm-cma/vc_sm.c:822:17: warning: implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1 [-Wsingle-bit-bitfield-constant-conversion]
+        buffer->in_use = 1;
+                       ^ ~
+2 warnings generated.
+
+Signed-off-by: Alexander Winkowski <[email protected]>
+---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm.c | 6 +++---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm.h | 4 ++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c
+@@ -533,7 +533,7 @@ static void vc_sm_dma_buf_release(struct
+ 
+ 	pr_debug("%s dmabuf %p, buffer %p\n", __func__, dmabuf, buffer);
+ 
+-	buffer->in_use = 0;
++	buffer->in_use = false;
+ 
+ 	/* Unmap on the VPU */
+ 	vc_sm_vpu_free(buffer);
+@@ -813,13 +813,13 @@ vc_sm_cma_import_dmabuf_internal(struct
+ 	buffer->size = import.size;
+ 	buffer->vpu_state = VPU_MAPPED;
+ 
+-	buffer->imported = 1;
++	buffer->imported = true;
+ 	buffer->import.dma_buf = dma_buf;
+ 
+ 	buffer->import.attach = attach;
+ 	buffer->import.sgt = sgt;
+ 	buffer->dma_addr = dma_addr;
+-	buffer->in_use = 1;
++	buffer->in_use = true;
+ 	buffer->kernel_id = import.kernel_id;
+ 
+ 	/*
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h
+@@ -57,8 +57,8 @@ struct vc_sm_buffer {
+ 
+ 	char name[VC_SM_MAX_NAME_LEN];
+ 
+-	int in_use:1;	/* Kernel is still using this resource */
+-	int imported:1;	/* Imported dmabuf */
++	bool in_use:1;   /* Kernel is still using this resource */
++	bool imported:1; /* Imported dmabuf */
+ 
+ 	enum vc_sm_vpu_mapping_state vpu_state;
+ 	u32 vc_handle;	/* VideoCore handle for this buffer */

+ 21 - 0
target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch

@@ -0,0 +1,21 @@
+From 3333d45347d313ea589b8b8da1193d342060a946 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <[email protected]>
+Date: Mon, 3 Jul 2023 18:36:45 +0000
+Subject: [PATCH] media: i2c: ov2311: Fix uninitialized variable usage
+
+Signed-off-by: Alexander Winkowski <[email protected]>
+---
+ drivers/media/i2c/ov2311.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov2311.c
++++ b/drivers/media/i2c/ov2311.c
+@@ -1018,7 +1018,7 @@ static int ov2311_check_sensor_id(struct
+ 				  struct i2c_client *client)
+ {
+ 	struct device *dev = &ov2311->client->dev;
+-	u32 id = 0, id_msb;
++	u32 id = 0, id_msb = 0;
+ 	int ret;
+ 
+ 	ret = ov2311_read_reg(client, OV2311_REG_CHIP_ID + 1,

+ 29 - 0
target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch

@@ -0,0 +1,29 @@
+From e89e7655a197d28df49da2be7e2003436cf52197 Mon Sep 17 00:00:00 2001
+From: Ignacio Larrain <[email protected]>
+Date: Tue, 22 Aug 2023 11:11:56 -0400
+Subject: [PATCH] drm/panel: Fix default values for Waveshare 7.9 inch DSI
+ touchscreen (#5565)
+
+This fixes touchscreen calibration, axis swapping and inversion.
+
+As referenced in https://github.com/raspberrypi/linux/issues/5550
+---
+ .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts    | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
+@@ -93,10 +93,10 @@
+ 				   <&touch>, "touchscreen-size-x:0=800",
+ 				   <&touch>, "touchscreen-size-y:0=480";
+ 		7_9_inch = <&panel>, "compatible=waveshare,7.9inch-panel",
+-				   <&touch>, "touchscreen-size-x:0=400",
+-				   <&touch>, "touchscreen-size-y:0=1280",
++				   <&touch>, "touchscreen-size-x:0=4096",
++				   <&touch>, "touchscreen-size-y:0=4096",
+ 				   <&touch>, "touchscreen-inverted-x?",
+-				   <&touch>, "touchscreen-inverted-y?";
++				   <&touch>, "touchscreen-swapped-x-y?";
+ 		8_0_inch = <&panel>, "compatible=waveshare,8.0inch-panel",
+ 				   <&touch>, "touchscreen-size-x:0=800",
+ 				   <&touch>, "touchscreen-size-y:0=1280",

+ 102 - 0
target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch

@@ -0,0 +1,102 @@
+From 3fa2fbb7f6e60b85086e454403c5eab1af63b1aa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 14 Jun 2023 13:43:58 +0100
+Subject: [PATCH] dtoverlays: Add i2c bus overrides to edt-ft5406 overlay
+
+Adds the option for the touch controller to be connected to any
+of the I2C ports.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README             | 17 +++++++++++++++-
+ .../boot/dts/overlays/edt-ft5406-overlay.dts  | 20 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi    |  9 ++++++++-
+ 3 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1040,9 +1040,11 @@ Params: dr_mode                 Dual rol
+ 
+ 
+ Name:   edt-ft5406
+-Info:   Overlay for the EDT FT5406 touchscreen on the CSI/DSI I2C interface.
++Info:   Overlay for the EDT FT5406 touchscreen.
+         This works with the Raspberry Pi 7" touchscreen when not being polled
+         by the firmware.
++        By default the overlay uses the i2c_csi_dsi I2C interface, but this
++        can be overridden
+         You MUST use either "disable_touchscreen=1" or "ignore_lcd=1" in
+         config.txt to stop the firmware polling the touchscreen.
+ Load:   dtoverlay=edt-ft5406,<param>=<val>
+@@ -1051,6 +1053,19 @@ Params: sizex                   Touchscr
+         invx                    Touchscreen inverted x axis
+         invy                    Touchscreen inverted y axis
+         swapxy                  Touchscreen swapped x y axis
++        i2c0                    Choose the I2C0 bus on GPIOs 0&1
++        i2c1                    Choose the I2C1 bus on GPIOs 2&3
++        i2c3                    Choose the I2C3 bus (configure with the i2c3
++                                overlay - BCM2711 only)
++        i2c4                    Choose the I2C4 bus (configure with the i2c4
++                                overlay - BCM2711 only)
++        i2c5                    Choose the I2C5 bus (configure with the i2c5
++                                overlay - BCM2711 only)
++        i2c6                    Choose the I2C6 bus (configure with the i2c6
++                                overlay - BCM2711 only)
++        addr                    Sets the address for the touch controller. Note
++                                that the device must be configured to use the
++                                specified address.
+ 
+ 
+ Name:   enc28j60
+--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
+@@ -23,4 +23,24 @@
+ 			status = "okay";
+ 		};
+ 	};
++
++	__overrides__ {
++		i2c0 = <&frag13>,"target:0=",<&i2c0>;
++		i2c1 = <&frag13>, "target?=0",
++		       <&frag13>, "target-path=i2c1",
++		       <0>,"-0-1";
++		i2c3 = <&frag13>, "target?=0",
++		       <&frag13>, "target-path=i2c3",
++		       <0>,"-0-1";
++		i2c4 = <&frag13>, "target?=0",
++		       <&frag13>, "target-path=i2c4",
++		       <0>,"-0-1";
++		i2c5 = <&frag13>, "target?=0",
++		       <&frag13>, "target-path=i2c5",
++		       <0>,"-0-1";
++		i2c6 = <&frag13>, "target?=0",
++		       <&frag13>, "target-path=i2c6",
++		       <0>,"-0-1";
++		addr = <&ft5406>,"reg:0";
++	};
+ };
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -23,7 +23,7 @@
+ 	};
+ 
+ 	fragment@12 {
+-		target = <&i2c_csi_dsi>;
++		target = <&i2cbus>;
+ 		__overlay__ {
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
+@@ -37,6 +37,13 @@
+ 		};
+ 	};
+ 
++	frag13: fragment@13 {
++		target = <&i2c_csi_dsi>;
++		i2cbus: __overlay__ {
++			status = "okay";
++		};
++	};
++
+ 	__overrides__ {
+ 		sizex = <&ft5406>,"touchscreen-size-x:0";
+ 		sizey = <&ft5406>,"touchscreen-size-y:0";

+ 25 - 0
target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch

@@ -0,0 +1,25 @@
+From 9d9586dc0c0deecb90675bd70862fe262f7376ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 14 Jun 2023 14:25:21 +0100
+Subject: [PATCH] dtoverlays: Fix README text for i2c-fan
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1800,10 +1800,10 @@ Params: addr                    Sets the
+         i2c3                    Choose the I2C3 bus (configure with the i2c3
+                                 overlay - BCM2711 only)
+ 
+-        i2c4                    Choose the I2C3 bus (configure with the i2c3
++        i2c4                    Choose the I2C4 bus (configure with the i2c4
+                                 overlay - BCM2711 only)
+ 
+-        i2c5                    Choose the I2C5 bus (configure with the i2c4
++        i2c5                    Choose the I2C5 bus (configure with the i2c5
+                                 overlay - BCM2711 only)
+ 
+         i2c6                    Choose the I2C6 bus (configure with the i2c6

+ 50 - 0
target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch

@@ -0,0 +1,50 @@
+From e804bd1843236a63815e9acfb1a38ebf9a28ef5b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 31 Aug 2023 16:45:44 +0100
+Subject: [PATCH] drivers: irqchip: irq-bcm2835: Concurrency fix
+
+The commit shown in Fixes: aims to improve interrupt throughput by
+getting the handlers invoked on different CPU cores. It does so (*) by
+using an irq_ack hook to change the interrupt routing.
+
+Unfortunately, the IRQ status bits must be cleared at source, which only
+happens once the interrupt handler has run - there is no easy way for
+one core to claim one of the IRQs before sending the remainder to the
+next core on the list, so waking another core immediately results in a
+race with a chance of both cores handling the same IRQ. It is probably
+for this reason that the routing change is deferred to irq_ack, but that
+doesn't guarantee no clashes - after irq_ack is called, control returns
+to bcm2836_chained_handler_irq which proceeds to check for other pending
+IRQs at a time when the next core is probably doing the same thing.
+
+Since the whole point of the original commit is to distribute the IRQ
+handling, there is no reason to attempt to handle multiple IRQs in one
+interrupt callback, so the problem can be solved (or at least made much
+harder to reproduce) by changing a "while" into an "if", so that each
+invocation only handles one IRQ.
+
+(*) I'm not convinced it's as effective as claimed since irq_ack is
+called _after_ the interrupt handler, but the author thought it made a
+difference.
+
+See: https://github.com/raspberrypi/linux/issues/5214
+     https://github.com/raspberrypi/linux/pull/1794
+
+Fixes: fd4c9785bde8 ("ARM64: Round-Robin dispatch IRQs between CPUs.")
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/irqchip/irq-bcm2835.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/irqchip/irq-bcm2835.c
++++ b/drivers/irqchip/irq-bcm2835.c
+@@ -343,7 +343,8 @@ static void bcm2836_chained_handle_irq(s
+ {
+ 	u32 hwirq;
+ 
+-	while ((hwirq = get_next_armctrl_hwirq()) != ~0)
++	hwirq = get_next_armctrl_hwirq();
++	if (hwirq != ~0)
+ 		generic_handle_domain_irq(intc.domain, hwirq);
+ }
+ 

+ 58 - 0
target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch

@@ -0,0 +1,58 @@
+From 5e54398e1b61335883dff1be46a6c8b3ca973926 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 30 Aug 2023 18:03:37 +0100
+Subject: [PATCH] dtoverlays: Add drm option to piscreen overlay
+
+Adds the option of selecting the DRM/KMS TinyDRM driver for
+this panel, rather than the deprecated FBTFT one.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README               |  3 +++
+ arch/arm/boot/dts/overlays/piscreen-overlay.dts | 10 +++++++---
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3245,6 +3245,9 @@ Params: speed                   Display
+ 
+         xohms                   Touchpanel sensitivity (X-plate resistance)
+ 
++        drm                     Select the DRM/KMS driver instead of the FBTFT
++                                one
++
+ 
+ Name:   piscreen2r
+ Info:   PiScreen 2 with resistive TP display by OzzMaker.com
+--- a/arch/arm/boot/dts/overlays/piscreen-overlay.dts
++++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts
+@@ -6,6 +6,8 @@
+ /dts-v1/;
+ /plugin/;
+ 
++#include <dt-bindings/gpio/gpio.h>
++
+ / {
+ 	compatible = "brcm,bcm2835";
+ 
+@@ -59,9 +61,9 @@
+ 				fps = <30>;
+ 				buswidth = <8>;
+ 				regwidth = <16>;
+-				reset-gpios = <&gpio 25 1>;
+-				dc-gpios = <&gpio 24 0>;
+-				led-gpios = <&gpio 22 0>;
++				reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
++				dc-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
++				led-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
+ 				debug = <0>;
+ 
+ 				init = <0x10000b0 0x00
+@@ -98,5 +100,7 @@
+ 		fps =		<&piscreen>,"fps:0";
+ 		debug =		<&piscreen>,"debug:0";
+ 		xohms =		<&piscreen_ts>,"ti,x-plate-ohms;0";
++		drm =		<&piscreen>,"compatible=waveshare,rpi-lcd-35",
++				<&piscreen>,"reset-gpios:8=",<GPIO_ACTIVE_HIGH>;
+ 	};
+ };

+ 36 - 0
target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch

@@ -0,0 +1,36 @@
+From f59fe2d1bd056af117eb512bb0e9210a943c6d47 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Fri, 1 Sep 2023 12:17:38 +0100
+Subject: [PATCH] drm/ili9486: Resolve clash in spi_device_id names
+
+For "Really Good Reasons" [1] the SPI core requires a match
+between compatible device strings and the name in spi_device_id.
+
+The ili9486 driver uses compatible strings "waveshare,rpi-lcd-35"
+and "ozzmaker,piscreen", but "rpi-lcd-35" and "piscreen" are missing,
+so add them.
+
+Compatible string "ilitek,ili9486" is already used by
+staging/fbtft/fb_ili9486, therefore leaving it present in ili9486 as an
+spi_device_id causes the incorrect module to be loaded, therefore remove
+this id.
+
+[1] https://elixir.bootlin.com/linux/latest/source/drivers/spi/spi.c#L487
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/tiny/ili9486.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/tiny/ili9486.c
++++ b/drivers/gpu/drm/tiny/ili9486.c
+@@ -187,7 +187,8 @@ static const struct of_device_id ili9486
+ MODULE_DEVICE_TABLE(of, ili9486_of_match);
+ 
+ static const struct spi_device_id ili9486_id[] = {
+-	{ "ili9486", 0 },
++	{ "rpi-lcd-35", 0 },
++	{ "piscreen", 0 },
+ 	{ }
+ };
+ MODULE_DEVICE_TABLE(spi, ili9486_id);

+ 50 - 0
target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch

@@ -0,0 +1,50 @@
+From 50c5a8558f4aaa54a3c4f5a8c2b6053f641d94eb Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Fri, 1 Sep 2023 12:23:30 +0100
+Subject: [PATCH] input: ads7846: Add missing spi_device_id strings
+
+The SPI core logs error messages if a compatible string device
+name is not also present as an spi_device_id.
+
+No spi_device_id values are specified by the driver, therefore
+we get 4 log lines every time it is loaded:
+SPI driver ads7846 has no spi_device_id for ti,tsc2046
+SPI driver ads7846 has no spi_device_id for ti,ads7843
+SPI driver ads7846 has no spi_device_id for ti,ads7845
+SPI driver ads7846 has no spi_device_id for ti,ads7873
+
+Add the spi_device_id values for these devices.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/input/touchscreen/ads7846.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/input/touchscreen/ads7846.c
++++ b/drivers/input/touchscreen/ads7846.c
+@@ -1127,6 +1127,17 @@ static const struct of_device_id ads7846
+ };
+ MODULE_DEVICE_TABLE(of, ads7846_dt_ids);
+ 
++static const struct spi_device_id ads7846_spi_ids[] = {
++	{ "tsc2046", 0 },
++	{ "ads7843", 0 },
++	{ "ads7843", 0 },
++	{ "ads7845", 0 },
++	{ "ads7846", 0 },
++	{ "ads7873", 0 },
++	{ }
++};
++MODULE_DEVICE_TABLE(spi, ads7846_spi_ids);
++
+ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+ {
+ 	struct ads7846_platform_data *pdata;
+@@ -1424,6 +1435,7 @@ static struct spi_driver ads7846_driver
+ 		.pm	= &ads7846_pm,
+ 		.of_match_table = of_match_ptr(ads7846_dt_ids),
+ 	},
++	.id_table	= ads7846_spi_ids,
+ 	.probe		= ads7846_probe,
+ 	.remove		= ads7846_remove,
+ };

+ 27 - 0
target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch

@@ -0,0 +1,27 @@
+From 65742d7116e89b08858fcd7d67bd521ee19ee837 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 30 Aug 2023 18:05:43 +0100
+Subject: [PATCH] staging: bcm2835-codec: Downgrade the level for a debug
+ message
+
+The debug message from bcm2835_codec_buf_prepare when the buffer
+size is incorrect can be a little spammy if the application isn't
+careful on how it drives it, therefore drop the priority of the
+message.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c    | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -2883,7 +2883,7 @@ static int bcm2835_codec_buf_prepare(str
+ 	}
+ 
+ 	if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+-		v4l2_err(&ctx->dev->v4l2_dev, "%s data will not fit into plane (%lu < %lu)\n",
++		v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s data will not fit into plane (%lu < %lu)\n",
+ 			 __func__, vb2_plane_size(vb, 0),
+ 			 (long)q_data->sizeimage);
+ 		return -EINVAL;

+ 286 - 0
target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch

@@ -0,0 +1,286 @@
+From cee471c3ada3215d6dfc53fb0f1b97548444dea7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 5 Sep 2023 11:56:19 +0100
+Subject: [PATCH] gpio-fsm: Sort functions into a more logical order
+
+Move some functions into a more logical ordering. This change causes
+no functional change and is essentially cosmetic.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/gpio/gpio-fsm.c | 245 ++++++++++++++++++++--------------------
+ 1 file changed, 125 insertions(+), 120 deletions(-)
+
+--- a/drivers/gpio/gpio-fsm.c
++++ b/drivers/gpio/gpio-fsm.c
+@@ -193,131 +193,14 @@ static void free_symbols(struct symtab_e
+ 	}
+ }
+ 
+-static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off)
+-{
+-	struct gpio_fsm *gf = gpiochip_get_data(gc);
+-	struct soft_gpio *sg;
+-
+-	if (off >= gf->num_soft_gpios)
+-		return -EINVAL;
+-	sg = &gf->soft_gpios[off];
+-
+-	return sg->dir;
+-}
+-
+-static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off)
+-{
+-	struct gpio_fsm *gf = gpiochip_get_data(gc);
+-	struct soft_gpio *sg;
+-
+-	if (off >= gf->num_soft_gpios)
+-		return -EINVAL;
+-	sg = &gf->soft_gpios[off];
+-
+-	return sg->value;
+-}
+-
+ static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
+-				   struct fsm_state *new_state)
+-{
+-	struct input_gpio_state *inp_state;
+-	struct gpio_event *gp_ev;
+-	struct fsm_state *state;
+-	int i;
+-
+-	dev_dbg(gf->dev, "go_to_state(%s)\n",
+-		  new_state ? new_state->name : "<unset>");
+-
+-	spin_lock(&gf->spinlock);
+-
+-	if (gf->next_state) {
+-		/* Something else has already requested a transition */
+-		spin_unlock(&gf->spinlock);
+-		return;
+-	}
+-
+-	gf->next_state = new_state;
+-	state = gf->current_state;
+-	gf->delay_target_state = NULL;
+-
+-	if (state) {
+-		/* Disarm any GPIO IRQs */
+-		for (i = 0; i < state->num_gpio_events; i++) {
+-			gp_ev = &state->gpio_events[i];
+-			inp_state = &gf->input_gpio_states[gp_ev->index];
+-			inp_state->target = NULL;
+-		}
+-	}
+-
+-	spin_unlock(&gf->spinlock);
+-
+-	if (new_state)
+-		schedule_work(&gf->work);
+-}
++				 struct fsm_state *new_state);
+ 
+ static void gpio_fsm_set_soft(struct gpio_fsm *gf,
+-				unsigned int off, int val)
+-{
+-	struct soft_gpio *sg = &gf->soft_gpios[off];
+-	struct gpio_event *gp_ev;
+-	struct fsm_state *state;
+-	int i;
+-
+-	dev_dbg(gf->dev, "set(%d,%d)\n", off, val);
+-	state = gf->current_state;
+-	sg->value = val;
+-	for (i = 0; i < state->num_soft_events; i++) {
+-		gp_ev = &state->soft_events[i];
+-		if (gp_ev->index == off && gp_ev->value == val) {
+-			if (gf->debug)
+-				dev_info(gf->dev,
+-					 "GF_SOFT %d->%d -> %s\n", gp_ev->index,
+-					 gp_ev->value, gp_ev->target->name);
+-			gpio_fsm_go_to_state(gf, gp_ev->target);
+-			break;
+-		}
+-	}
+-}
+-
+-static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off)
+-{
+-	struct gpio_fsm *gf = gpiochip_get_data(gc);
+-	struct soft_gpio *sg;
+-
+-	if (off >= gf->num_soft_gpios)
+-		return -EINVAL;
+-	sg = &gf->soft_gpios[off];
+-	sg->dir = GPIOF_DIR_IN;
+-
+-	return 0;
+-}
+-
+-static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off,
+-				       int value)
+-{
+-	struct gpio_fsm *gf = gpiochip_get_data(gc);
+-	struct soft_gpio *sg;
+-
+-	if (off >= gf->num_soft_gpios)
+-		return -EINVAL;
+-	sg = &gf->soft_gpios[off];
+-	sg->dir = GPIOF_DIR_OUT;
+-	gpio_fsm_set_soft(gf, off, value);
+-
+-	return 0;
+-}
+-
+-static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val)
+-{
+-	struct gpio_fsm *gf;
+-
+-	gf = gpiochip_get_data(gc);
+-	if (off < gf->num_soft_gpios)
+-		gpio_fsm_set_soft(gf, off, val);
+-}
++			      unsigned int off, int val);
+ 
+ static void gpio_fsm_enter_state(struct gpio_fsm *gf,
+-				   struct fsm_state *state)
++				 struct fsm_state *state)
+ {
+ 	struct input_gpio_state *inp_state;
+ 	struct output_signal *signal;
+@@ -431,6 +314,44 @@ static void gpio_fsm_enter_state(struct
+ 	}
+ }
+ 
++static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
++				 struct fsm_state *new_state)
++{
++	struct input_gpio_state *inp_state;
++	struct gpio_event *gp_ev;
++	struct fsm_state *state;
++	int i;
++
++	dev_dbg(gf->dev, "go_to_state(%s)\n",
++		  new_state ? new_state->name : "<unset>");
++
++	spin_lock(&gf->spinlock);
++
++	if (gf->next_state) {
++		/* Something else has already requested a transition */
++		spin_unlock(&gf->spinlock);
++		return;
++	}
++
++	gf->next_state = new_state;
++	state = gf->current_state;
++	gf->delay_target_state = NULL;
++
++	if (state) {
++		/* Disarm any GPIO IRQs */
++		for (i = 0; i < state->num_gpio_events; i++) {
++			gp_ev = &state->gpio_events[i];
++			inp_state = &gf->input_gpio_states[gp_ev->index];
++			inp_state->target = NULL;
++		}
++	}
++
++	spin_unlock(&gf->spinlock);
++
++	if (new_state)
++		schedule_work(&gf->work);
++}
++
+ static void gpio_fsm_work(struct work_struct *work)
+ {
+ 	struct input_gpio_state *inp_state;
+@@ -851,6 +772,90 @@ static int resolve_sym_to_state(struct g
+ 	return 0;
+ }
+ 
++static void gpio_fsm_set_soft(struct gpio_fsm *gf,
++			      unsigned int off, int val)
++{
++	struct soft_gpio *sg = &gf->soft_gpios[off];
++	struct gpio_event *gp_ev;
++	struct fsm_state *state;
++	int i;
++
++	dev_dbg(gf->dev, "set(%d,%d)\n", off, val);
++	state = gf->current_state;
++	sg->value = val;
++	for (i = 0; i < state->num_soft_events; i++) {
++		gp_ev = &state->soft_events[i];
++		if (gp_ev->index == off && gp_ev->value == val) {
++			if (gf->debug)
++				dev_info(gf->dev,
++					 "GF_SOFT %d->%d -> %s\n", gp_ev->index,
++					 gp_ev->value, gp_ev->target->name);
++			gpio_fsm_go_to_state(gf, gp_ev->target);
++			break;
++		}
++	}
++}
++
++static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off)
++{
++	struct gpio_fsm *gf = gpiochip_get_data(gc);
++	struct soft_gpio *sg;
++
++	if (off >= gf->num_soft_gpios)
++		return -EINVAL;
++	sg = &gf->soft_gpios[off];
++
++	return sg->value;
++}
++
++static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val)
++{
++	struct gpio_fsm *gf;
++
++	gf = gpiochip_get_data(gc);
++	if (off < gf->num_soft_gpios)
++		gpio_fsm_set_soft(gf, off, val);
++}
++
++static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off)
++{
++	struct gpio_fsm *gf = gpiochip_get_data(gc);
++	struct soft_gpio *sg;
++
++	if (off >= gf->num_soft_gpios)
++		return -EINVAL;
++	sg = &gf->soft_gpios[off];
++
++	return sg->dir;
++}
++
++static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off)
++{
++	struct gpio_fsm *gf = gpiochip_get_data(gc);
++	struct soft_gpio *sg;
++
++	if (off >= gf->num_soft_gpios)
++		return -EINVAL;
++	sg = &gf->soft_gpios[off];
++	sg->dir = GPIOF_DIR_IN;
++
++	return 0;
++}
++
++static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off,
++				       int value)
++{
++	struct gpio_fsm *gf = gpiochip_get_data(gc);
++	struct soft_gpio *sg;
++
++	if (off >= gf->num_soft_gpios)
++		return -EINVAL;
++	sg = &gf->soft_gpios[off];
++	sg->dir = GPIOF_DIR_OUT;
++	gpio_fsm_set_soft(gf, off, value);
++
++	return 0;
++}
+ 
+ /*
+  * /sys/class/gpio-fsm/<fsm-name>/

+ 192 - 0
target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch

@@ -0,0 +1,192 @@
+From f0061ffc98c6e027c5774e2a24ceadcfee4167ea Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 5 Sep 2023 12:01:13 +0100
+Subject: [PATCH] gpio_fsm: Rework the atomic-vs-non-atomic split
+
+Partition the code to separate atomic and non-atomic methods so that
+none of them have to handle both cases. The result avoids using deferred
+work unless necessary, and should be easier to understand.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/gpio/gpio-fsm.c | 84 ++++++++++++++++++++---------------------
+ 1 file changed, 41 insertions(+), 43 deletions(-)
+
+--- a/drivers/gpio/gpio-fsm.c
++++ b/drivers/gpio/gpio-fsm.c
+@@ -193,9 +193,6 @@ static void free_symbols(struct symtab_e
+ 	}
+ }
+ 
+-static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
+-				 struct fsm_state *new_state);
+-
+ static void gpio_fsm_set_soft(struct gpio_fsm *gf,
+ 			      unsigned int off, int val);
+ 
+@@ -213,6 +210,7 @@ static void gpio_fsm_enter_state(struct
+ 	dev_dbg(gf->dev, "enter_state(%s)\n", state->name);
+ 
+ 	gf->current_state = state;
++	gf->delay_target_state = NULL;
+ 
+ 	// 1. Apply any listed signals
+ 	for (i = 0; i < state->num_signals; i++) {
+@@ -271,7 +269,7 @@ static void gpio_fsm_enter_state(struct
+ 				dev_info(gf->dev,
+ 					 "GF_SOFT %d=%d -> %s\n", event->index,
+ 					 event->value, event->target->name);
+-			gpio_fsm_go_to_state(gf, event->target);
++			gpio_fsm_enter_state(gf, event->target);
+ 			return;
+ 		}
+ 	}
+@@ -284,7 +282,7 @@ static void gpio_fsm_enter_state(struct
+ 		inp_state->value = event->value;
+ 		inp_state->enabled = true;
+ 
+-		value = gpiod_get_value(gf->input_gpios->desc[event->index]);
++		value = gpiod_get_value_cansleep(gf->input_gpios->desc[event->index]);
+ 
+ 		// Clear stale event state
+ 		disable_irq(inp_state->irq);
+@@ -299,7 +297,7 @@ static void gpio_fsm_enter_state(struct
+ 				dev_info(gf->dev,
+ 					 "GF_IN %d=%d -> %s\n", event->index,
+ 					 event->value, event->target->name);
+-			gpio_fsm_go_to_state(gf, event->target);
++			gpio_fsm_enter_state(gf, event->target);
+ 			return;
+ 		}
+ 	}
+@@ -325,6 +323,33 @@ static void gpio_fsm_go_to_state(struct
+ 	dev_dbg(gf->dev, "go_to_state(%s)\n",
+ 		  new_state ? new_state->name : "<unset>");
+ 
++	state = gf->current_state;
++
++	/* Disable any enabled GPIO IRQs */
++	for (i = 0; i < state->num_gpio_events; i++) {
++		gp_ev = &state->gpio_events[i];
++		inp_state = &gf->input_gpio_states[gp_ev->index];
++		if (inp_state->enabled) {
++			inp_state->enabled = false;
++			irq_set_irq_type(inp_state->irq,
++					 IRQF_TRIGGER_NONE);
++		}
++	}
++
++	gpio_fsm_enter_state(gf, new_state);
++}
++
++static void gpio_fsm_go_to_state_deferred(struct gpio_fsm *gf,
++					  struct fsm_state *new_state)
++{
++	struct input_gpio_state *inp_state;
++	struct gpio_event *gp_ev;
++	struct fsm_state *state;
++	int i;
++
++	dev_dbg(gf->dev, "go_to_state_deferred(%s)\n",
++		  new_state ? new_state->name : "<unset>");
++
+ 	spin_lock(&gf->spinlock);
+ 
+ 	if (gf->next_state) {
+@@ -335,57 +360,31 @@ static void gpio_fsm_go_to_state(struct
+ 
+ 	gf->next_state = new_state;
+ 	state = gf->current_state;
+-	gf->delay_target_state = NULL;
+ 
+-	if (state) {
+-		/* Disarm any GPIO IRQs */
+-		for (i = 0; i < state->num_gpio_events; i++) {
+-			gp_ev = &state->gpio_events[i];
+-			inp_state = &gf->input_gpio_states[gp_ev->index];
+-			inp_state->target = NULL;
+-		}
++	/* Disarm any GPIO IRQs */
++	for (i = 0; i < state->num_gpio_events; i++) {
++		gp_ev = &state->gpio_events[i];
++		inp_state = &gf->input_gpio_states[gp_ev->index];
++		inp_state->target = NULL;
+ 	}
+ 
+ 	spin_unlock(&gf->spinlock);
+ 
+-	if (new_state)
+-		schedule_work(&gf->work);
++	schedule_work(&gf->work);
+ }
+ 
+ static void gpio_fsm_work(struct work_struct *work)
+ {
+-	struct input_gpio_state *inp_state;
+ 	struct fsm_state *new_state;
+-	struct fsm_state *state;
+-	struct gpio_event *gp_ev;
+ 	struct gpio_fsm *gf;
+-	int i;
+ 
+ 	gf = container_of(work, struct gpio_fsm, work);
+ 	spin_lock(&gf->spinlock);
+-	state = gf->current_state;
+ 	new_state = gf->next_state;
+-	if (!new_state)
+-		new_state = gf->delay_target_state;
+ 	gf->next_state = NULL;
+-	gf->delay_target_state = NULL;
+ 	spin_unlock(&gf->spinlock);
+ 
+-	if (state) {
+-		/* Disable any enabled GPIO IRQs */
+-		for (i = 0; i < state->num_gpio_events; i++) {
+-			gp_ev = &state->gpio_events[i];
+-			inp_state = &gf->input_gpio_states[gp_ev->index];
+-			if (inp_state->enabled) {
+-				inp_state->enabled = false;
+-				irq_set_irq_type(inp_state->irq,
+-						 IRQF_TRIGGER_NONE);
+-			}
+-		}
+-	}
+-
+-	if (new_state)
+-		gpio_fsm_enter_state(gf, new_state);
++	gpio_fsm_go_to_state(gf, new_state);
+ }
+ 
+ static irqreturn_t gpio_fsm_gpio_irq_handler(int irq, void *dev_id)
+@@ -404,7 +403,7 @@ static irqreturn_t gpio_fsm_gpio_irq_han
+ 	if (gf->debug)
+ 		dev_info(gf->dev, "GF_IN %d->%d -> %s\n",
+ 			 inp_state->index, inp_state->value, target->name);
+-	gpio_fsm_go_to_state(gf, target);
++	gpio_fsm_go_to_state_deferred(gf, target);
+ 	return IRQ_HANDLED;
+ }
+ 
+@@ -416,12 +415,11 @@ static void gpio_fsm_timer(struct timer_
+ 	target = gf->delay_target_state;
+ 	if (!target)
+ 		return;
+-
+ 	if (gf->debug)
+ 		dev_info(gf->dev, "GF_DELAY %d -> %s\n", gf->delay_ms,
+ 			 target->name);
+ 
+-	gpio_fsm_go_to_state(gf, target);
++	gpio_fsm_go_to_state_deferred(gf, target);
+ }
+ 
+ int gpio_fsm_parse_signals(struct gpio_fsm *gf, struct fsm_state *state,
+@@ -1119,7 +1117,7 @@ static int gpio_fsm_probe(struct platfor
+ 	if (gf->debug)
+ 		dev_info(gf->dev, "Start -> %s\n", gf->start_state->name);
+ 
+-	gpio_fsm_go_to_state(gf, gf->start_state);
++	gpio_fsm_enter_state(gf, gf->start_state);
+ 
+ 	return devm_gpiochip_add_data(dev, &gf->gc, gf);
+ }

+ 63 - 0
target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch

@@ -0,0 +1,63 @@
+From bf9fb25f3265605572f04e5c7836bb83ee345236 Mon Sep 17 00:00:00 2001
+From: Chao Yu <[email protected]>
+Date: Fri, 30 Dec 2022 23:43:32 +0800
+Subject: [PATCH] f2fs: fix to avoid NULL pointer dereference in
+ f2fs_issue_flush()
+
+commit b3d83066cbebc76dbac8a5fca931f64b4c6fff34 upstream.
+
+With below two cases, it will cause NULL pointer dereference when
+accessing SM_I(sbi)->fcc_info in f2fs_issue_flush().
+
+a) If kthread_run() fails in f2fs_create_flush_cmd_control(), it will
+release SM_I(sbi)->fcc_info,
+
+- mount -o noflush_merge /dev/vda /mnt/f2fs
+- mount -o remount,flush_merge /dev/vda /mnt/f2fs  -- kthread_run() fails
+- dd if=/dev/zero of=/mnt/f2fs/file bs=4k count=1 conv=fsync
+
+b) we will never allocate memory for SM_I(sbi)->fcc_info w/ below
+testcase,
+
+- mount -o ro /dev/vda /mnt/f2fs
+- mount -o rw,remount /dev/vda /mnt/f2fs
+- dd if=/dev/zero of=/mnt/f2fs/file bs=4k count=1 conv=fsync
+
+In order to fix this issue, let change as below:
+- fix error path handling in f2fs_create_flush_cmd_control().
+- allocate SM_I(sbi)->fcc_info even if readonly is on.
+
+Signed-off-by: Chao Yu <[email protected]>
+Signed-off-by: Jaegeuk Kim <[email protected]>
+---
+ fs/f2fs/segment.c | 12 ++++--------
+ 1 file changed, 4 insertions(+), 8 deletions(-)
+
+--- a/fs/f2fs/segment.c
++++ b/fs/f2fs/segment.c
+@@ -663,9 +663,7 @@ init_thread:
+ 				"f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));
+ 	if (IS_ERR(fcc->f2fs_issue_flush)) {
+ 		err = PTR_ERR(fcc->f2fs_issue_flush);
+-		kfree(fcc);
+-		SM_I(sbi)->fcc_info = NULL;
+-		return err;
++		fcc->f2fs_issue_flush = NULL;
+ 	}
+ 
+ 	return err;
+@@ -5062,11 +5060,9 @@ int f2fs_build_segment_manager(struct f2
+ 
+ 	init_f2fs_rwsem(&sm_info->curseg_lock);
+ 
+-	if (!f2fs_readonly(sbi->sb)) {
+-		err = f2fs_create_flush_cmd_control(sbi);
+-		if (err)
+-			return err;
+-	}
++	err = f2fs_create_flush_cmd_control(sbi);
++	if (err)
++		return err;
+ 
+ 	err = create_discard_cmd_control(sbi);
+ 	if (err)

+ 15 - 7
target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch → target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch

@@ -1,4 +1,4 @@
-From b642f64d629df5515f3a01fc5b2e17c3fa7b404c Mon Sep 17 00:00:00 2001
+From e079555a4c68356e58249cfc041b28f6eb455bd5 Mon Sep 17 00:00:00 2001
 From: Stefan Wahren <[email protected]>
 Date: Sat, 4 May 2019 17:06:15 +0200
 Subject: [PATCH] hwrng: iproc-rng200: Add BCM2838 support
@@ -17,8 +17,8 @@ Fixes: "hwrng: iproc-rng200: Add BCM2838 support"
 Signed-off-by: Phil Elwell <[email protected]>
 ---
  drivers/char/hw_random/Kconfig        |  2 +-
- drivers/char/hw_random/iproc-rng200.c | 78 +++++++++++++++++++++++++--
- 2 files changed, 76 insertions(+), 4 deletions(-)
+ drivers/char/hw_random/iproc-rng200.c | 79 ++++++++++++++++++++++++++-
+ 2 files changed, 77 insertions(+), 4 deletions(-)
 
 --- a/drivers/char/hw_random/Kconfig
 +++ b/drivers/char/hw_random/Kconfig
@@ -33,7 +33,15 @@ Signed-off-by: Phil Elwell <[email protected]>
  	  module will be called iproc-rng200
 --- a/drivers/char/hw_random/iproc-rng200.c
 +++ b/drivers/char/hw_random/iproc-rng200.c
-@@ -21,6 +21,7 @@
+@@ -14,6 +14,7 @@
+ #include <linux/module.h>
+ #include <linux/of_address.h>
+ #include <linux/of_platform.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/delay.h>
+ 
+@@ -21,6 +22,7 @@
  #define RNG_CTRL_OFFSET					0x00
  #define RNG_CTRL_RNG_RBGEN_MASK				0x00001FFF
  #define RNG_CTRL_RNG_RBGEN_ENABLE			0x00000001
@@ -41,7 +49,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  
  #define RNG_SOFT_RESET_OFFSET				0x04
  #define RNG_SOFT_RESET					0x00000001
-@@ -28,16 +29,23 @@
+@@ -28,16 +30,23 @@
  #define RBG_SOFT_RESET_OFFSET				0x08
  #define RBG_SOFT_RESET					0x00000001
  
@@ -65,7 +73,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  
  struct iproc_rng200_dev {
  	struct hwrng rng;
-@@ -158,6 +166,64 @@ static int iproc_rng200_init(struct hwrn
+@@ -158,6 +167,64 @@ static int iproc_rng200_init(struct hwrn
  	return 0;
  }
  
@@ -130,7 +138,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  static void iproc_rng200_cleanup(struct hwrng *rng)
  {
  	struct iproc_rng200_dev *priv = to_rng_priv(rng);
-@@ -184,11 +250,17 @@ static int iproc_rng200_probe(struct pla
+@@ -184,11 +251,17 @@ static int iproc_rng200_probe(struct pla
  
  	dev_set_drvdata(dev, priv);
  

+ 39 - 0
target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch

@@ -0,0 +1,39 @@
+From 6f634d7efb8876e5953c30c0a613aaa5f575fe05 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <[email protected]>
+Date: Tue, 11 Oct 2022 14:42:07 -0400
+Subject: [PATCH] PCI: brcmstb: Wait for 100ms following PERST# deassert
+
+commit 3ae140ad827b359bc4fa7c7985691c4c1e3ca8f4 upstream.
+
+Be prudent and give some time for power and clocks to become stable.  As
+described in the PCIe CEM specification sections 2.2 and 2.2.1; as well as
+PCIe r5.0, 6.6.1.
+
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jim Quinlan <[email protected]>
+Signed-off-by: Lorenzo Pieralisi <[email protected]>
+Acked-by: Florian Fainelli <[email protected]>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -1038,8 +1038,15 @@ static int brcm_pcie_start_link(struct b
+ 	pcie->perst_set(pcie, 0);
+ 
+ 	/*
+-	 * Give the RC/EP time to wake up, before trying to configure RC.
+-	 * Intermittently check status for link-up, up to a total of 100ms.
++	 * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
++	 * sections 2.2, PCIe r5.0, 6.6.1.
++	 */
++	msleep(100);
++
++	/*
++	 * Give the RC/EP even more time to wake up, before trying to
++	 * configure RC.  Intermittently check status for link-up, up to a
++	 * total of 100ms.
+ 	 */
+ 	for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
+ 		msleep(5);

+ 47 - 0
target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch

@@ -0,0 +1,47 @@
+From cc08810f89e52337a99cc6ae5f53f08588357c5f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 19 Sep 2023 20:31:34 +0100
+Subject: [PATCH] overlays: Add a sample hat_map
+
+The HAT map is way of associating named overlays with HATs whose
+EEPROMs were programmed with the contents of the overlay.
+Unfortunately, change in the DT and kernel drivers has meant that some
+of these embedded overlays no longer function, or even don't apply.
+
+The HAT map is a mapping from HAT UUIDs to overlay names. If a HAT with
+a listed UUID is detected, the embedded overlay is ignored and the
+overlay named in the mapping is loaded in its place.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile    |  2 +-
+ arch/arm/boot/dts/overlays/hat_map.dts | 13 +++++++++++++
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/overlays/hat_map.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -1,6 +1,6 @@
+ # Overlays for the Raspberry Pi platform
+ 
+-dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
++dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb hat_map.dtb
+ 
+ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	act-led.dtbo \
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -0,0 +1,13 @@
++/dts-v1/;
++
++/ {
++	iqaudio-pi-codecplus {
++		uuid = [ dc1c9594 c1ab 4c6c acda a88dc59a3c5b ];
++		overlay = "iqaudio-codec";
++	};
++
++	recalbox-rgbdual {
++		uuid = [ 1c955808 681f 4bbc a2ef b7ea47cd388e ];
++		overlay = "recalboxrgbdual";
++	};
++};

+ 26 - 0
target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch

@@ -0,0 +1,26 @@
+From 406e7dc82be6ce1b81c88b418640daeef6c2be42 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Mon, 23 May 2022 16:56:44 +0100
+Subject: [PATCH] Revert "usb: phy: generic: Get the vbus supply"
+
+This reverts commit c0ea202fbc855d60bc4a0603ca52a9e80654b327.
+---
+ drivers/usb/phy/phy-generic.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/usb/phy/phy-generic.c
++++ b/drivers/usb/phy/phy-generic.c
+@@ -265,13 +265,6 @@ int usb_phy_gen_create_phy(struct device
+ 			return -EPROBE_DEFER;
+ 	}
+ 
+-	nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
+-	if (PTR_ERR(nop->vbus_draw) == -ENODEV)
+-		nop->vbus_draw = NULL;
+-	if (IS_ERR(nop->vbus_draw))
+-		return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
+-				     "could not get vbus regulator\n");
+-
+ 	nop->dev		= dev;
+ 	nop->phy.dev		= nop->dev;
+ 	nop->phy.label		= "nop-xceiv";

+ 7672 - 0
target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch

@@ -0,0 +1,7672 @@
+From 1196bf1a7736ff0ab79f5012fa84082e298031a7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Tue, 19 Sep 2023 15:55:00 +0100
+Subject: [PATCH] dts: 2712: Update for device tree
+
+dtoverlays: Fix up edt5406 entries to match with vc4-kms-dsi-7inch
+
+vc4-kms-dsi-7inch expects the touch fragment to be named ts_i2c_frag,
+but edt5406 didn't do this.
+
+Fixes: 736d601fb38c ("dts: 2712: Update for device tree")
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/Makefile                    |    3 +-
+ arch/arm/boot/dts/bcm2708-rpi-b-plus.dts      |    3 +
+ arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts      |    3 +
+ arch/arm/boot/dts/bcm2708-rpi-b.dts           |    3 +
+ arch/arm/boot/dts/bcm2708-rpi-cm.dts          |    3 +
+ arch/arm/boot/dts/bcm2708-rpi-zero-w.dts      |    1 +
+ arch/arm/boot/dts/bcm2708-rpi-zero.dts        |    1 +
+ arch/arm/boot/dts/bcm2709-rpi-2-b.dts         |    3 +
+ arch/arm/boot/dts/bcm2709-rpi-cm2.dts         |    3 +
+ arch/arm/boot/dts/bcm270x-rpi.dtsi            |    3 +
+ arch/arm/boot/dts/bcm2710-rpi-2-b.dts         |    3 +
+ arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts    |    3 +
+ arch/arm/boot/dts/bcm2710-rpi-3-b.dts         |    3 +
+ arch/arm/boot/dts/bcm2710-rpi-cm3.dts         |    3 +
+ arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts    |    3 +
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts         |    3 +
+ arch/arm/boot/dts/bcm2711-rpi-cm4.dts         |    3 +
+ arch/arm/boot/dts/bcm2711-rpi-cm4s.dts        |    3 +
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts         |  824 +++++++++++
+ arch/arm/boot/dts/bcm2712-rpi.dtsi            |  281 ++++
+ arch/arm/boot/dts/bcm2712.dtsi                | 1287 +++++++++++++++++
+ arch/arm/boot/dts/overlays/Makefile           |   23 +
+ arch/arm/boot/dts/overlays/README             |  360 ++++-
+ .../dts/overlays/adau1977-adc-overlay.dts     |    4 +-
+ .../dts/overlays/adau7002-simple-overlay.dts  |    4 +-
+ .../overlays/akkordion-iqdacplus-overlay.dts  |    4 +-
+ .../allo-boss-dac-pcm512x-audio-overlay.dts   |   10 +-
+ .../overlays/allo-boss2-dac-audio-overlay.dts |    2 +-
+ .../dts/overlays/allo-digione-overlay.dts     |    4 +-
+ .../allo-katana-dac-audio-overlay.dts         |    2 +-
+ .../allo-piano-dac-pcm512x-audio-overlay.dts  |    4 +-
+ ...o-piano-dac-plus-pcm512x-audio-overlay.dts |    4 +-
+ .../boot/dts/overlays/applepi-dac-overlay.dts |    4 +-
+ .../dts/overlays/arducam-64mp-overlay.dts     |    2 +-
+ .../overlays/arducam-pivariety-overlay.dts    |    2 +-
+ .../overlays/audioinjector-addons-overlay.dts |    4 +-
+ .../audioinjector-bare-i2s-overlay.dts        |    6 +-
+ ...dioinjector-isolated-soundcard-overlay.dts |    4 +-
+ .../overlays/audioinjector-ultra-overlay.dts  |    6 +-
+ .../audioinjector-wm8731-audio-overlay.dts    |    4 +-
+ .../dts/overlays/audiosense-pi-overlay.dts    |    4 +-
+ .../boot/dts/overlays/chipdip-dac-overlay.dts |    4 +-
+ .../dts/overlays/cirrus-wm5102-overlay.dts    |    4 +-
+ .../boot/dts/overlays/dacberry400-overlay.dts |    4 +-
+ .../dts/overlays/dionaudio-kiwi-overlay.dts   |    4 +-
+ .../dts/overlays/dionaudio-loco-overlay.dts   |    4 +-
+ .../overlays/dionaudio-loco-v2-overlay.dts    |    4 +-
+ .../dts/overlays/disable-bt-pi5-overlay.dts   |   17 +
+ .../dts/overlays/disable-wifi-pi5-overlay.dts |   13 +
+ arch/arm/boot/dts/overlays/draws-overlay.dts  |    6 +-
+ .../boot/dts/overlays/edt-ft5406-overlay.dts  |   22 +-
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi    |    2 +-
+ .../boot/dts/overlays/fe-pi-audio-overlay.dts |    4 +-
+ .../boot/dts/overlays/ghost-amp-overlay.dts   |    4 +-
+ .../googlevoicehat-soundcard-overlay.dts      |    4 +-
+ .../dts/overlays/hifiberry-amp-overlay.dts    |    4 +-
+ .../dts/overlays/hifiberry-amp100-overlay.dts |   11 +-
+ .../dts/overlays/hifiberry-amp3-overlay.dts   |    4 +-
+ .../dts/overlays/hifiberry-dac-overlay.dts    |    4 +-
+ .../overlays/hifiberry-dacplus-overlay.dts    |   11 +-
+ .../overlays/hifiberry-dacplusadc-overlay.dts |   10 +-
+ .../hifiberry-dacplusadcpro-overlay.dts       |   10 +-
+ .../overlays/hifiberry-dacplusdsp-overlay.dts |    4 +-
+ .../overlays/hifiberry-dacplushd-overlay.dts  |    4 +-
+ .../dts/overlays/hifiberry-digi-overlay.dts   |    4 +-
+ .../overlays/hifiberry-digi-pro-overlay.dts   |    4 +-
+ .../boot/dts/overlays/i-sabre-q2m-overlay.dts |    4 +-
+ .../boot/dts/overlays/i2c0-pi5-overlay.dts    |   34 +
+ .../boot/dts/overlays/i2c1-pi5-overlay.dts    |   34 +
+ .../boot/dts/overlays/i2c2-pi5-overlay.dts    |   21 +
+ .../boot/dts/overlays/i2c3-pi5-overlay.dts    |   22 +
+ .../arm/boot/dts/overlays/i2s-dac-overlay.dts |    4 +-
+ arch/arm/boot/dts/overlays/imx219-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/imx258-overlay.dts |    2 +-
+ .../boot/dts/overlays/imx290_327-overlay.dtsi |    2 +-
+ arch/arm/boot/dts/overlays/imx296-overlay.dts |    2 +-
+ .../boot/dts/overlays/imx477_378-overlay.dtsi |    2 +-
+ arch/arm/boot/dts/overlays/imx519-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/imx708-overlay.dts |    4 +-
+ .../dts/overlays/iqaudio-codec-overlay.dts    |    4 +-
+ .../boot/dts/overlays/iqaudio-dac-overlay.dts |    4 +-
+ .../dts/overlays/iqaudio-dacplus-overlay.dts  |    4 +-
+ .../iqaudio-digi-wm8804-audio-overlay.dts     |    4 +-
+ .../arm/boot/dts/overlays/irs1125-overlay.dts |    2 +-
+ .../dts/overlays/justboom-both-overlay.dts    |    4 +-
+ .../dts/overlays/justboom-dac-overlay.dts     |    4 +-
+ .../dts/overlays/justboom-digi-overlay.dts    |    4 +-
+ .../boot/dts/overlays/max98357a-overlay.dts   |    6 +-
+ .../boot/dts/overlays/mbed-dac-overlay.dts    |    6 +-
+ .../boot/dts/overlays/merus-amp-overlay.dts   |    4 +-
+ .../dts/overlays/midi-uart0-pi5-overlay.dts   |   35 +
+ .../dts/overlays/midi-uart1-pi5-overlay.dts   |   35 +
+ .../dts/overlays/midi-uart2-pi5-overlay.dts   |   35 +
+ .../dts/overlays/midi-uart3-pi5-overlay.dts   |   35 +
+ .../dts/overlays/midi-uart4-pi5-overlay.dts   |   35 +
+ arch/arm/boot/dts/overlays/ov2311-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/ov7251-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/ov9281-overlay.dts |    2 +-
+ arch/arm/boot/dts/overlays/overlay_map.dts    |  226 +++
+ arch/arm/boot/dts/overlays/pibell-overlay.dts |    6 +-
+ .../arm/boot/dts/overlays/pifi-40-overlay.dts |    4 +-
+ .../boot/dts/overlays/pifi-dac-hd-overlay.dts |    4 +-
+ .../dts/overlays/pifi-dac-zero-overlay.dts    |    4 +-
+ .../dts/overlays/pifi-mini-210-overlay.dts    |    4 +-
+ .../arm/boot/dts/overlays/pisound-overlay.dts |    4 +-
+ .../boot/dts/overlays/proto-codec-overlay.dts |    4 +-
+ .../rra-digidac1-wm8741-audio-overlay.dts     |    4 +-
+ .../dts/overlays/spi2-1cs-pi5-overlay.dts     |   33 +
+ .../dts/overlays/spi2-2cs-pi5-overlay.dts     |   44 +
+ .../dts/overlays/spi3-1cs-pi5-overlay.dts     |   33 +
+ .../dts/overlays/spi3-2cs-pi5-overlay.dts     |   44 +
+ .../dts/overlays/spi5-1cs-pi5-overlay.dts     |   33 +
+ .../dts/overlays/spi5-2cs-pi5-overlay.dts     |   44 +
+ .../dts/overlays/superaudioboard-overlay.dts  |    6 +-
+ .../dts/overlays/tc358743-audio-overlay.dts   |   10 +-
+ .../boot/dts/overlays/tc358743-overlay.dts    |    2 +-
+ .../boot/dts/overlays/uart0-pi5-overlay.dts   |   17 +
+ .../boot/dts/overlays/uart1-pi5-overlay.dts   |   17 +
+ .../boot/dts/overlays/uart2-pi5-overlay.dts   |   17 +
+ .../boot/dts/overlays/uart3-pi5-overlay.dts   |   17 +
+ .../boot/dts/overlays/uart4-pi5-overlay.dts   |   17 +
+ arch/arm/boot/dts/overlays/udrc-overlay.dts   |    6 +-
+ .../dts/overlays/ugreen-dabboard-overlay.dts  |   10 +-
+ .../dts/overlays/vc4-fkms-v3d-overlay.dts     |    6 +
+ .../dts/overlays/vc4-fkms-v3d-pi4-overlay.dts |    6 +
+ .../overlays/vc4-kms-dsi-7inch-overlay.dts    |   18 +-
+ .../vc4-kms-dsi-waveshare-panel-overlay.dts   |    8 +-
+ .../dts/overlays/vc4-kms-v3d-pi5-overlay.dts  |  147 ++
+ .../dts/overlays/vc4-kms-vga666-overlay.dts   |    9 +-
+ .../dts/overlays/wm8960-soundcard-overlay.dts |    4 +-
+ arch/arm/boot/dts/rp1.dtsi                    | 1168 +++++++++++++++
+ arch/arm64/boot/dts/broadcom/Makefile         |    1 +
+ .../boot/dts/broadcom/bcm2712-rpi-5-b.dts     |    1 +
+ 134 files changed, 5143 insertions(+), 264 deletions(-)
+ create mode 100644 arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+ create mode 100644 arch/arm/boot/dts/bcm2712-rpi.dtsi
+ create mode 100644 arch/arm/boot/dts/bcm2712.dtsi
+ create mode 100644 arch/arm/boot/dts/overlays/disable-bt-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/disable-wifi-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c0-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c1-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c2-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c3-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart0-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart1-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart2-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart3-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart4-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi2-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi2-2cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi3-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi3-2cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi5-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi5-2cs-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/rp1.dtsi
+ create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -18,7 +18,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \
+ 	bcm2709-rpi-cm2.dtb \
+ 	bcm2710-rpi-cm3.dtb \
+ 	bcm2711-rpi-cm4.dtb \
+-	bcm2711-rpi-cm4s.dtb
++	bcm2711-rpi-cm4s.dtb \
++	bcm2712-rpi-5-b.dtb
+ 
+ dtb-$(CONFIG_ARCH_ALPINE) += \
+ 	alpine-db.dtb
+--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
+@@ -192,6 +192,9 @@ i2c_arm: &i2c1 {
+ i2c_vc: &i2c0 {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
+@@ -203,6 +203,9 @@ i2c_arm: &i2c0 {
+ i2c_vc: &i2c1 {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts
+@@ -185,6 +185,9 @@ i2c_arm: &i2c1 {
+ i2c_vc: &i2c0 {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts
+@@ -19,6 +19,9 @@ cam0_reg: &cam0_regulator {
+ 	gpio = <&gpio 31 GPIO_ACTIVE_HIGH>;
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ 	status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
+@@ -243,6 +243,7 @@ cam0_reg: &cam_dummy_reg {
+ 
+ i2c_arm: &i2c1 {};
+ i2c_vc: &i2c0 {};
++i2c_csi_dsi0: &i2c0 {};
+ 
+ / {
+ 	__overrides__ {
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts
+@@ -178,6 +178,7 @@ cam0_reg: &cam_dummy_reg {
+ 
+ i2c_arm: &i2c1 {};
+ i2c_vc: &i2c0 {};
++i2c_csi_dsi0: &i2c0 {};
+ 
+ / {
+ 	__overrides__ {
+--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
+@@ -186,6 +186,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2709-rpi-cm2.dts
++++ b/arch/arm/boot/dts/bcm2709-rpi-cm2.dts
+@@ -20,6 +20,9 @@ cam0_reg: &cam0_regulator {
+ 	gpio = <&gpio 30 GPIO_ACTIVE_HIGH>;
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ 	status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -127,6 +127,9 @@
+ 	status = "disabled";
+ };
+ 
++i2s_clk_producer: &i2s {};
++i2s_clk_consumer: &i2s {};
++
+ &clocks {
+ 	firmware = <&firmware>;
+ };
+--- a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
+@@ -186,6 +186,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
+@@ -274,6 +274,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
+@@ -283,6 +283,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
+@@ -19,6 +19,9 @@ cam0_reg: &cam0_regulator {
+ 	gpio = <&gpio 31 GPIO_ACTIVE_HIGH>;
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ 	status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
+@@ -262,6 +262,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -400,6 +400,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
+@@ -409,6 +409,9 @@ cam0_reg: &cam1_reg {
+ 	gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts
+@@ -282,6 +282,9 @@ cam0_reg: &cam0_regulator {
+ 	status = "disabled";
+ };
+ 
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ 	__overrides__ {
+ 		audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -0,0 +1,824 @@
++// SPDX-License-Identifier: GPL-2.0
++/dts-v1/;
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/clock/rp1.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/mfd/rp1.h>
++#include <dt-bindings/pwm/pwm.h>
++#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
++
++#define i2c0 _i2c0
++#define i2c3 _i2c3
++#define i2c4 _i2c4
++#define i2c5 _i2c5
++#define i2c6 _i2c6
++#define i2c8 _i2c8
++#define i2s _i2s
++#define pwm0 _pwm0
++#define pwm1 _pwm1
++#define spi0 _spi0
++#define spi3 _spi3
++#define spi4 _spi4
++#define spi5 _spi5
++#define spi6 _spi6
++#define uart0 _uart0
++#define uart2 _uart2
++#define uart3 _uart3
++#define uart4 _uart4
++#define uart5 _uart5
++
++#include "bcm2712.dtsi"
++
++#undef i2c0
++#undef i2c3
++#undef i2c4
++#undef i2c5
++#undef i2c6
++#undef i2c8
++#undef i2s
++#undef pwm0
++#undef pwm1
++#undef spi0
++#undef spi3
++#undef spi4
++#undef spi5
++#undef spi6
++#undef uart0
++#undef uart2
++#undef uart3
++#undef uart4
++#undef uart5
++
++/ {
++	compatible = "raspberrypi,5-model-b", "brcm,bcm2712";
++	model = "Raspberry Pi 5 Model B";
++
++	/* Will be filled by the bootloader */
++	memory@0 {
++		device_type = "memory";
++		reg = <0 0 0x28000000>;
++	};
++
++	leds: leds {
++		compatible = "gpio-leds";
++
++		pwr_led: led-pwr {
++			label = "PWR";
++			gpios = <&rp1_gpio 44 GPIO_ACTIVE_LOW>;
++			default-state = "off";
++			linux,default-trigger = "none";
++		};
++
++		act_led: led-act {
++			label = "ACT";
++			gpios = <&gio_aon 9 GPIO_ACTIVE_LOW>;
++			default-state = "off";
++			linux,default-trigger = "mmc0";
++		};
++	};
++
++	sd_io_1v8_reg: sd_io_1v8_reg {
++		compatible = "regulator-gpio";
++		regulator-name = "vdd-sd-io";
++		regulator-min-microvolt = <1800000>;
++		regulator-max-microvolt = <3300000>;
++		regulator-boot-on;
++		regulator-always-on;
++		regulator-settling-time-us = <5000>;
++		gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>;
++		states = <1800000 0x1
++			  3300000 0x0>;
++		status = "okay";
++	};
++
++	sd_vcc_reg: sd_vcc_reg {
++		compatible = "regulator-fixed";
++		regulator-name = "vcc-sd";
++		regulator-min-microvolt = <3300000>;
++		regulator-max-microvolt = <3300000>;
++		regulator-boot-on;
++		enable-active-high;
++		gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>;
++		status = "okay";
++	};
++
++	wl_on_reg: wl_on_reg {
++		compatible = "regulator-fixed";
++		regulator-name = "wl-on-regulator";
++		regulator-min-microvolt = <3300000>;
++		regulator-max-microvolt = <3300000>;
++		pinctrl-0 = <&wl_on_pins>;
++		pinctrl-names = "default";
++
++		gpio = <&gio 28 GPIO_ACTIVE_HIGH>;
++
++		startup-delay-us = <150000>;
++		enable-active-high;
++	};
++
++	clocks: clocks {
++	};
++
++	cam1_clk: cam1_clk {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		status = "disabled";
++	};
++
++	cam0_clk: cam0_clk {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		status = "disabled";
++	};
++
++	cam0_reg: cam0_reg {
++		compatible = "regulator-fixed";
++		regulator-name = "cam0_reg";
++		enable-active-high;
++		status = "okay";
++		gpio = <&rp1_gpio 34 0>;  // CD0_IO0_MICCLK, to MIPI 0 connector
++	};
++
++	cam1_reg: cam1_reg {
++		compatible = "regulator-fixed";
++		regulator-name = "cam1_reg";
++		enable-active-high;
++		status = "okay";
++		gpio = <&rp1_gpio 46 0>;  // CD1_IO0_MICCLK, to MIPI 1 connector
++	};
++
++	cam_dummy_reg: cam_dummy_reg {
++		compatible = "regulator-fixed";
++		regulator-name = "cam-dummy-reg";
++		status = "okay";
++	};
++
++	dummy: dummy {
++		// A target for unwanted overlay fragments
++	};
++};
++
++rp1_target: &pcie2 {
++	brcm,vdm-qos-map = <0xbbaa9888>;
++	aspm-no-l0s;
++	status = "okay";
++};
++
++// Add some labels to 2712 device
++
++// The system UART
++uart10: &_uart0 { status = "okay"; };
++
++// The system SPI for the bootloader EEPROM
++spi10: &_spi0 { status = "okay"; };
++
++i2c_rp1boot: &_i2c3 { };
++
++#include "rp1.dtsi"
++
++&rp1 {
++	// PCIe address space layout:
++	// 00_00000000-00_00xxxxxx = RP1 peripherals
++	// 10_00000000-1x_xxxxxxxx = up to 64GB system RAM
++
++	// outbound access aimed at PCIe 0_00xxxxxx -> RP1 c0_40xxxxxx
++	// This is the RP1 peripheral space
++	ranges = <0xc0 0x40000000
++		  0x02000000 0x00 0x00000000
++		  0x00 0x00400000>;
++
++	dma-ranges =
++	// inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
++		     <0x10 0x00000000
++		      0x43000000 0x10 0x00000000
++		      0x10 0x00000000>,
++
++	// inbound RP1 c0_40xxxxxx -> PCIe 00_00xxxxxx
++	// This allows the RP1 DMA controller to address RP1 hardware
++		     <0xc0 0x40000000
++		      0x02000000 0x0 0x00000000
++		      0x0 0x00400000>,
++
++	// inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
++		     <0x00 0x00000000
++		      0x02000000 0x10 0x00000000
++		      0x10 0x00000000>;
++};
++
++// Expose RP1 nodes as system nodes with labels
++
++&rp1_dma  {
++	status = "okay";
++};
++
++&rp1_eth {
++	status = "okay";
++	phy-handle = <&phy1>;
++	phy-reset-gpios = <&rp1_gpio 32 GPIO_ACTIVE_LOW>;
++	phy-reset-duration = <5>;
++
++	phy1: ethernet-phy@1 {
++		reg = <0x1>;
++		brcm,powerdown-enable;
++	};
++};
++
++gpio: &rp1_gpio {
++	status = "okay";
++};
++
++aux: &dummy {};
++
++&rp1_usb0 {
++	pinctrl-0 = <&usb_vbus_pins>;
++	pinctrl-names = "default";
++	status = "okay";
++};
++
++&rp1_usb1 {
++	status = "okay";
++};
++
++#include "bcm2712-rpi.dtsi"
++
++// A few extra labels to keep overlays happy
++
++i2c0if: &rp1_gpio {};
++i2c0mux: &rp1_gpio {};
++
++i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
++	pinctrl-0 = <&rp1_i2c6_38_39>;
++	pinctrl-names = "default";
++};
++
++i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only
++	pinctrl-0 = <&rp1_i2c4_40_41>;
++	pinctrl-names = "default";
++};
++
++i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
++
++csi0: &rp1_csi0 { };
++csi1: &rp1_csi1 { };
++dsi0: &rp1_dsi0 { };
++dsi1: &rp1_dsi1 { };
++dpi: &rp1_dpi { };
++vec: &rp1_vec { };
++dpi_gpio0:              &rp1_dpi_24bit_gpio0        { };
++dpi_gpio1:              &rp1_dpi_24bit_gpio2        { };
++dpi_18bit_cpadhi_gpio0: &rp1_dpi_18bit_cpadhi_gpio0 { };
++dpi_18bit_cpadhi_gpio2: &rp1_dpi_18bit_cpadhi_gpio2 { };
++dpi_18bit_gpio0:        &rp1_dpi_18bit_gpio0        { };
++dpi_18bit_gpio2:        &rp1_dpi_18bit_gpio2        { };
++dpi_16bit_cpadhi_gpio0: &rp1_dpi_16bit_cpadhi_gpio0 { };
++dpi_16bit_cpadhi_gpio2: &rp1_dpi_16bit_cpadhi_gpio2 { };
++dpi_16bit_gpio0:        &rp1_dpi_16bit_gpio0        { };
++dpi_16bit_gpio2:        &rp1_dpi_16bit_gpio2        { };
++
++/* Add the IOMMUs for some RP1 bus masters */
++
++&csi0 {
++	iommus = <&iommu5>;
++};
++
++&csi1 {
++	iommus = <&iommu5>;
++};
++
++&dsi0 {
++	iommus = <&iommu5>;
++};
++
++&dsi1 {
++	iommus = <&iommu5>;
++};
++
++&dpi {
++	iommus = <&iommu5>;
++};
++
++&vec {
++	iommus = <&iommu5>;
++};
++
++&ddc0 {
++	status = "disabled";
++};
++
++&ddc1 {
++	status = "disabled";
++};
++
++&hdmi0 {
++	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
++	clock-names = "hdmi", "bvb", "audio", "cec";
++	status = "disabled";
++};
++
++&hdmi1 {
++	clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
++	clock-names = "hdmi", "bvb", "audio", "cec";
++	status = "disabled";
++};
++
++&hvs {
++	clocks = <&firmware_clocks 4>, <&firmware_clocks 16>;
++	clock-names = "core", "disp";
++};
++
++&mop {
++	status = "disabled";
++};
++
++&moplet {
++	status = "disabled";
++};
++
++&pixelvalve0 {
++	status = "disabled";
++};
++
++&pixelvalve1 {
++	status = "disabled";
++};
++
++&disp_intr {
++	status = "disabled";
++};
++
++/* SDIO1 is used to drive the SD card */
++&sdio1 {
++	pinctrl-0 = <&emmc_sd_pulls>, <&emmc_aon_cd_pins>;
++	pinctrl-names = "default";
++	vqmmc-supply = <&sd_io_1v8_reg>;
++	vmmc-supply = <&sd_vcc_reg>;
++	bus-width = <4>;
++	sd-uhs-sdr50;
++	sd-uhs-ddr50;
++	sd-uhs-sdr104;
++	//broken-cd;
++	//no-1-8-v;
++	status = "okay";
++};
++
++&pinctrl_aon {
++	emmc_aon_cd_pins: emmc_aon_cd_pins {
++		function = "sd_card_g";
++		pins = "aon_gpio5";
++		bias-pull-up;
++	};
++
++	/* Slight hack - only one PWM pin (status LED) is usable */
++	aon_pwm_1pin: aon_pwm_1pin {
++		function = "aon_pwm";
++		pins = "aon_gpio9";
++	};
++};
++
++&pinctrl {
++	pwr_button_pins: pwr_button_pins {
++		function = "gpio";
++		pins = "gpio20";
++		bias-pull-up;
++	};
++
++	wl_on_pins: wl_on_pins {
++		function = "gpio";
++		pins = "gpio28";
++	};
++
++	bt_shutdown_pins: bt_shutdown_pins {
++		function = "gpio";
++		pins = "gpio29";
++	};
++
++	emmc_sd_pulls: emmc_sd_pulls {
++		function = "emmc_dat0", "emmc_dat1", "emmc_dat2", "emmc_dat3";
++		bias-pull-up;
++	};
++};
++
++/* uarta communicates with the BT module */
++&uarta {
++	uart-has-rtscts;
++	auto-flow-control;
++	status = "okay";
++	clock-frequency = <96000000>;
++	pinctrl-0 = <&uarta_24_pins &bt_shutdown_pins>;
++	pinctrl-names = "default";
++
++	bluetooth: bluetooth {
++		compatible = "brcm,bcm43438-bt";
++		max-speed = <3000000>;
++		shutdown-gpios = <&gio 29 GPIO_ACTIVE_HIGH>;
++		local-bd-address = [ 00 00 00 00 00 00 ];
++	};
++};
++
++&i2c_rp1boot {
++	clock-frequency = <400000>;
++	pinctrl-0 = <&i2c3_m4_agpio0_pins>;
++	pinctrl-names = "default";
++};
++
++/ {
++	chosen: chosen {
++		bootargs = "coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
++		stdout-path = "serial10:115200n8";
++	};
++
++	fan: cooling_fan {
++		status = "disabled";
++		compatible = "pwm-fan";
++		#cooling-cells = <2>;
++		cooling-min-state = <0>;
++		cooling-max-state = <3>;
++		cooling-levels = <0 75 125 175 250>;
++		pwms = <&rp1_pwm1 3 41566 PWM_POLARITY_INVERTED>;
++		rpm-regmap = <&rp1_pwm1>;
++		rpm-offset = <0x3c>;
++	};
++
++	pwr_button {
++		compatible = "gpio-keys";
++
++		pinctrl-names = "default";
++		pinctrl-0 = <&pwr_button_pins>;
++		status = "okay";
++
++		pwr_key: pwr {
++			label = "pwr_button";
++			// linux,code = <205>; // KEY_SUSPEND
++			linux,code = <116>; // KEY_POWER
++			gpios = <&gio 20 GPIO_ACTIVE_LOW>;
++			debounce-interval = <50>; // ms
++		};
++	};
++};
++
++&usb {
++	power-domains = <&power RPI_POWER_DOMAIN_USB>;
++};
++
++/* SDIO2 drives the WLAN interface */
++&sdio2 {
++	pinctrl-0 = <&sdio2_30_pins>;
++	pinctrl-names = "default";
++	bus-width = <4>;
++	vmmc-supply = <&wl_on_reg>;
++	sd-uhs-ddr50;
++	non-removable;
++	status = "okay";
++	#address-cells = <1>;
++	#size-cells = <0>;
++
++	wifi: wifi@1 {
++		reg = <1>;
++		compatible = "brcm,bcm4329-fmac";
++		local-mac-address = [00 00 00 00 00 00];
++	};
++};
++
++&rpivid {
++	status = "okay";
++};
++
++&pinctrl {
++	spi10_gpio2: spi10_gpio2 {
++		function = "vc_spi0";
++		pins = "gpio2", "gpio3", "gpio4";
++		bias-disable;
++	};
++
++	spi10_cs_gpio1: spi10_cs_gpio1 {
++		function = "gpio";
++		pins = "gpio1";
++		bias-pull-up;
++	};
++};
++
++spi10_pins: &spi10_gpio2 {};
++spi10_cs_pins: &spi10_cs_gpio1 {};
++
++&spi10 {
++	pinctrl-names = "default";
++	cs-gpios = <&gio 1 1>;
++	pinctrl-0 = <&spi10_pins &spi10_cs_pins>;
++
++	spidev10: spidev@0 {
++		compatible = "spidev";
++		reg = <0>;	/* CE0 */
++		#address-cells = <1>;
++		#size-cells = <0>;
++		spi-max-frequency = <20000000>;
++		status = "okay";
++	};
++};
++
++// =============================================
++// Board specific stuff here
++
++&gio_aon {
++	// Don't use GIO_AON as an interrupt controller because it will
++	// clash with the firmware monitoring the PMIC interrupt via the VPU.
++
++	/delete-property/ interrupt-controller;
++};
++
++&main_aon_irq {
++	// Don't use the MAIN_AON_IRQ interrupt controller because it will
++	// clash with the firmware monitoring the PMIC interrupt via the VPU.
++
++	status = "disabled";
++};
++
++&rp1_pwm1 {
++	status = "disabled";
++	pinctrl-0 = <&rp1_pwm1_gpio45>;
++	pinctrl-names = "default";
++};
++
++&thermal_trips {
++	cpu_tepid: cpu-tepid {
++		temperature = <50000>;
++		hysteresis = <5000>;
++		type = "active";
++	};
++
++	cpu_warm: cpu-warm {
++		temperature = <60000>;
++		hysteresis = <5000>;
++		type = "active";
++	};
++
++	cpu_hot: cpu-hot {
++		temperature = <67500>;
++		hysteresis = <5000>;
++		type = "active";
++	};
++
++	cpu_vhot: cpu-vhot {
++		temperature = <75000>;
++		hysteresis = <5000>;
++		type = "active";
++	};
++};
++
++&cooling_maps {
++	tepid {
++		trip = <&cpu_tepid>;
++		cooling-device = <&fan 1 1>;
++	};
++
++	warm {
++		trip = <&cpu_warm>;
++		cooling-device = <&fan 2 2>;
++	};
++
++	hot {
++		trip = <&cpu_hot>;
++		cooling-device = <&fan 3 3>;
++	};
++
++	vhot {
++		trip = <&cpu_vhot>;
++		cooling-device = <&fan 4 4>;
++	};
++
++	melt {
++		trip = <&cpu_crit>;
++		cooling-device = <&fan 4 4>;
++	};
++};
++
++&gio {
++	// The GPIOs above 35 are not used on Pi 5, so shrink the upper bank
++	// to reduce the clutter in gpioinfo/pinctrl
++	brcm,gpio-bank-widths = <32 4>;
++
++	gpio-line-names =
++		"-", // GPIO_000
++		"2712_BOOT_CS_N", // GPIO_001
++		"2712_BOOT_MISO", // GPIO_002
++		"2712_BOOT_MOSI", // GPIO_003
++		"2712_BOOT_SCLK", // GPIO_004
++		"-", // GPIO_005
++		"-", // GPIO_006
++		"-", // GPIO_007
++		"-", // GPIO_008
++		"-", // GPIO_009
++		"-", // GPIO_010
++		"-", // GPIO_011
++		"-", // GPIO_012
++		"-", // GPIO_013
++		"PCIE_SDA", // GPIO_014
++		"PCIE_SCL", // GPIO_015
++		"-", // GPIO_016
++		"-", // GPIO_017
++		"-", // GPIO_018
++		"-", // GPIO_019
++		"PWR_GPIO", // GPIO_020
++		"2712_G21_FS", // GPIO_021
++		"-", // GPIO_022
++		"-", // GPIO_023
++		"BT_RTS", // GPIO_024
++		"BT_CTS", // GPIO_025
++		"BT_TXD", // GPIO_026
++		"BT_RXD", // GPIO_027
++		"WL_ON", // GPIO_028
++		"BT_ON", // GPIO_029
++		"WIFI_SDIO_CLK", // GPIO_030
++		"WIFI_SDIO_CMD", // GPIO_031
++		"WIFI_SDIO_D0", // GPIO_032
++		"WIFI_SDIO_D1", // GPIO_033
++		"WIFI_SDIO_D2", // GPIO_034
++		"WIFI_SDIO_D3"; // GPIO_035
++};
++
++&gio_aon {
++	gpio-line-names =
++		"RP1_SDA", // AON_GPIO_00
++		"RP1_SCL", // AON_GPIO_01
++		"RP1_RUN", // AON_GPIO_02
++		"SD_IOVDD_SEL", // AON_GPIO_03
++		"SD_PWR_ON", // AON_GPIO_04
++		"SD_CDET_N", // AON_GPIO_05
++		"SD_FLG_N", // AON_GPIO_06
++		"-", // AON_GPIO_07
++		"2712_WAKE", // AON_GPIO_08
++		"2712_STAT_LED", // AON_GPIO_09
++		"-", // AON_GPIO_10
++		"-", // AON_GPIO_11
++		"PMIC_INT", // AON_GPIO_12
++		"UART_TX_FS", // AON_GPIO_13
++		"UART_RX_FS", // AON_GPIO_14
++		"-", // AON_GPIO_15
++		"-", // AON_GPIO_16
++
++		// Pad bank0 out to 32 entries
++		"", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
++
++		"HDMI0_SCL", // AON_SGPIO_00
++		"HDMI0_SDA", // AON_SGPIO_01
++		"HDMI1_SCL", // AON_SGPIO_02
++		"HDMI1_SDA", // AON_SGPIO_03
++		"PMIC_SCL", // AON_SGPIO_04
++		"PMIC_SDA"; // AON_SGPIO_05
++
++	rp1_run_hog {
++		gpio-hog;
++		gpios = <2 GPIO_ACTIVE_HIGH>;
++		output-high;
++		line-name = "RP1 RUN pin";
++	};
++};
++
++&rp1_gpio {
++	gpio-line-names =
++		"ID_SD", // GPIO0
++		"ID_SC", // GPIO1
++		"PIN3", // GPIO2
++		"PIN5", // GPIO3
++		"PIN7", // GPIO4
++		"PIN29", // GPIO5
++		"PIN31", // GPIO6
++		"PIN26", // GPIO7
++		"PIN24", // GPIO8
++		"PIN21", // GPIO9
++		"PIN19", // GPIO10
++		"PIN23", // GPIO11
++		"PIN32", // GPIO12
++		"PIN33", // GPIO13
++		"PIN8", // GPIO14
++		"PIN10", // GPIO15
++		"PIN36", // GPIO16
++		"PIN11", // GPIO17
++		"PIN12", // GPIO18
++		"PIN35", // GPIO19
++		"PIN38", // GPIO20
++		"PIN40", // GPIO21
++		"PIN15", // GPIO22
++		"PIN16", // GPIO23
++		"PIN18", // GPIO24
++		"PIN22", // GPIO25
++		"PIN37", // GPIO26
++		"PIN13", // GPIO27
++
++		"PCIE_RP1_WAKE", // GPIO28
++		"FAN_TACH", // GPIO29
++		"HOST_SDA", // GPIO30
++		"HOST_SCL", // GPIO31
++		"ETH_RST_N", // GPIO32
++		"-", // GPIO33
++
++		"CD0_IO0_MICCLK", // GPIO34
++		"CD0_IO0_MICDAT0", // GPIO35
++		"RP1_PCIE_CLKREQ_N", // GPIO36
++		"-", // GPIO37
++		"CD0_SDA", // GPIO38
++		"CD0_SCL", // GPIO39
++		"CD1_SDA", // GPIO40
++		"CD1_SCL", // GPIO41
++		"USB_VBUS_EN", // GPIO42
++		"USB_OC_N", // GPIO43
++		"RP1_STAT_LED", // GPIO44
++		"FAN_PWM", // GPIO45
++		"CD1_IO0_MICCLK", // GPIO46
++		"2712_WAKE", // GPIO47
++		"CD1_IO1_MICDAT1", // GPIO48
++		"EN_MAX_USB_CUR", // GPIO49
++		"-", // GPIO50
++		"-", // GPIO51
++		"-", // GPIO52
++		"-"; // GPIO53
++
++	usb_vbus_pins: usb_vbus_pins {
++		function = "vbus1";
++		pins = "gpio42", "gpio43";
++	};
++};
++
++/ {
++	aliases: aliases {
++		blconfig = &blconfig;
++		bluetooth = &bluetooth;
++		console = &uart10;
++		ethernet0 = &rp1_eth;
++		wifi0 = &wifi;
++		fb = &fb;
++		mailbox = &mailbox;
++		mmc0 = &sdio1;
++		uart0 = &uart0;
++		uart1 = &uart1;
++		uart2 = &uart2;
++		uart3 = &uart3;
++		uart4 = &uart4;
++		uart10 = &uart10;
++		serial0 = &uart0;
++		serial1 = &uart1;
++		serial2 = &uart2;
++		serial3 = &uart3;
++		serial4 = &uart4;
++		serial10 = &uart10;
++		i2c = &i2c_arm;
++		i2c0 = &i2c0;
++		i2c1 = &i2c1;
++		i2c2 = &i2c2;
++		i2c3 = &i2c3;
++		i2c4 = &i2c4;
++		i2c5 = &i2c5;
++		i2c6 = &i2c6;
++		i2c10 = &i2c_rp1boot;
++		// Bit-bashed i2c_gpios start at 10
++		spi0 = &spi0;
++		spi1 = &spi1;
++		spi2 = &spi2;
++		spi3 = &spi3;
++		spi4 = &spi4;
++		spi5 = &spi5;
++		spi10 = &spi10;
++		gpio0 = &gpio;
++		gpio1 = &gio;
++		gpio2 = &gio_aon;
++		gpio3 = &pinctrl;
++		gpio4 = &pinctrl_aon;
++		usb0 = &rp1_usb0;
++		usb1 = &rp1_usb1;
++	};
++
++	__overrides__ {
++		bdaddr = <&bluetooth>, "local-bd-address[";
++		button_debounce = <&pwr_key>, "debounce-interval:0";
++		cooling_fan = <&fan>, "status", <&rp1_pwm1>, "status";
++		uart0_console = <&uart0>,"status", <&aliases>, "console=",&uart0;
++		i2c0 = <&i2c0>, "status";
++		i2c1 = <&i2c1>, "status";
++		i2c = <&i2c1>, "status";
++		i2c_arm = <&i2c_arm>, "status";
++		i2c_vc = <&i2c_vc>, "status";
++		i2c_csi_dsi = <&i2c_csi_dsi>, "status";
++		i2c_csi_dsi0 = <&i2c_csi_dsi0>, "status";
++		i2c_csi_dsi1 = <&i2c_csi_dsi1>, "status";
++		i2c0_baudrate = <&i2c0>, "clock-frequency:0";
++		i2c1_baudrate = <&i2c1>, "clock-frequency:0";
++		i2c_baudrate = <&i2c_arm>, "clock-frequency:0";
++		i2c_arm_baudrate = <&i2c_arm>, "clock-frequency:0";
++		i2c_vc_baudrate = <&i2c_vc>, "clock-frequency:0";
++		nvme = <&pciex1>, "status";
++		pciex1 = <&pciex1>, "status";
++		pciex1_gen = <&pciex1> , "max-link-speed:0";
++		pciex1_no_l0s = <&pciex1>, "aspm-no-l0s?";
++		random = <&random>, "status";
++		rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
++		spi = <&spi0>, "status";
++		suspend = <&pwr_key>, "linux,code:0=205";
++		uart0 = <&uart0>, "status";
++		wifiaddr = <&wifi>, "local-mac-address[";
++
++		act_led_activelow = <&act_led>, "active-low?";
++		act_led_trigger = <&act_led>, "linux,default-trigger";
++		pwr_led_activelow = <&pwr_led>, "gpios:8";
++		pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712-rpi.dtsi
+@@ -0,0 +1,281 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <dt-bindings/power/raspberrypi-power.h>
++
++&soc {
++	firmware: firmware {
++		compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
++		#address-cells = <1>;
++		#size-cells = <1>;
++
++		mboxes = <&mailbox>;
++		dma-ranges;
++
++		firmware_clocks: clocks {
++			compatible = "raspberrypi,firmware-clocks";
++			#clock-cells = <1>;
++		};
++
++		reset: reset {
++			compatible = "raspberrypi,firmware-reset";
++			#reset-cells = <1>;
++		};
++
++		vcio: vcio {
++			compatible = "raspberrypi,vcio";
++		};
++	};
++
++	power: power {
++		compatible = "raspberrypi,bcm2835-power";
++		firmware = <&firmware>;
++		#power-domain-cells = <1>;
++	};
++
++	fb: fb {
++		compatible = "brcm,bcm2708-fb";
++		firmware = <&firmware>;
++		status = "okay";
++	};
++
++	rpi_rtc: rpi_rtc {
++		compatible = "raspberrypi,rpi-rtc";
++		firmware = <&firmware>;
++		status = "okay";
++		trickle-charge-microvolt = <0>;
++	};
++
++	/* Define these notional regulators for use by overlays, etc. */
++	vdd_3v3_reg: fixedregulator_3v3 {
++		compatible = "regulator-fixed";
++		regulator-always-on;
++		regulator-max-microvolt = <3300000>;
++		regulator-min-microvolt = <3300000>;
++		regulator-name = "3v3";
++	};
++
++	vdd_5v0_reg: fixedregulator_5v0 {
++		compatible = "regulator-fixed";
++		regulator-always-on;
++		regulator-max-microvolt = <5000000>;
++		regulator-min-microvolt = <5000000>;
++		regulator-name = "5v0";
++	};
++};
++
++/ {
++	__overrides__ {
++		arm_freq;
++	};
++};
++
++pciex1: &pcie1 { };
++pciex4: &pcie2 { };
++
++&dma32 {
++	/* The VPU firmware uses DMA channel 11 for VCHIQ */
++	brcm,dma-channel-mask = <0x03f>;
++};
++
++&dma40 {
++	/* The VPU firmware DMA channel 11 for VCHIQ */
++	brcm,dma-channel-mask = <0x07c0>;
++};
++
++&hdmi0 {
++	dmas = <&dma40 (10|(1<<30)|(1<<24)|(10<<16)|(15<<20))>;
++};
++
++&hdmi1 {
++	dmas = <&dma40 (17|(1<<30)|(1<<24)|(10<<16)|(15<<20))>;
++};
++
++&spi10 {
++	dmas = <&dma40 6>, <&dma40 7>;
++	dma-names = "tx", "rx";
++};
++
++&usb {
++	power-domains = <&power RPI_POWER_DOMAIN_USB>;
++};
++
++&rmem {
++	/*
++	 * RPi4's co-processor will copy the board's bootloader configuration
++	 * into memory for the OS to consume. It'll also update this node with
++	 * its placement information.
++	 */
++	blconfig: nvram@0 {
++		compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
++		#address-cells = <1>;
++		#size-cells = <1>;
++		reg = <0x0 0x0 0x0>;
++		no-map;
++		status = "disabled";
++	};
++};
++
++&rp1_adc {
++	status = "okay";
++};
++
++/* Add some gpiomem nodes to make the devices accessible to userspace.
++ * /dev/gpiomem<n> should expose the registers for the interface with DT alias
++ * gpio<n>.
++ */
++
++&rp1 {
++	gpiomem@d0000 {
++		/* Export IO_BANKs, RIO_BANKs and PADS_BANKs to userspace */
++		compatible = "raspberrypi,gpiomem";
++		reg = <0xc0 0x400d0000  0x0 0x30000>;
++		chardev-name = "gpiomem0";
++	};
++};
++
++&soc {
++	gpiomem@7d508500 {
++		compatible = "raspberrypi,gpiomem";
++		reg = <0x7d508500 0x40>;
++		chardev-name = "gpiomem1";
++	};
++
++	gpiomem@7d517c00 {
++		compatible = "raspberrypi,gpiomem";
++		reg = <0x7d517c00 0x40>;
++		chardev-name = "gpiomem2";
++	};
++
++	gpiomem@7d504100 {
++		compatible = "raspberrypi,gpiomem";
++		reg = <0x7d504100 0x20>;
++		chardev-name = "gpiomem3";
++	};
++
++	gpiomem@7d510700 {
++		compatible = "raspberrypi,gpiomem";
++		reg = <0x7d510700 0x20>;
++		chardev-name = "gpiomem4";
++	};
++};
++
++i2c0: &rp1_i2c0 { };
++i2c1: &rp1_i2c1 { };
++i2c2: &rp1_i2c2 { };
++i2c3: &rp1_i2c3 { };
++i2c4: &rp1_i2c4 { };
++i2c5: &rp1_i2c5 { };
++i2c6: &rp1_i2c6 { };
++i2s:  &rp1_i2s0 { };
++i2s_clk_producer: &rp1_i2s0 { };
++i2s_clk_consumer: &rp1_i2s1 { };
++pwm0: &rp1_pwm0 { };
++pwm1: &rp1_pwm1 { };
++pwm: &pwm0 { };
++spi0: &rp1_spi0 { };
++spi1: &rp1_spi1 { };
++spi2: &rp1_spi2 { };
++spi3: &rp1_spi3 { };
++spi4: &rp1_spi4 { };
++spi5: &rp1_spi5 { };
++
++uart0_pins: &rp1_uart0_14_15 {};
++uart0_ctsrts_pins: &rp1_uart0_ctsrts_16_17 {};
++uart0: &rp1_uart0 {
++	pinctrl-0 = <&uart0_pins>;
++};
++
++uart1_pins: &rp1_uart1_0_1 {};
++uart1_ctsrts_pins: &rp1_uart1_ctsrts_2_3 {};
++uart1: &rp1_uart1 { };
++
++uart2_pins: &rp1_uart2_4_5 {};
++uart2_ctsrts_pins: &rp1_uart2_ctsrts_6_7 {};
++uart2: &rp1_uart2 { };
++
++uart3_pins: &rp1_uart3_8_9 {};
++uart3_ctsrts_pins: &rp1_uart3_ctsrts_10_11 {};
++uart3: &rp1_uart3 { };
++
++uart4_pins: &rp1_uart4_12_13 {};
++uart4_ctsrts_pins: &rp1_uart4_ctsrts_14_15 {};
++uart4: &rp1_uart4 { };
++
++i2c_vc: &i2c0 {      // This is pins 27,28 on the header (not MIPI)
++	pinctrl-0 = <&rp1_i2c0_0_1>;
++	pinctrl-names = "default";
++};
++
++i2c_arm: &i2c1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&rp1_i2c1_2_3>;
++};
++
++&i2c2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&rp1_i2c2_4_5>;
++};
++
++&i2c3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&rp1_i2c3_6_7>;
++};
++
++&i2s_clk_producer {
++	pinctrl-names = "default";
++	pinctrl-0 = <&rp1_i2s0_18_21>;
++};
++
++&i2s_clk_consumer {
++	pinctrl-names = "default";
++	pinctrl-0 = <&rp1_i2s1_18_21>;
++};
++
++spi0_pins: &rp1_spi0_gpio9 {};
++spi0_cs_pins: &rp1_spi0_cs_gpio7 {};
++
++&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>;
++	};
++};
++
++spi2_pins: &rp1_spi2_gpio1 {};
++&spi2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&spi2_pins>;
++};
++
++spi3_pins: &rp1_spi3_gpio5 {};
++&spi3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&spi3_pins>;
++};
++
++spi4_pins: &rp1_spi4_gpio9 {};
++&spi4 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&spi4_pins>;
++};
++
++spi5_pins: &rp1_spi5_gpio13 {};
++&spi5 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&spi5_pins>;
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -0,0 +1,1287 @@
++// SPDX-License-Identifier: GPL-2.0
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/soc/bcm2835-pm.h>
++#include <dt-bindings/phy/phy.h>
++
++/ {
++	compatible = "brcm,bcm2712", "brcm,bcm2711";
++	model = "BCM2712";
++
++	#address-cells = <2>;
++	#size-cells = <1>;
++
++	interrupt-parent = <&gicv2>;
++
++	rmem: reserved-memory {
++		#address-cells = <2>;
++		#size-cells = <1>;
++		ranges;
++
++		atf@0 {
++			reg = <0x0 0x0 0x80000>;
++			no-map;
++		};
++
++		cma: linux,cma {
++			compatible = "shared-dma-pool";
++			size = <0x4000000>; /* 64MB */
++			reusable;
++			linux,cma-default;
++
++			/*
++			 * 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>;
++		};
++	};
++
++	thermal-zones {
++		cpu_thermal: cpu-thermal {
++			polling-delay-passive = <2000>;
++			polling-delay = <1000>;
++			coefficients = <(-550) 450000>;
++			thermal-sensors = <&thermal>;
++
++			thermal_trips: trips {
++				cpu_crit: cpu-crit {
++					temperature	= <110000>;
++					hysteresis	= <0>;
++					type		= "critical";
++				};
++			};
++
++			cooling_maps: cooling-maps {
++			};
++		};
++	};
++
++	clk_27MHz: clk-27M {
++		#clock-cells = <0>;
++		compatible = "fixed-clock";
++		clock-frequency = <27000000>;
++		clock-output-names = "27MHz-clock";
++	};
++
++	clk_108MHz: clk-108M {
++		#clock-cells = <0>;
++		compatible = "fixed-clock";
++		clock-frequency = <108000000>;
++		clock-output-names = "108MHz-clock";
++	};
++
++	hvs: hvs@107c580000 {
++		compatible = "brcm,bcm2712-hvs";
++		reg = <0x10 0x7c580000 0x1a000>;
++		interrupt-parent = <&disp_intr>;
++		interrupts = <2>, <9>, <16>;
++		interrupt-names = "ch0-eof", "ch1-eof", "ch2-eof";
++		//iommus = <&iommu4>;
++		status = "disabled";
++	};
++
++	soc: soc {
++		compatible = "simple-bus";
++		#address-cells = <1>;
++		#size-cells = <1>;
++
++		ranges     = <0x7c000000  0x10 0x7c000000  0x04000000>;
++		/* Emulate a contiguous 30-bit address range for DMA */
++		dma-ranges = <0xc0000000  0x00 0x00000000  0x40000000>,
++			     <0x7c000000  0x10 0x7c000000  0x04000000>;
++
++		system_timer: timer@7c003000 {
++			compatible = "brcm,bcm2835-system-timer";
++			reg = <0x7c003000 0x1000>;
++			interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
++		     		     <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
++		     		     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
++			clock-frequency = <1000000>;
++		};
++
++		firmwarekms: firmwarekms@7d503000 {
++			compatible = "raspberrypi,rpi-firmware-kms";
++			/* SUN_L2 interrupt reg */
++			reg = <0x7d503000 0x18>;
++			interrupt-parent = <&cpu_l2_irq>;
++			interrupts = <19>;
++			brcm,firmware = <&firmware>;
++			status = "disabled";
++		};
++
++		mailbox: mailbox@7c013880 {
++			compatible = "brcm,bcm2835-mbox";
++			reg = <0x7c013880 0x40>;
++			interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++			#mbox-cells = <0>;
++		};
++
++		pixelvalve0: pixelvalve@7c410000 {
++			compatible = "brcm,bcm2712-pixelvalve0";
++			reg = <0x7c410000 0x100>;
++			interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
++			status = "disabled";
++		};
++
++		pixelvalve1: pixelvalve@7c411000 {
++			compatible = "brcm,bcm2712-pixelvalve1";
++			reg = <0x7c411000 0x100>;
++			interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
++			status = "disabled";
++		};
++
++		usb: usb@7c480000 {
++			compatible = "brcm,bcm2835-usb";
++			reg = <0x7c480000 0x10000>;
++			interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			clocks = <&clk_usb>;
++			clock-names = "otg";
++			phys = <&usbphy>;
++			phy-names = "usb2-phy";
++			status = "disabled";
++		};
++
++		mop: mop@7c500000 {
++			compatible = "brcm,bcm2712-mop";
++			reg = <0x7c500000 0x20>;
++			interrupt-parent = <&disp_intr>;
++			interrupts = <1>;
++			status = "disabled";
++		};
++
++		moplet: moplet@7c501000 {
++			compatible = "brcm,bcm2712-moplet";
++			reg = <0x7c501000 0x20>;
++			interrupt-parent = <&disp_intr>;
++			interrupts = <0>;
++			status = "disabled";
++		};
++
++		disp_intr: interrupt-controller@7c502000 {
++			compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
++			reg = <0x7c502000 0x30>;
++			interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++			status = "disabled";
++		};
++
++		dvp: clock@7c700000 {
++			compatible = "brcm,brcm2711-dvp";
++			reg = <0x7c700000 0x10>;
++			clocks = <&clk_108MHz>;
++			#clock-cells = <1>;
++			#reset-cells = <1>;
++		};
++
++		/*
++		 * This node is the provider for the enable-method for
++		 * bringing up secondary cores.
++		 */
++		local_intc: local_intc@7cd00000 {
++			compatible = "brcm,bcm2836-l1-intc";
++			reg = <0x7cd00000 0x100>;
++		};
++
++		uart0: serial@7d001000 {
++			compatible = "arm,pl011", "arm,primecell";
++			reg = <0x7d001000 0x200>;
++			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_uart>,
++				 <&clk_vpu>;
++			clock-names = "uartclk", "apb_pclk";
++			arm,primecell-periphid = <0x00241011>;
++			status = "disabled";
++		};
++
++		uart2: serial@7d001400 {
++			compatible = "arm,pl011", "arm,primecell";
++			reg = <0x7d001400 0x200>;
++			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_uart>,
++				 <&clk_vpu>;
++			clock-names = "uartclk", "apb_pclk";
++			arm,primecell-periphid = <0x00241011>;
++			status = "disabled";
++		};
++
++		uart3: serial@7d001600 {
++			compatible = "arm,pl011", "arm,primecell";
++			reg = <0x7d001600 0x200>;
++			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_uart>,
++				 <&clk_vpu>;
++			clock-names = "uartclk", "apb_pclk";
++			arm,primecell-periphid = <0x00241011>;
++			status = "disabled";
++		};
++
++		uart4: serial@7d001800 {
++			compatible = "arm,pl011", "arm,primecell";
++			reg = <0x7d001800 0x200>;
++			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_uart>,
++				 <&clk_vpu>;
++			clock-names = "uartclk", "apb_pclk";
++			arm,primecell-periphid = <0x00241011>;
++			status = "disabled";
++		};
++
++		uart5: serial@7d001a00 {
++			compatible = "arm,pl011", "arm,primecell";
++			reg = <0x7d001a00 0x200>;
++			interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_uart>,
++				 <&clk_vpu>;
++			clock-names = "uartclk", "apb_pclk";
++			arm,primecell-periphid = <0x00241011>;
++			status = "disabled";
++		};
++
++		sdhost: mmc@7d002000 {
++			compatible = "brcm,bcm2835-sdhost";
++			reg = <0x7d002000 0x100>;
++			//interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			status = "disabled";
++		};
++
++		i2s: i2s@7d003000 {
++			compatible = "brcm,bcm2835-i2s";
++			reg = <0x7d003000 0x24>;
++			//clocks = <&cprman BCM2835_CLOCK_PCM>;
++			status = "disabled";
++		};
++
++		spi0: spi@7d004000 {
++			compatible = "brcm,bcm2835-spi";
++			reg = <0x7d004000 0x200>;
++			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			num-cs = <1>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		spi3: spi@7d004600 {
++			compatible = "brcm,bcm2835-spi";
++			reg = <0x7d004600 0x0200>;
++			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		spi4: spi@7d004800 {
++			compatible = "brcm,bcm2835-spi";
++			reg = <0x7d004800 0x0200>;
++			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		spi5: spi@7d004a00 {
++			compatible = "brcm,bcm2835-spi";
++			reg = <0x7d004a00 0x0200>;
++			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		spi6: spi@7d004c00 {
++			compatible = "brcm,bcm2835-spi";
++			reg = <0x7d004c00 0x0200>;
++			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c0: i2c@7d005000 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005000 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c3: i2c@7d005600 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005600 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c4: i2c@7d005800 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005800 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c5: i2c@7d005a00 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005a00 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c6: i2c@7d005c00 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005c00 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		i2c8: i2c@7d005e00 {
++			compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++			reg = <0x7d005e00 0x20>;
++			interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_vpu>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		pwm0: pwm@7d00c000 {
++			compatible = "brcm,bcm2835-pwm";
++			reg = <0x7d00c000 0x28>;
++			assigned-clock-rates = <10000000>;
++			#pwm-cells = <2>;
++			status = "disabled";
++		};
++
++		pwm1: pwm@7d00c800 {
++			compatible = "brcm,bcm2835-pwm";
++			reg = <0x7d00c800 0x28>;
++			assigned-clock-rates = <10000000>;
++			#pwm-cells = <2>;
++			status = "disabled";
++		};
++
++		pm: watchdog@7d200000 {
++			compatible = "brcm,bcm2712-pm";
++			reg = <0x7d200000 0x308>;
++			reg-names = "pm";
++			#power-domain-cells = <1>;
++			#reset-cells = <1>;
++			//clocks = <&cprman BCM2835_CLOCK_V3D>,
++			//	 <&cprman BCM2835_CLOCK_PERI_IMAGE>,
++			//	 <&cprman BCM2835_CLOCK_H264>,
++			//	 <&cprman BCM2835_CLOCK_ISP>;
++			clock-names = "v3d", "peri_image", "h264", "isp";
++			system-power-controller;
++		};
++
++		cprman: cprman@7d202000 {
++			compatible = "brcm,bcm2711-cprman";
++			reg = <0x7d202000 0x2000>;
++			#clock-cells = <1>;
++
++			/* CPRMAN derives almost everything from the
++			 * platform's oscillator.  However, the DSI
++			 * pixel clocks come from the DSI analog PHY.
++			 */
++			clocks = <&clk_osc>;
++			status = "disabled";
++		};
++
++		random: rng@7d208000 {
++			compatible = "brcm,bcm2711-rng200";
++			reg = <0x7d208000 0x28>;
++			status = "okay";
++		};
++
++		cpu_l2_irq: intc@7d503000 {
++			compatible = "brcm,l2-intc";
++			reg = <0x7d503000 0x18>;
++			interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++		};
++
++		pinctrl: pinctrl@7d504100 {
++			compatible = "brcm,bcm2712-pinctrl";
++			reg = <0x7d504100 0x30>;
++
++			uarta_24_pins: uarta_24_pins {
++				pin_rts {
++					function = "uart0";
++					pins = "gpio24";
++					bias-disable;
++				};
++				pin_cts {
++					function = "uart0";
++					pins = "gpio25";
++					bias-pull-up;
++				};
++				pin_txd {
++					function = "uart0";
++					pins = "gpio26";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart0";
++					pins = "gpio27";
++					bias-pull-up;
++				};
++			};
++
++			sdio2_30_pins: sdio2_30_pins {
++				pin_clk {
++					function = "sd2";
++					pins = "gpio30";
++					bias-disable;
++				};
++				pin_cmd {
++					function = "sd2";
++					pins = "gpio31";
++					bias-pull-up;
++				};
++				pins_dat {
++					function = "sd2";
++					pins = "gpio32", "gpio33", "gpio34", "gpio35";
++					bias-pull-up;
++				};
++			};
++		};
++
++		ddc0: i2c@7d508200 {
++			compatible = "brcm,brcmstb-i2c";
++			reg = <0x7d508200 0x58>;
++			interrupt-parent = <&bsc_irq>;
++			interrupts = <1>;
++			clock-frequency = <200000>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		ddc1: i2c@7d508280 {
++			compatible = "brcm,brcmstb-i2c";
++			reg = <0x7d508280 0x58>;
++			interrupt-parent = <&bsc_irq>;
++			interrupts = <2>;
++			clock-frequency = <200000>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		bscd: i2c@7d508300 {
++			compatible = "brcm,brcmstb-i2c";
++			reg = <0x7d508300 0x58>;
++			interrupt-parent = <&bsc_irq>;
++			interrupts = <0>;
++			clock-frequency = <200000>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		bsc_irq: intc@7d508380 {
++			compatible = "brcm,bcm7271-l2-intc";
++			reg = <0x7d508380 0x10>;
++			interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++		};
++
++		main_irq: intc@7d508400 {
++			compatible = "brcm,bcm7271-l2-intc";
++			reg = <0x7d508400 0x10>;
++			interrupts = <GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++		};
++
++		gio: gpio@7d508500 {
++			compatible = "brcm,brcmstb-gpio";
++			reg = <0x7d508500 0x40>;
++			interrupt-parent = <&main_irq>;
++			interrupts = <0>;
++			gpio-controller;
++			#gpio-cells = <2>;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			brcm,gpio-bank-widths = <32 22>;
++			brcm,gpio-direct;
++		};
++
++		uarta: serial@7d50c000 {
++			compatible = "brcm,bcm7271-uart";
++			reg = <0x7d50c000 0x20>;
++			reg-names = "uart";
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			interrupts = <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>;
++			skip-init;
++			status = "disabled";
++		};
++
++		uartb: serial@7d50d000 {
++			compatible = "brcm,bcm7271-uart";
++			reg = <0x7d50d000 0x20>;
++			reg-names = "uart";
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			interrupts = <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>;
++			skip-init;
++			status = "disabled";
++		};
++
++		uartc: serial@7d50e000 {
++			compatible = "brcm,bcm7271-uart";
++			reg = <0x7d50e000 0x20>;
++			reg-names = "uart";
++			reg-shift = <2>;
++			reg-io-width = <4>;
++			interrupts = <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>;
++			skip-init;
++			status = "disabled";
++		};
++
++		aon_intr: interrupt-controller@7d510600 {
++			compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
++			reg = <0x7d510600 0x30>;
++			interrupts = <GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++			status = "disabled";
++		};
++
++		pinctrl_aon: pinctrl@7d510700 {
++			compatible = "brcm,bcm2712-aon-pinctrl";
++			reg = <0x7d510700 0x20>;
++
++			i2c3_m4_agpio0_pins: i2c3_m4_agpio0_pins {
++				function = "vc_i2c3";
++				pins = "aon_gpio0", "aon_gpio1";
++				bias-pull-up;
++			};
++
++			bsc_m1_agpio13_pins: bsc_m1_agpio13_pins {
++				function = "bsc_m1";
++				pins = "aon_gpio13", "aon_gpio14";
++				bias-pull-up;
++			};
++
++			bsc_pmu_sgpio4_pins: bsc_pmu_sgpio4_pins {
++				function = "avs_pmu_bsc";
++				pins = "aon_sgpio4", "aon_sgpio5";
++			};
++
++			bsc_m2_sgpio4_pins: bsc_m2_sgpio4_pins {
++				function = "bsc_m2";
++				pins = "aon_sgpio4", "aon_sgpio5";
++			};
++
++			pwm_aon_agpio1_pins: pwm_aon_agpio1_pins {
++				function = "aon_pwm";
++				pins = "aon_gpio1", "aon_gpio2";
++			};
++
++			pwm_aon_agpio4_pins: pwm_aon_agpio4_pins {
++				function = "vc_pwm0";
++				pins = "aon_gpio4", "aon_gpio5";
++			};
++
++			pwm_aon_agpio7_pins: pwm_aon_agpio7_pins {
++				function = "aon_pwm";
++				pins = "aon_gpio7", "aon_gpio9";
++			};
++		};
++
++		intc@7d517000 {
++			compatible = "brcm,bcm7271-l2-intc";
++			reg = <0x7d517000 0x10>;
++			interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++			status = "disabled";
++		};
++
++		bscc: i2c@7d517a00 {
++			compatible = "brcm,brcmstb-i2c";
++			reg = <0x7d517a00 0x58>;
++			interrupt-parent = <&bsc_aon_irq>;
++			interrupts = <0>;
++			clock-frequency = <200000>;
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		pwm_aon: pwm@7d517a80 {
++			compatible = "brcm,bcm7038-pwm";
++			reg = <0x7d517a80 0x28>;
++			#pwm-cells = <2>;
++			clocks = <&clk_27MHz>;
++		};
++
++		main_aon_irq: intc@7d517ac0 {
++			compatible = "brcm,bcm7271-l2-intc";
++			reg = <0x7d517ac0 0x10>;
++			interrupts = <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++		};
++
++		bsc_aon_irq: intc@7d517b00 {
++			compatible = "brcm,bcm7271-l2-intc";
++			reg = <0x7d517b00 0x10>;
++			interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-controller;
++			#interrupt-cells = <1>;
++		};
++
++		gio_aon: gpio@7d517c00 {
++			compatible = "brcm,brcmstb-gpio";
++			reg = <0x7d517c00 0x40>;
++			interrupt-parent = <&main_aon_irq>;
++			interrupts = <0>;
++			gpio-controller;
++			#gpio-cells = <2>;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			brcm,gpio-bank-widths = <17 6>;
++			brcm,gpio-direct;
++		};
++
++		avs_monitor: avs-monitor@7d542000 {
++			compatible = "brcm,bcm2711-avs-monitor",
++				     "syscon", "simple-mfd";
++			reg = <0x7d542000 0xf00>;
++			status = "okay";
++
++			thermal: thermal {
++				compatible = "brcm,bcm2711-thermal";
++				#thermal-sensor-cells = <0>;
++			};
++		};
++
++		bsc_pmu: i2c@7d544000 {
++			compatible = "brcm,brcmstb-i2c";
++			reg = <0x7d544000 0x58>;
++			interrupt-parent = <&bsc_aon_irq>;
++			interrupts = <1>;
++			clock-frequency = <200000>;
++			status = "disabled";
++		};
++
++		hdmi0: hdmi@7ef00700 {
++			compatible = "brcm,bcm2712-hdmi0";
++			reg = <0x7c701400 0x300>,
++			      <0x7c701000 0x200>,
++			      <0x7c701d00 0x300>,
++			      <0x7c702000 0x80>,
++			      <0x7c703800 0x200>,
++			      <0x7c704000 0x800>,
++			      <0x7c700100 0x80>,
++			      <0x7d510800 0x100>,
++			      <0x7c720000 0x100>;
++			reg-names = "hdmi",
++				    "dvp",
++				    "phy",
++				    "rm",
++				    "packet",
++				    "metadata",
++				    "csc",
++				    "cec",
++				    "hd";
++			resets = <&dvp 1>;
++			interrupt-parent = <&aon_intr>;
++			interrupts = <1>, <2>, <3>,
++				     <7>, <8>;
++			interrupt-names = "cec-tx", "cec-rx", "cec-low",
++					  "hpd-connected", "hpd-removed";
++			ddc = <&ddc0>;
++			dmas = <&dma32 10>;
++			dma-names = "audio-rx";
++			status = "disabled";
++		};
++
++		hdmi1: hdmi@7ef05700 {
++			compatible = "brcm,bcm2712-hdmi1";
++			reg = <0x7c706400 0x300>,
++			      <0x7c706000 0x200>,
++			      <0x7c706d00 0x300>,
++			      <0x7c707000 0x80>,
++			      <0x7c708800 0x200>,
++			      <0x7c709000 0x800>,
++			      <0x7c700180 0x80>,
++			      <0x7d511000 0x100>,
++			      <0x7c720000 0x100>;
++			reg-names = "hdmi",
++				    "dvp",
++				    "phy",
++				    "rm",
++				    "packet",
++				    "metadata",
++				    "csc",
++				    "cec",
++				    "hd";
++			ddc = <&ddc1>;
++			resets = <&dvp 2>;
++			interrupt-parent = <&aon_intr>;
++			interrupts = <11>, <12>, <13>,
++				     <14>, <15>;
++			interrupt-names = "cec-tx", "cec-rx", "cec-low",
++					  "hpd-connected", "hpd-removed";
++			dmas = <&dma32 17>;
++			dma-names = "audio-rx";
++			status = "disabled";
++		};
++
++		sound: sound {
++		};
++	};
++
++	arm-pmu {
++		compatible = "arm,cortex-a76-pmu";
++		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
++			<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
++			<GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
++			<GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
++		interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>;
++	};
++
++	timer {
++		compatible = "arm,armv8-timer";
++		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
++					  IRQ_TYPE_LEVEL_LOW)>,
++			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
++					  IRQ_TYPE_LEVEL_LOW)>,
++			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
++					  IRQ_TYPE_LEVEL_LOW)>,
++			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
++					  IRQ_TYPE_LEVEL_LOW)>;
++		/* This only applies to the ARMv7 stub */
++		arm,cpu-registers-not-fw-configured;
++	};
++
++	cpus: cpus {
++		#address-cells = <1>;
++		#size-cells = <0>;
++		enable-method = "brcm,bcm2836-smp"; // for ARM 32-bit
++
++		cpu0: cpu@0 {
++			device_type = "cpu";
++			compatible = "arm,cortex-a76";
++			reg = <0x000>;
++			enable-method = "psci";
++			next-level-cache = <&l2_cache>;
++		};
++
++		cpu1: cpu@1 {
++			device_type = "cpu";
++			compatible = "arm,cortex-a76";
++			reg = <0x100>;
++			enable-method = "psci";
++			next-level-cache = <&l2_cache>;
++		};
++
++		cpu2: cpu@2 {
++			device_type = "cpu";
++			compatible = "arm,cortex-a76";
++			reg = <0x200>;
++			enable-method = "psci";
++			next-level-cache = <&l2_cache>;
++		};
++
++		cpu3: cpu@3 {
++			device_type = "cpu";
++			compatible = "arm,cortex-a76";
++			reg = <0x300>;
++			enable-method = "psci";
++			next-level-cache = <&l2_cache>;
++		};
++
++		l2_cache: l2-cache {
++			compatible = "cache";
++			next-level-cache = <&l3_cache>;
++		};
++
++		l3_cache: l3-cache {
++			compatible = "cache";
++		};
++	};
++
++	psci {
++		method = "smc";
++		compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";
++		cpu_on = <0xc4000003>;
++		cpu_suspend = <0xc4000001>;
++		cpu_off = <0x84000002>;
++	};
++
++	axi: axi {
++		compatible = "simple-bus";
++		#address-cells = <2>;
++		#size-cells = <2>;
++
++		ranges = <0x00 0x00000000  0x00 0x00000000  0x10 0x00000000>,
++			 <0x10 0x00000000  0x10 0x00000000  0x01 0x00000000>,
++			 <0x14 0x00000000  0x14 0x00000000  0x04 0x00000000>,
++			 <0x18 0x00000000  0x18 0x00000000  0x04 0x00000000>,
++			 <0x1c 0x00000000  0x1c 0x00000000  0x04 0x00000000>;
++
++		dma-ranges = <0x00 0x00000000  0x00 0x00000000  0x10 0x00000000>,
++			     <0x10 0x00000000  0x10 0x00000000  0x01 0x00000000>,
++			     <0x14 0x00000000  0x14 0x00000000  0x04 0x00000000>,
++			     <0x18 0x00000000  0x18 0x00000000  0x04 0x00000000>,
++			     <0x1c 0x00000000  0x1c 0x00000000  0x04 0x00000000>;
++
++		vc4: gpu {
++			compatible = "brcm,bcm2712-vc6";
++		};
++
++		iommu2: iommu@5100 {
++			/* IOMMU2 for PISP-BE, HEVC; and (unused) H264 accelerators */
++			compatible = "brcm,bcm2712-iommu";
++			reg = <0x10 0x5100  0x0 0x80>;
++			cache = <&iommuc>;
++			#iommu-cells = <0>;
++		};
++
++		iommu4: iommu@5200 {
++			/* IOMMU4 for HVS, MPL/TXP; and (unused) Unicam, PISP-FE, MiniBVN */
++			compatible = "brcm,bcm2712-iommu";
++			reg = <0x10 0x5200  0x0 0x80>;
++			cache = <&iommuc>;
++			#iommu-cells = <0>;
++			#interconnect-cells = <0>;
++		};
++
++		iommu5: iommu@5280 {
++			/* IOMMU5 for PCIe2 (RP1); and (unused) BSTM */
++			compatible = "brcm,bcm2712-iommu";
++			reg = <0x10 0x5280  0x0 0x80>;
++			cache = <&iommuc>;
++			#iommu-cells = <0>;
++			dma-iova-offset = <0x10 0x00000000>; // HACK for RP1 masters over PCIe
++		};
++
++		iommuc: iommuc@5b00 {
++			compatible = "brcm,bcm2712-iommuc";
++			reg = <0x10 0x5b00  0x0 0x80>;
++		};
++
++		dma32: dma@10000 {
++			compatible = "brcm,bcm2712-dma";
++			reg = <0x10 0x00010000 0 0x600>;
++			interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-names = "dma0",
++					  "dma1",
++					  "dma2",
++					  "dma3",
++					  "dma4",
++					  "dma5";
++			#dma-cells = <1>;
++			brcm,dma-channel-mask = <0x0035>;
++		};
++
++		dma40: dma@10600 {
++			compatible = "brcm,bcm2712-dma";
++			reg = <0x10 0x00010600 0 0x600>;
++			interrupts =
++				<GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, /* dma4 6 */
++				<GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, /* dma4 7 */
++				<GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, /* dma4 8 */
++				<GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 9 */
++				<GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 10 */
++				<GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; /* dma4 11 */
++			interrupt-names = "dma6",
++					  "dma7",
++					  "dma8",
++					  "dma9",
++					  "dma10",
++					  "dma11";
++			#dma-cells = <1>;
++			brcm,dma-channel-mask = <0x0fc0>;
++		};
++
++		// Single-lane Gen3 PCIe
++		// Outbound window at 0x14_000000-0x17_ffffff
++		pcie0: pcie@100000 {
++			compatible = "brcm,bcm2712-pcie";
++			reg = <0x10 0x00100000  0x0 0x9310>;
++			device_type = "pci";
++			max-link-speed = <2>;
++			#address-cells = <3>;
++			#interrupt-cells = <1>;
++			#size-cells = <2>;
++			/*
++			 * Unused interrupts:
++			 * 208: AER
++			 * 215: NMI
++			 * 216: PME
++			 */
++			interrupt-parent = <&gicv2>;
++			interrupts = <GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-names = "pcie", "msi";
++			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++			interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 209
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 2 &gicv2 GIC_SPI 210
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 3 &gicv2 GIC_SPI 211
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 4 &gicv2 GIC_SPI 212
++							IRQ_TYPE_LEVEL_HIGH>;
++			resets = <&bcm_reset 5>, <&bcm_reset 42>, <&pcie_rescal>;
++			reset-names = "swinit", "bridge", "rescal";
++			msi-controller;
++			msi-parent = <&pcie0>;
++
++			ranges = <0x02000000 0x00 0x00000000
++				  0x17 0x00000000
++				  0x0 0xfffffffc>,
++				 <0x43000000 0x04 0x00000000
++				  0x14 0x00000000
++				  0x3 0x00000000>;
++
++			dma-ranges = <0x43000000 0x10 0x00000000
++				      0x00 0x00000000
++				      0x10 0x00000000>;
++
++			status = "disabled";
++		};
++
++		// Single-lane Gen3 PCIe
++		// Outbound window at 0x18_000000-0x1b_ffffff
++		pcie1: pcie@110000 {
++			compatible = "brcm,bcm2712-pcie";
++			reg = <0x10 0x00110000  0x0 0x9310>;
++			device_type = "pci";
++			max-link-speed = <2>;
++			#address-cells = <3>;
++			#interrupt-cells = <1>;
++			#size-cells = <2>;
++			/*
++			 * Unused interrupts:
++			 * 218: AER
++			 * 225: NMI
++			 * 226: PME
++			 */
++			interrupt-parent = <&gicv2>;
++			interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-names = "pcie", "msi";
++			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++			interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 219
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 2 &gicv2 GIC_SPI 220
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 3 &gicv2 GIC_SPI 221
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 4 &gicv2 GIC_SPI 222
++							IRQ_TYPE_LEVEL_HIGH>;
++			resets = <&bcm_reset 7>, <&bcm_reset 43>, <&pcie_rescal>;
++			reset-names = "swinit", "bridge", "rescal";
++			msi-controller;
++			msi-parent = <&mip1>;
++
++			ranges = <0x02000000 0x00 0x00000000
++				  0x1b 0x00000000
++				  0x00 0xfffffffc>,
++				 <0x43000000 0x04 0x00000000
++				  0x18 0x00000000
++				  0x03 0x00000000>;
++
++			dma-ranges = <0x03000000 0x10 0x00000000
++				      0x00 0x00000000
++				      0x10 0x00000000>;
++
++			brcm,enable-l1ss;
++			status = "disabled";
++		};
++
++		pcie_rescal: reset-controller@119500 {
++			compatible = "brcm,bcm7216-pcie-sata-rescal";
++			reg = <0x10 0x00119500  0x0 0x10>;
++			#reset-cells = <0>;
++		};
++
++		// Quad-lane Gen3 PCIe
++		// Outbound window at 0x1c_000000-0x1f_ffffff
++		pcie2: pcie@120000 {
++			compatible = "brcm,bcm2712-pcie";
++			reg = <0x10 0x00120000  0x0 0x9310>;
++			device_type = "pci";
++			max-link-speed = <2>;
++			#address-cells = <3>;
++			#interrupt-cells = <1>;
++			#size-cells = <2>;
++			/*
++			 * Unused interrupts:
++			 * 228: AER
++			 * 235: NMI
++			 * 236: PME
++			 */
++			interrupt-parent = <&gicv2>;
++			interrupts = <GIC_SPI 233 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH>;
++			interrupt-names = "pcie", "msi";
++			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++			interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 229
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 2 &gicv2 GIC_SPI 230
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 3 &gicv2 GIC_SPI 231
++							IRQ_TYPE_LEVEL_HIGH>,
++					<0 0 0 4 &gicv2 GIC_SPI 232
++							IRQ_TYPE_LEVEL_HIGH>;
++			resets = <&bcm_reset 32>, <&bcm_reset 44>, <&pcie_rescal>;
++			reset-names = "swinit", "bridge", "rescal";
++			msi-controller;
++			msi-parent = <&mip0>;
++
++			// ~4GB, 32-bit, not-prefetchable at PCIe 00_00000000
++			ranges = <0x02000000 0x00 0x00000000
++				  0x1f 0x00000000
++				  0x0 0xfffffffc>,
++			// 12GB, 64-bit, prefetchable at PCIe 04_00000000
++				 <0x43000000 0x04 0x00000000
++				  0x1c 0x00000000
++				  0x03 0x00000000>;
++
++			// 64GB system RAM space at PCIe 10_00000000
++			dma-ranges = <0x02000000 0x00 0x00000000
++				      0x1f 0x00000000
++				      0x00 0x00400000>,
++				     <0x43000000 0x10 0x00000000
++				      0x00 0x00000000
++				      0x10 0x00000000>;
++
++			brcm,enable-mps-rcb;
++			brcm,enable-l1ss;
++			status = "disabled";
++		};
++
++		mip0: msi-controller@130000 {
++			compatible = "brcm,bcm2712-mip-intc";
++			reg = <0x10 0x00130000  0x0 0xc0>;
++			msi-controller;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			brcm,msi-base-spi = <128>;
++			brcm,msi-num-spis = <64>;
++			brcm,msi-offset = <0>;
++			brcm,msi-pci-addr = <0xff 0xfffff000>;
++		};
++
++		mip1: msi-controller@131000 {
++			compatible = "brcm,bcm2712-mip-intc";
++			reg = <0x10 0x00131000  0x0 0xc0>;
++			msi-controller;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			brcm,msi-base-spi = <247>;
++			/* Actually 20 total, but the others are
++			 * both sparse and non-consecutive */
++			brcm,msi-num-spis = <8>;
++			brcm,msi-offset = <8>;
++			brcm,msi-pci-addr = <0xff 0xffffe000>;
++		};
++
++		genet: ethernet@1300000 {
++			compatible = "brcm,bcm2711-genet-v5";
++			reg = <0x10 0x01300000  0x0 0x20010>;
++			#address-cells = <0x1>;
++			#size-cells = <0x0>;
++			interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
++			status = "disabled";
++			phy-mode = "rgmii";
++			fixed-link = <0x0 0x1 0x3e8 0x0 0x0>;
++	                  phy-speed = <0x3e8>;
++	                  phy-id = <0x101>;
++	                  phy-type = <0x6>;
++	                  local-mac-address = [ 00 10 18 d8 45 de ];
++	                  device_type = "network";
++
++			genet_mdio: mdio@e14 {
++				compatible = "brcm,genet-mdio-v5";
++				reg = <0xe14 0x8>;
++				#address-cells = <0x1>;
++				#size-cells = <0x0>;
++			};
++		};
++
++		syscon_piarbctl: syscon@400018 {
++			compatible = "brcm,syscon-piarbctl", "syscon", "simple-mfd";
++			reg = <0x10 0x00400018  0x0 0x18>;
++		};
++
++		rpivid: codec@800000 {
++			compatible = "raspberrypi,rpivid-vid-decoder";
++			reg = <0x10 0x00800000  0x0 0x10000>, /* HEVC */
++			      <0x10 0x00840000  0x0 0x1000>;  /* INTC */
++			reg-names = "hevc",
++				    "intc";
++
++			interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&firmware_clocks 11>;
++			clock-names = "hevc";
++			status = "disabled";
++		};
++
++		sdio1: mmc@fff000 {
++			compatible = "brcm,bcm2712-sdhci";
++			reg = <0x10 0x00fff000  0x0 0x260>,
++			      <0x10 0x00fff400  0x0 0x200>,
++			      <0x10 0x015040b0  0x0 0x4>,  // Bus isolation control
++			      <0x10 0x015200f0  0x0 0x24>; // LCPLL control misc0-8
++			reg-names = "host", "cfg", "busisol", "lcpll";
++			interrupts = <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_emmc2>;
++			sdhci-caps-mask = <0x0000C000 0x0>;
++			sdhci-caps = <0x0 0x0>;
++			supports-cqe;
++			mmc-ddr-3_3v;
++		};
++
++		sdio2: mmc@1100000 {
++			compatible = "brcm,bcm2712-sdhci";
++			reg = <0x10 0x01100000  0x0 0x260>,
++			      <0x10 0x01100400  0x0 0x200>;
++			reg-names = "host", "cfg";
++			interrupts = <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_emmc2>;
++			sdhci-caps-mask = <0x0000C000 0x0>;
++			sdhci-caps = <0x0 0x0>;
++			supports-cqe;
++			mmc-ddr-3_3v;
++			status = "disabled";
++		};
++
++		sdio0: mmc@1108000 {
++			compatible = "brcm,bcm2711-emmc2";
++			reg = <0x10 0x01108000  0x0 0x100>;
++			interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clk_emmc2>;
++			mmc-ddr-3_3v;
++			status = "disabled";
++		};
++
++		bcm_reset: reset-controller@1504318 {
++			compatible = "brcm,brcmstb-reset";
++			reg = <0x10 0x01504318  0x0 0x30>;
++			#reset-cells = <1>;
++		};
++
++		v3d: v3d@2000000 {
++			compatible = "brcm,2712-v3d";
++			reg = <0x10 0x02000000  0x0 0x4000>,
++			      <0x10 0x02008000  0x0 0x6000>;
++			reg-names = "hub", "core0";
++
++			power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
++			resets = <&pm BCM2835_RESET_V3D>;
++			clocks = <&firmware_clocks 5>;
++			clocks-names = "v3d";
++			interrupts = <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>,
++				     <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>;
++			status = "disabled";
++		};
++
++		gicv2: interrupt-controller@7fff9000 {
++			interrupt-controller;
++			#interrupt-cells = <3>;
++			compatible = "arm,gic-400";
++			reg =	<0x10 0x7fff9000  0x0 0x1000>,
++				<0x10 0x7fffa000  0x0 0x2000>,
++				<0x10 0x7fffc000  0x0 0x2000>,
++				<0x10 0x7fffe000  0x0 0x2000>;
++			interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) |
++						 IRQ_TYPE_LEVEL_HIGH)>;
++		};
++
++		pisp_be: pisp_be@880000  {
++			compatible = "raspberrypi,pispbe";
++			reg = <0x10 0x00880000  0x0 0x4000>;
++			interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&firmware_clocks 7>;
++			clocks-names = "isp_be";
++			status = "okay";
++			iommus = <&iommu2>;
++		};
++	};
++
++	clocks {
++		/* The oscillator is the root of the clock tree. */
++		clk_osc: clk-osc {
++			compatible = "fixed-clock";
++			#clock-cells = <0>;
++			clock-output-names = "osc";
++			clock-frequency = <54000000>;
++		};
++
++		clk_usb: clk-usb {
++			compatible = "fixed-clock";
++			#clock-cells = <0>;
++			clock-output-names = "otg";
++			clock-frequency = <480000000>;
++		};
++
++		clk_vpu: clk_vpu {
++			#clock-cells = <0>;
++			compatible = "fixed-clock";
++			clock-frequency = <750000000>;
++			clock-output-names = "vpu-clock";
++		};
++
++		clk_uart: clk_uart {
++			#clock-cells = <0>;
++			compatible = "fixed-clock";
++			clock-frequency = <9216000>;
++			clock-output-names = "uart-clock";
++		};
++
++		clk_emmc2: clk_emmc2 {
++			#clock-cells = <0>;
++			compatible = "fixed-clock";
++			clock-frequency = <54000000>;
++			clock-output-names = "emmc2-clock";
++		};
++	};
++
++	usbphy: phy {
++		compatible = "usb-nop-xceiv";
++		#phy-cells = <0>;
++	};
++};
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -49,8 +49,10 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	dionaudio-loco.dtbo \
+ 	dionaudio-loco-v2.dtbo \
+ 	disable-bt.dtbo \
++	disable-bt-pi5.dtbo \
+ 	disable-emmc2.dtbo \
+ 	disable-wifi.dtbo \
++	disable-wifi-pi5.dtbo \
+ 	dpi18.dtbo \
+ 	dpi18cpadhi.dtbo \
+ 	dpi24.dtbo \
+@@ -106,8 +108,12 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	i2c-rtc-gpio.dtbo \
+ 	i2c-sensor.dtbo \
+ 	i2c0.dtbo \
++	i2c0-pi5.dtbo \
+ 	i2c1.dtbo \
++	i2c1-pi5.dtbo \
++	i2c2-pi5.dtbo \
+ 	i2c3.dtbo \
++	i2c3-pi5.dtbo \
+ 	i2c4.dtbo \
+ 	i2c5.dtbo \
+ 	i2c6.dtbo \
+@@ -150,10 +156,15 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	media-center.dtbo \
+ 	merus-amp.dtbo \
+ 	midi-uart0.dtbo \
++	midi-uart0-pi5.dtbo \
+ 	midi-uart1.dtbo \
++	midi-uart1-pi5.dtbo \
+ 	midi-uart2.dtbo \
++	midi-uart2-pi5.dtbo \
+ 	midi-uart3.dtbo \
++	midi-uart3-pi5.dtbo \
+ 	midi-uart4.dtbo \
++	midi-uart4-pi5.dtbo \
+ 	midi-uart5.dtbo \
+ 	minipitft13.dtbo \
+ 	miniuart-bt.dtbo \
+@@ -231,14 +242,20 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	spi1-2cs.dtbo \
+ 	spi1-3cs.dtbo \
+ 	spi2-1cs.dtbo \
++	spi2-1cs-pi5.dtbo \
+ 	spi2-2cs.dtbo \
++	spi2-2cs-pi5.dtbo \
+ 	spi2-3cs.dtbo \
+ 	spi3-1cs.dtbo \
++	spi3-1cs-pi5.dtbo \
+ 	spi3-2cs.dtbo \
++	spi3-2cs-pi5.dtbo \
+ 	spi4-1cs.dtbo \
+ 	spi4-2cs.dtbo \
+ 	spi5-1cs.dtbo \
++	spi5-1cs-pi5.dtbo \
+ 	spi5-2cs.dtbo \
++	spi5-2cs-pi5.dtbo \
+ 	spi6-1cs.dtbo \
+ 	spi6-2cs.dtbo \
+ 	ssd1306.dtbo \
+@@ -253,10 +270,15 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	tpm-slb9670.dtbo \
+ 	tpm-slb9673.dtbo \
+ 	uart0.dtbo \
++	uart0-pi5.dtbo \
+ 	uart1.dtbo \
++	uart1-pi5.dtbo \
+ 	uart2.dtbo \
++	uart2-pi5.dtbo \
+ 	uart3.dtbo \
++	uart3-pi5.dtbo \
+ 	uart4.dtbo \
++	uart4-pi5.dtbo \
+ 	uart5.dtbo \
+ 	udrc.dtbo \
+ 	ugreen-dabboard.dtbo \
+@@ -276,6 +298,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	vc4-kms-kippah-7inch.dtbo \
+ 	vc4-kms-v3d.dtbo \
+ 	vc4-kms-v3d-pi4.dtbo \
++	vc4-kms-v3d-pi5.dtbo \
+ 	vc4-kms-vga666.dtbo \
+ 	vga666.dtbo \
+ 	vl805.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -151,6 +151,9 @@ Params:
+                                 bdaddr=06:05:04:03:02:01
+                                 will set the BDADDR to 01:02:03:04:05:06.
+ 
++        button_debounce         Set the debounce delay (in ms) on the power/
++                                shutdown button (default 50ms)
++
+         cam0_reg                Enables CAM 0 regulator.
+                                 Only required on CM1 & 3.
+ 
+@@ -167,6 +170,9 @@ Params:
+                                 Default of GPIO expander 5 on CM4, but override
+                                 switches to normal GPIO.
+ 
++        cooling_fan             Enables the Pi 5 cooling fan (enabled
++                                automatically by the firmware)
++
+         eee                     Enable Energy Efficient Ethernet support for
+                                 compatible devices (default "on"). See also
+                                 "tx_lpi_timer". Pi3B+ only.
+@@ -206,23 +212,29 @@ Params:
+         hdmi                    Set to "off" to disable the HDMI interface
+                                 (default "on")
+ 
++        i2c                     An alias for i2c_arm
++
+         i2c_arm                 Set to "on" to enable the ARM's i2c interface
+                                 (default "off")
+ 
++        i2c_arm_baudrate        Set the baudrate of the ARM's i2c interface
++                                (default "100000")
++
++        i2c_baudrate            An alias for i2c_arm_baudrate
++
++        i2c_csi_dsi             Set to "on" to enable the i2c_csi_dsi interface
++
++        i2c_csi_dsi0            Set to "on" to enable the i2c_csi_dsi0 interface
++
++        i2c_csi_dsi1            Set to "on" to enable the i2c_csi_dsi1 interface
++
+         i2c_vc                  Set to "on" to enable the i2c interface
+                                 usually reserved for the VideoCore processor
+                                 (default "off")
+ 
+-        i2c                     An alias for i2c_arm
+-
+-        i2c_arm_baudrate        Set the baudrate of the ARM's i2c interface
+-                                (default "100000")
+-
+         i2c_vc_baudrate         Set the baudrate of the VideoCore i2c interface
+                                 (default "100000")
+ 
+-        i2c_baudrate            An alias for i2c_arm_baudrate
+-
+         i2s                     Set to "on" to enable the i2s interface
+                                 (default "off")
+ 
+@@ -237,11 +249,23 @@ Params:
+         krnbt_baudrate          Set the baudrate of the PL011 UART when used
+                                 with krnbt=on
+ 
++        nvme                    Alias for "pciex1" (2712 only)
++
+         pcie                    Set to "off" to disable the PCIe interface
+                                 (default "on")
+                                 (2711 only, but not applicable on CM4S)
+                                 N.B. USB-A ports on 4B are subsequently disabled
+ 
++        pciex1                  Set to "on" to enable the external PCIe link
++                                (2712 only, default "off")
++
++        pciex1_gen              Sets the PCIe "GEN"/speed for the external PCIe
++                                link (2712 only, default "2")
++
++        pciex1_no_l0s           Set to "on" to disable ASPM L0s on the external
++                                PCIe link for devices that have broken
++                                implementations (2712 only, default "off")
++
+         spi                     Set to "on" to enable the spi interfaces
+                                 (default "off")
+ 
+@@ -252,6 +276,11 @@ Params:
+         random                  Set to "on" to enable the hardware random
+                                 number generator (default "on")
+ 
++        rtc_bbat_vchg           Set the RTC backup battery charging voltage in
++                                microvolts. If set to 0 or not specified, the
++                                trickle charger is disabled.
++                                (2712 only, default "0")
++
+         sd                      Set to "off" to disable the SD card (or eMMC on
+                                 non-lite SKU of CM4).
+                                 (default "on")
+@@ -276,18 +305,30 @@ Params:
+         sdio_overclock          Clock (in MHz) to use when the MMC framework
+                                 requests 50MHz for the SDIO/WLAN interface.
+ 
++        suspend                 Make the power button trigger a suspend rather
++                                than a power-off (2712 only, default "off")
++
+         tx_lpi_timer            Set the delay in microseconds between going idle
+                                 and entering the low power state (default 600).
+                                 Requires EEE to be enabled - see "eee".
+ 
+         uart0                   Set to "off" to disable uart0 (default "on")
+ 
++        uart0_console           Move the kernel boot console to UART0 on pins
++                                6, 8 and 10 of the 40-way header (2712 only,
++                                default "off")
++
+         uart1                   Set to "on" or "off" to enable or disable uart1
+                                 (default varies)
+ 
+         watchdog                Set to "on" to enable the hardware watchdog
+                                 (default "off")
+ 
++        wifiaddr                Set an alternative WiFi MAC address.
++                                The value should be a 6-byte hexadecimal value,
++                                with or without colon separators, written in the
++                                natural (big-endian) order.
++
+         act_led_trigger         Choose which activity the LED tracks.
+                                 Use "heartbeat" for a nice load indicator.
+                                 (default "mmc")
+@@ -919,14 +960,16 @@ Params: 24db_digital_gain       Allow ga
+ 
+ 
+ Name:   disable-bt
+-Info:   Disable onboard Bluetooth on Pi 3B, 3B+, 3A+, 4B and Zero W, restoring
+-        UART0/ttyAMA0 over GPIOs 14 & 15.
+-        N.B. To disable the systemd service that initialises the modem so it
+-        doesn't use the UART, use 'sudo systemctl disable hciuart'.
++Info:   Disable onboard Bluetooth on Bluetooth-capable Raspberry Pis. On Pis
++        prior to Pi 5 this restores UART0/ttyAMA0 over GPIOs 14 & 15.
+ Load:   dtoverlay=disable-bt
+ Params: <None>
+ 
+ 
++Name:   disable-bt-pi5
++Info:   See disable-bt
++
++
+ Name:   disable-emmc2
+ Info:   Disable EMMC2 controller on BCM2711.
+         The allows the onboard EMMC storage on Compute Module 4 to be disabled
+@@ -936,11 +979,15 @@ Params: <None>
+ 
+ 
+ Name:   disable-wifi
+-Info:   Disable onboard WLAN on Pi 3B, 3B+, 3A+, 4B and Zero W.
++Info:   Disable onboard WLAN on WiFi-capable Raspberry Pis.
+ Load:   dtoverlay=disable-wifi
+ Params: <None>
+ 
+ 
++Name:   disable-wifi-pi5
++Info:   See disable-wifi
++
++
+ Name:   dpi18
+ Info:   Overlay for a generic 18-bit DPI display
+         This uses GPIOs 0-21 (so no I2C, uart etc.), and activates the output
+@@ -2233,6 +2280,15 @@ Info:   Deprecated, legacy version of i2
+ Load:   <Deprecated>
+ 
+ 
++Name:   i2c0-pi5
++Info:   Enable i2c0 (Pi 5 only)
++Load:   dtoverlay=i2c0-pi5,<param>=<val>
++Params: pins_0_1                Use GPIOs 0 and 1 (default)
++        pins_8_9                Use GPIOs 8 and 9
++        baudrate                Set the baudrate for the interface (default
++                                "100000")
++
++
+ Name:   i2c1
+ Info:   Change i2c1 pin usage. Not all pin combinations are usable on all
+         platforms - platforms other then Compute Modules can only use this
+@@ -2249,6 +2305,24 @@ Info:   Deprecated, legacy version of i2
+ Load:   <Deprecated>
+ 
+ 
++Name:   i2c1-pi5
++Info:   Enable i2c1 (Pi 5 only)
++Load:   dtoverlay=i2c1-pi5,<param>=<val>
++Params: pins_2_3                Use GPIOs 2 and 3 (default)
++        pins_10_11              Use GPIOs 10 and 11
++        baudrate                Set the baudrate for the interface (default
++                                "100000")
++
++
++Name:   i2c2-pi5
++Info:   Enable i2c2 (Pi 5 only)
++Load:   dtoverlay=i2c2-pi5,<param>=<val>
++Params: pins_4_5                Use GPIOs 4 and 5 (default)
++        pins_12_13              Use GPIOs 12 and 13
++        baudrate                Set the baudrate for the interface (default
++                                "100000")
++
++
+ Name:   i2c3
+ Info:   Enable the i2c3 bus. BCM2711 only.
+ Load:   dtoverlay=i2c3,<param>
+@@ -2258,6 +2332,16 @@ Params: pins_2_3                Use GPIO
+                                 "100000")
+ 
+ 
++Name:   i2c3-pi5
++Info:   Enable i2c3 (Pi 5 only)
++Load:   dtoverlay=i2c3-pi5,<param>=<val>
++Params: pins_6_7                Use GPIOs 6 and 7 (default)
++        pins_14_15              Use GPIOs 14 and 15
++        pins_22_23              Use GPIOs 22 and 23
++        baudrate                Set the baudrate for the interface (default
++                                "100000")
++
++
+ Name:   i2c4
+ Info:   Enable the i2c4 bus. BCM2711 only.
+ Load:   dtoverlay=i2c4,<param>
+@@ -2869,6 +2953,10 @@ Load:   dtoverlay=midi-uart0
+ Params: <None>
+ 
+ 
++Name:   midi-uart0-pi5
++Info:   See midi-uart0 (this is the Pi 5 version)
++
++
+ Name:   midi-uart1
+ Info:   Configures UART1 (ttyS0) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+@@ -2876,29 +2964,45 @@ Load:   dtoverlay=midi-uart1
+ Params: <None>
+ 
+ 
++Name:   midi-uart1-pi5
++Info:   See midi-uart1 (this is the Pi 5 version)
++
++
+ Name:   midi-uart2
+-Info:   Configures UART2 (ttyAMA1) so that a requested 38.4kbaud actually gets
++Info:   Configures UART2 (ttyAMA2) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+ Load:   dtoverlay=midi-uart2
+ Params: <None>
+ 
+ 
++Name:   midi-uart2-pi5
++Info:   See midi-uart2 (this is the Pi 5 version)
++
++
+ Name:   midi-uart3
+-Info:   Configures UART3 (ttyAMA2) so that a requested 38.4kbaud actually gets
++Info:   Configures UART3 (ttyAMA3) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+ Load:   dtoverlay=midi-uart3
+ Params: <None>
+ 
+ 
++Name:   midi-uart3-pi5
++Info:   See midi-uart3 (this is the Pi 5 version)
++
++
+ Name:   midi-uart4
+-Info:   Configures UART4 (ttyAMA3) so that a requested 38.4kbaud actually gets
++Info:   Configures UART4 (ttyAMA4) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+ Load:   dtoverlay=midi-uart4
+ Params: <None>
+ 
+ 
++Name:   midi-uart4-pi5
++Info:   See midi-uart4 (this is the Pi 5 version)
++
++
+ Name:   midi-uart5
+-Info:   Configures UART5 (ttyAMA4) so that a requested 38.4kbaud actually gets
++Info:   Configures UART5 (ttyAMA5) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+ Load:   dtoverlay=midi-uart5
+ Params: <None>
+@@ -3921,105 +4025,131 @@ Name:   spi1-1cs
+ Info:   Enables spi1 with a single chip select (CS) line and associated spidev
+         dev node. The gpio pin number for the CS line and spidev device node
+         creation are configurable.
+-        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+-              A+, B+, Zero and PI2 B; as well as the Compute Module.
++        N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load:   dtoverlay=spi1-1cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.0 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi1-2cs
+ Info:   Enables spi1 with two chip select (CS) lines and associated spidev
+         dev nodes. The gpio pin numbers for the CS lines and spidev device node
+         creation are configurable.
+-        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+-              A+, B+, Zero and PI2 B; as well as the Compute Module.
++        N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load:   dtoverlay=spi1-2cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.0 (default
+-                                is 'okay' or enabled).
+-        cs1_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.1 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi1-3cs
+ Info:   Enables spi1 with three chip select (CS) lines and associated spidev
+         dev nodes. The gpio pin numbers for the CS lines and spidev device node
+         creation are configurable.
+-        N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+-              A+, B+, Zero and PI2 B; as well as the Compute Module.
++        N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load:   dtoverlay=spi1-3cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+         cs2_pin                 GPIO pin for CS2 (default 16 - BCM SPI1_CE2).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.0 (default
+-                                is 'okay' or enabled).
+-        cs1_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.1 (default
+-                                is 'okay' or enabled).
+-        cs2_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs2_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev1.2 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi2-1cs
+-Info:   Enables spi2 with a single chip select (CS) line and associated spidev
+-        dev node. The gpio pin number for the CS line and spidev device node
+-        creation are configurable.
+-        N.B.: spi2 is only accessible with the Compute Module.
++Info:   Enables spi2 on GPIOs 40-42 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin number for the CS line and
++        spidev device node creation are configurable. spi2-2cs-pi5 is
++        substituted on a Pi 5.
++        N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load:   dtoverlay=spi2-1cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.0 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
++
++
++Name:   spi2-1cs-pi5
++Info:   Enables spi2 on GPIOs 1-3 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin number for the CS line and
++        spidev device node creation are configurable. Pi 5 only.
++Load:   dtoverlay=spi2-1cs-pi5,<param>=<val>
++Params: cs0_pin                 GPIO pin for CS0 (default 0).
++        cs0_spidev              Set to 'off' to stop the creation of a
++                                userspace device node /dev/spidev2.0 (default
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi2-2cs
+-Info:   Enables spi2 with two chip select (CS) lines and associated spidev
+-        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable.
+-        N.B.: spi2 is only accessible with the Compute Module.
++Info:   Enables spi2 on GPIOs 40-42 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. spi2-2cs-pi5 is
++        substituted on a Pi 5.
++        N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load:   dtoverlay=spi2-2cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
++                                userspace device node /dev/spidev2.0 (default
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to stop the creation of a
++                                userspace device node /dev/spidev2.1 (default
++                                is 'on' or enabled).
++
++
++Name:   spi2-2cs-pi5
++Info:   Enables spi2 on GPIOs 1-3 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. Pi 5 only.
++Load:   dtoverlay=spi2-2cs-pi5,<param>=<val>
++Params: cs0_pin                 GPIO pin for CS0 (default 0).
++        cs1_pin                 GPIO pin for CS1 (default 24).
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.0 (default
+-                                is 'okay' or enabled).
+-        cs1_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.1 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi2-3cs
+-Info:   Enables spi2 with three chip select (CS) lines and associated spidev
+-        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable.
+-        N.B.: spi2 is only accessible with the Compute Module.
++Info:   Enables spi2 on GPIOs 40-42 with three chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable.
++        N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load:   dtoverlay=spi2-3cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+         cs2_pin                 GPIO pin for CS2 (default 45 - BCM SPI2_CE2).
+-        cs0_spidev              Set to 'disabled' to stop the creation of a
++        cs0_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.0 (default
+-                                is 'okay' or enabled).
+-        cs1_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.1 (default
+-                                is 'okay' or enabled).
+-        cs2_spidev              Set to 'disabled' to stop the creation of a
++                                is 'on' or enabled).
++        cs2_spidev              Set to 'off' to stop the creation of a
+                                 userspace device node /dev/spidev2.2 (default
+-                                is 'okay' or enabled).
++                                is 'on' or enabled).
+ 
+ 
+ Name:   spi3-1cs
+-Info:   Enables spi3 with a single chip select (CS) line and associated spidev
+-        dev node. The gpio pin number for the CS line and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi3 on GPIOs 1-3 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin number for the CS line and
++        spidev device node creation are configurable. BCM2711 only,
++        spi3-1cs-pi5 is substituted on Pi 5.
+ Load:   dtoverlay=spi3-1cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+         cs0_spidev              Set to 'off' to prevent the creation of a
+@@ -4027,10 +4157,22 @@ Params: cs0_pin                 GPIO pin
+                                 is 'on' or enabled).
+ 
+ 
++Name:   spi3-1cs-pi5
++Info:   Enables spi3 on GPIOs 5-7 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin number for the CS line and
++        spidev device node creation are configurable. Pi 5 only.
++Load:   dtoverlay=spi3-1cs-pi5,<param>=<val>
++Params: cs0_pin                 GPIO pin for CS0 (default 4).
++        cs0_spidev              Set to 'off' to prevent the creation of a
++                                userspace device node /dev/spidev3.0 (default
++                                is 'on' or enabled).
++
++
+ Name:   spi3-2cs
+-Info:   Enables spi3 with two chip select (CS) lines and associated spidev
+-        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi3 on GPIO2 1-3 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. BCM2711 only,
++        spi3-2cs-pi5 is substituted on Pi 5.
+ Load:   dtoverlay=spi3-2cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 24 - BCM SPI3_CE1).
+@@ -4042,10 +4184,25 @@ Params: cs0_pin                 GPIO pin
+                                 is 'on' or enabled).
+ 
+ 
++Name:   spi3-2cs-pi5
++Info:   Enables spi3 on GPIOs 5-7 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. Pi 5 only.
++Load:   dtoverlay=spi3-2cs-pi5,<param>=<val>
++Params: cs0_pin                 GPIO pin for CS0 (default 4).
++        cs1_pin                 GPIO pin for CS1 (default 25).
++        cs0_spidev              Set to 'off' to prevent the creation of a
++                                userspace device node /dev/spidev3.0 (default
++                                is 'on' or enabled).
++        cs1_spidev              Set to 'off' to prevent the creation of a
++                                userspace device node /dev/spidev3.1 (default
++                                is 'on' or enabled).
++
++
+ Name:   spi4-1cs
+-Info:   Enables spi4 with a single chip select (CS) line and associated spidev
+-        dev node. The gpio pin number for the CS line and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi4 on GPIOs 5-7 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin number for the CS line and
++        spidev device node creation are configurable. BCM2711 only.
+ Load:   dtoverlay=spi4-1cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+         cs0_spidev              Set to 'off' to prevent the creation of a
+@@ -4054,9 +4211,9 @@ Params: cs0_pin                 GPIO pin
+ 
+ 
+ Name:   spi4-2cs
+-Info:   Enables spi4 with two chip select (CS) lines and associated spidev
+-        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi4 on GPIOs 5-6 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. BCM2711 only.
+ Load:   dtoverlay=spi4-2cs,<param>=<val>
+ Params: cs0_pin                 GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+         cs1_pin                 GPIO pin for CS1 (default 25 - BCM SPI4_CE1).
+@@ -4069,23 +4226,27 @@ Params: cs0_pin                 GPIO pin
+ 
+ 
+ Name:   spi5-1cs
+-Info:   Enables spi5 with a single chip select (CS) line and associated spidev
+-        dev node. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi5 on GPIOs 13-15 with a single chip select (CS) line and
++        associated spidev dev node. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. BCM2711 and Pi 5.
+ Load:   dtoverlay=spi5-1cs,<param>=<val>
+-Params: cs0_pin                 GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
++Params: cs0_pin                 GPIO pin for CS0 (default 12).
+         cs0_spidev              Set to 'off' to prevent the creation of a
+                                 userspace device node /dev/spidev5.0 (default
+                                 is 'on' or enabled).
+ 
+ 
++Name:   spi5-1cs-pi5
++Info:   See spi5-1cs
++
++
+ Name:   spi5-2cs
+-Info:   Enables spi5 with two chip select (CS) lines and associated spidev
+-        dev nodes. The gpio pin numbers for the CS lines and spidev device node
+-        creation are configurable. BCM2711 only.
++Info:   Enables spi5 on GPIOs 13-15 with two chip select (CS) lines and
++        associated spidev dev nodes. The gpio pin numbers for the CS lines and
++        spidev device node creation are configurable. BCM2711 and Pi 5.
+ Load:   dtoverlay=spi5-2cs,<param>=<val>
+-Params: cs0_pin                 GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
+-        cs1_pin                 GPIO pin for CS1 (default 26 - BCM SPI5_CE1).
++Params: cs0_pin                 GPIO pin for CS0 (default 12).
++        cs1_pin                 GPIO pin for CS1 (default 26).
+         cs0_spidev              Set to 'off' to prevent the creation of a
+                                 userspace device node /dev/spidev5.0 (default
+                                 is 'on' or enabled).
+@@ -4094,6 +4255,10 @@ Params: cs0_pin                 GPIO pin
+                                 is 'on' or enabled).
+ 
+ 
++Name:   spi5-2cs-pi5
++Info:   See spi5-2cs
++
++
+ Name:   spi6-1cs
+ Info:   Enables spi6 with a single chip select (CS) line and associated spidev
+         dev node. The gpio pin number for the CS line and spidev device node
+@@ -4296,6 +4461,12 @@ Params: txd0_pin                GPIO pin
+                                 7(Alt3) for 32&33, 6(Alt2) for 36&37
+ 
+ 
++Name:   uart0-pi5
++Info:   Enable uart 0 on GPIOs 14-15. Pi 5 only.
++Load:   dtoverlay=uart0-pi5,<param>
++Params: ctsrts                  Enable CTS/RTS on GPIOs 16-17 (default off)
++
++
+ Name:   uart1
+ Info:   Change the pin usage of uart1
+ Load:   dtoverlay=uart1,<param>=<val>
+@@ -4304,24 +4475,48 @@ Params: txd1_pin                GPIO pin
+         rxd1_pin                GPIO pin for RXD1 (15, 33 or 41 - default 15)
+ 
+ 
++Name:   uart1-pi5
++Info:   Enable uart 1 on GPIOs 0-1. Pi 5 only.
++Load:   dtoverlay=uart1-pi5,<param>
++Params: ctsrts                  Enable CTS/RTS on GPIOs 2-3 (default off)
++
++
+ Name:   uart2
+ Info:   Enable uart 2 on GPIOs 0-3. BCM2711 only.
+ Load:   dtoverlay=uart2,<param>
+ Params: ctsrts                  Enable CTS/RTS on GPIOs 2-3 (default off)
+ 
+ 
++Name:   uart2-pi5
++Info:   Enable uart 2 on GPIOs 4-5. Pi 5 only.
++Load:   dtoverlay=uart2-pi5,<param>
++Params: ctsrts                  Enable CTS/RTS on GPIOs 6-7 (default off)
++
++
+ Name:   uart3
+ Info:   Enable uart 3 on GPIOs 4-7. BCM2711 only.
+ Load:   dtoverlay=uart3,<param>
+ Params: ctsrts                  Enable CTS/RTS on GPIOs 6-7 (default off)
+ 
+ 
++Name:   uart3-pi5
++Info:   Enable uart 3 on GPIOs 8-9. Pi 5 only.
++Load:   dtoverlay=uart3-pi5,<param>
++Params: ctsrts                  Enable CTS/RTS on GPIOs 10-11 (default off)
++
++
+ Name:   uart4
+ Info:   Enable uart 4 on GPIOs 8-11. BCM2711 only.
+ Load:   dtoverlay=uart4,<param>
+ Params: ctsrts                  Enable CTS/RTS on GPIOs 10-11 (default off)
+ 
+ 
++Name:   uart4-pi5
++Info:   Enable uart 4 on GPIOs 12-13. Pi 5 only.
++Load:   dtoverlay=uart4-pi5,<param>
++Params: ctsrts                  Enable CTS/RTS on GPIOs 14-15 (default off)
++
++
+ Name:   uart5
+ Info:   Enable uart 5 on GPIOs 12-15. BCM2711 only.
+ Load:   dtoverlay=uart5,<param>
+@@ -4530,6 +4725,8 @@ Params: sizex                   Touchscr
+         invy                    Touchscreen inverted y axis
+         swapxy                  Touchscreen swapped x y axis
+         disable_touch           Disables the touch screen overlay driver
++        dsi0                    Use DSI0 and i2c_csi_dsi0 (rather than
++                                the default DSI1 and i2c_csi_dsi).
+ 
+ 
+ Name:   vc4-kms-dsi-lt070me05000
+@@ -4579,6 +4776,8 @@ Params: 2_8_inch                2.8" 480
+         invx                    Touchscreen inverted x axis
+         invy                    Touchscreen inverted y axis
+         swapxy                  Touchscreen swapped x y axis
++        dsi0                    Use DSI0 and i2c_csi_dsi0 (rather than
++                                the default DSI1 and i2c_csi_dsi).
+ 
+ 
+ Name:   vc4-kms-kippah-7inch
+@@ -4633,6 +4832,9 @@ Params: cma-512                 CMA is 5
+         nohdmi1                 Disable HDMI 1 output
+ 
+ 
++Name:   vc4-kms-v3d-pi5
++Info:   See vc4-kms-v3d-pi4 (this is the Pi 5 version)
++
+ 
+ Name:   vc4-kms-vga666
+ Info:   Enable the VGA666 (resistor ladder ADC) for the vc4-kms-v3d driver.
+--- a/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts
+@@ -23,7 +23,7 @@
+ 	};
+ 
+ 	fragment@1 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -33,7 +33,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "adi,adau1977-adc";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts
++++ b/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts
+@@ -5,7 +5,7 @@
+     compatible = "brcm,bcm2835";
+ 
+     fragment@0 {
+-        target = <&i2s>;
++        target = <&i2s_clk_producer>;
+         __overlay__ {
+             status = "okay";
+         };
+@@ -37,7 +37,7 @@
+                     "PDM_DAT", "Microphone Jack";
+             status = "okay";
+             simple-audio-card,cpu {
+-                sound-dai = <&i2s>;
++                sound-dai = <&i2s_clk_producer>;
+             };
+             dailink0_slave: simple-audio-card,codec {
+                 sound-dai = <&adau7002_codec>;
+--- a/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -38,7 +38,7 @@
+ 			card_name = "Akkordion";
+ 			dai_name = "IQaudIO DAC";
+ 			dai_stream_name = "IQaudIO DAC HiFi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
+@@ -18,8 +18,8 @@
+ 		};
+ 	};
+ 
+-	fragment@1 {
+-		target = <&i2s>;
++	frag1: fragment@1 {
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -46,7 +46,7 @@
+ 		target = <&sound>;
+ 		boss_dac: __overlay__ {
+ 			compatible = "allo,boss-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			mute-gpios = <&gpio 6 1>;
+ 			status = "okay";
+ 		};
+@@ -54,6 +54,8 @@
+ 
+ 	__overrides__ {
+ 		24db_digital_gain = <&boss_dac>,"allo,24db_digital_gain?";
+-		slave = <&boss_dac>,"allo,slave?";
++		slave = <&boss_dac>,"allo,slave?",
++			<&frag1>,"target:0=",<&i2s_clk_producer>,
++			<&boss_dac>,"i2s-controller:0=",<&i2s_clk_producer>;
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts
+@@ -8,7 +8,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			#sound-dai-cells = <0>;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/allo-digione-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-digione-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -35,7 +35,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "allo,allo-digione";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 			clock44-gpio = <&gpio 5 0>;
+ 			clock48-gpio = <&gpio 6 0>;
+--- a/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
+@@ -9,7 +9,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			#sound-dai-cells = <0>;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts
+@@ -16,7 +16,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -42,7 +42,7 @@
+ 		target = <&sound>;
+ 		piano_dac: __overlay__ {
+ 			compatible = "allo,piano-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -41,7 +41,7 @@
+ 		piano_dac: __overlay__ {
+ 			compatible = "allo,piano-dac-plus";
+ 			audio-codec = <&allo_pcm5122_4c &allo_pcm5122_4d>;
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			mute1-gpios = <&gpio 6 1>;
+ 			mute2-gpios = <&gpio 25 1>;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts
+@@ -16,7 +16,7 @@
+                 format = "i2s";
+ 
+                 p_cpu_dai: cpu {
+-                    sound-dai = <&i2s>;
++                    sound-dai = <&i2s_clk_producer>;
+                     dai-tdm-slot-num = <2>;
+                     dai-tdm-slot-width = <32>;
+                 };
+@@ -40,7 +40,7 @@
+     };
+ 
+     fragment@2 {
+-        target = <&i2s>;
++        target = <&i2s_clk_producer>;
+         __overlay__ {
+             #sound-dai-cells = <0>;
+             status = "okay";
+--- a/arch/arm/boot/dts/overlays/arducam-64mp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/arducam-64mp-overlay.dts
+@@ -67,7 +67,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/arducam-pivariety-overlay.dts
++++ b/arch/arm/boot/dts/overlays/arducam-pivariety-overlay.dts
+@@ -85,7 +85,7 @@
+ 		rotation = <&arducam_pivariety>,"rotation:0";
+ 		orientation = <&arducam_pivariety>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&arducam_pivariety>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -48,7 +48,7 @@
+ 			mult-gpios = <&gpio 27 0>, <&gpio 22 0>, <&gpio 23 0>,
+ 				     <&gpio 24 0>;
+ 			reset-gpios = <&gpio 5 0>;
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			codec = <&cs42448>;
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/audioinjector-bare-i2s-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-bare-i2s-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -27,7 +27,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "simple-audio-card";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 
+ 			simple-audio-card,name = "audioinjector-bare";
+@@ -37,7 +37,7 @@
+ 			simple-audio-card,frame-master = <&dailink0_master>;
+ 
+ 			dailink0_master: simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_producer>;
+ 				dai-tdm-slot-num = <2>;
+ 				dai-tdm-slot-width = <32>;
+ 			};
+--- a/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -47,7 +47,7 @@
+ 		snd: __overlay__ {
+ 			compatible = "ai,audioinjector-isolated-soundcard";
+ 			mute-gpios = <&gpio 17 0>;
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			codec = <&cs4272>;
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -33,7 +33,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "simple-audio-card";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 
+ 			simple-audio-card,name = "audioinjector-ultra";
+@@ -57,7 +57,7 @@
+ 			simple-audio-card,frame-master = <&sound_master>;
+ 
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_consumer>;
+ 				dai-tdm-slot-num = <2>;
+ 				dai-tdm-slot-width = <32>;
+ 			};
+--- a/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "ai,audioinjector-pi-soundcard";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
+@@ -8,7 +8,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -75,7 +75,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "as,audiosense-pi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/chipdip-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/chipdip-dac-overlay.dts
+@@ -9,7 +9,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "chipdip,chipdip-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			sr0-gpios = <&gpio 5 0>;
+ 			sr1-gpios = <&gpio 6 0>;
+ 			sr2-gpios = <&gpio 12 0>;
+--- a/arch/arm/boot/dts/overlays/cirrus-wm5102-overlay.dts
++++ b/arch/arm/boot/dts/overlays/cirrus-wm5102-overlay.dts
+@@ -9,7 +9,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -165,7 +165,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "wlf,rpi-cirrus";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/dacberry400-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dacberry400-overlay.dts
+@@ -5,7 +5,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -62,7 +62,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "osaelectronics,dacberry400";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/dionaudio-kiwi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-kiwi-overlay.dts
+@@ -11,7 +11,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "dionaudio,dionaudio-kiwi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts
+@@ -11,7 +11,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "dionaudio,loco-pcm5242-tpa3118";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts
+@@ -15,13 +15,13 @@
+ 		target = <&sound>;
+ 		frag0: __overlay__ {
+ 			compatible = "dionaudio,dionaudio-loco-v2";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+ 
+ 	fragment@1 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/disable-bt-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/* Disable Bluetooth */
++
++#include <dt-bindings/gpio/gpio.h>
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&bluetooth>;
++		__overlay__ {
++			status = "disabled";
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/disable-wifi-pi5-overlay.dts
+@@ -0,0 +1,13 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&sdio2>;
++		__overlay__ {
++			status = "disabled";
++		};
++	};
++};
+--- a/arch/arm/boot/dts/overlays/draws-overlay.dts
++++ b/arch/arm/boot/dts/overlays/draws-overlay.dts
+@@ -9,7 +9,7 @@
+ / {
+     compatible = "brcm,bcm2835";
+     fragment@0 {
+-        target = <&i2s>;
++        target = <&i2s_clk_producer>;
+         __overlay__ {
+             status = "okay";
+         };
+@@ -131,7 +131,7 @@
+         target = <&sound>;
+         snd: __overlay__ {
+             compatible = "simple-audio-card";
+-            i2s-controller = <&i2s>;
++            i2s-controller = <&i2s_clk_producer>;
+             status = "okay";
+ 
+             simple-audio-card,name = "draws";
+@@ -153,7 +153,7 @@
+                 "Line Out", "LOL";
+ 
+             dailink0_master: simple-audio-card,cpu {
+-                sound-dai = <&i2s>;
++                sound-dai = <&i2s_clk_producer>;
+             };
+ 
+             simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
+@@ -25,21 +25,21 @@
+ 	};
+ 
+ 	__overrides__ {
+-		i2c0 = <&frag13>,"target:0=",<&i2c0>;
+-		i2c1 = <&frag13>, "target?=0",
+-		       <&frag13>, "target-path=i2c1",
++		i2c0 = <&ts_i2c_frag>,"target:0=",<&i2c0>;
++		i2c1 = <&ts_i2c_frag>, "target?=0",
++		       <&ts_i2c_frag>, "target-path=i2c1",
+ 		       <0>,"-0-1";
+-		i2c3 = <&frag13>, "target?=0",
+-		       <&frag13>, "target-path=i2c3",
++		i2c3 = <&ts_i2c_frag>, "target?=0",
++		       <&ts_i2c_frag>, "target-path=i2c3",
+ 		       <0>,"-0-1";
+-		i2c4 = <&frag13>, "target?=0",
+-		       <&frag13>, "target-path=i2c4",
++		i2c4 = <&ts_i2c_frag>, "target?=0",
++		       <&ts_i2c_frag>, "target-path=i2c4",
+ 		       <0>,"-0-1";
+-		i2c5 = <&frag13>, "target?=0",
+-		       <&frag13>, "target-path=i2c5",
++		i2c5 = <&ts_i2c_frag>, "target?=0",
++		       <&ts_i2c_frag>, "target-path=i2c5",
+ 		       <0>,"-0-1";
+-		i2c6 = <&frag13>, "target?=0",
+-		       <&frag13>, "target-path=i2c6",
++		i2c6 = <&ts_i2c_frag>, "target?=0",
++		       <&ts_i2c_frag>, "target-path=i2c6",
+ 		       <0>,"-0-1";
+ 		addr = <&ft5406>,"reg:0";
+ 	};
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -37,7 +37,7 @@
+ 		};
+ 	};
+ 
+-	frag13: fragment@13 {
++	ts_i2c_frag: fragment@13 {
+ 		target = <&i2c_csi_dsi>;
+ 		i2cbus: __overlay__ {
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
+@@ -53,7 +53,7 @@
+ 	};
+ 
+ 	fragment@3 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -63,7 +63,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "fe-pi,fe-pi-audio";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts
+@@ -14,7 +14,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -43,7 +43,7 @@
+ 		target = <&sound>;
+ 		iqaudio_dac: __overlay__ {
+ 			compatible = "iqaudio,iqaudio-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			mute-gpios = <&amp 0 0>;
+ 			iqaudio-dac,auto-mute-amp;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -42,7 +42,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "googlevoicehat,googlevoicehat-soundcard";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-amp";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
+@@ -15,8 +15,8 @@
+ 		};
+ 	};
+ 
+-	fragment@1 {
+-		target = <&i2s>;
++	frag1: fragment@1 {
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -46,7 +46,7 @@
+ 		target = <&sound>;
+ 		hifiberry_dacplus: __overlay__ {
+ 			compatible = "hifiberry,hifiberry-dacplus";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 			mute-gpio = <&gpio 4 0>;
+ 			reset-gpio = <&gpio 17 0x11>;
+@@ -56,7 +56,10 @@
+ 	__overrides__ {
+ 		24db_digital_gain =
+ 			<&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+-		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
++		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?",
++			<&frag1>,"target:0=",<&i2s_clk_producer>,
++			<&hifiberry_dacplus>,"i2s-controller:0=",<&i2s_clk_producer>;
++
+ 		leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+ 		mute_ext_ctl = <&hifiberry_dacplus>,"hifiberry-dacplus,mute_ext_ctl:0";
+ 		auto_mute = <&hifiberry_dacplus>,"hifiberry-dacplus,auto_mute?";
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp3-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp3-overlay.dts
+@@ -10,7 +10,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -50,7 +50,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-amp3";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -27,7 +27,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
+@@ -15,8 +15,8 @@
+ 		};
+ 	};
+ 
+-	fragment@1 {
+-		target = <&i2s>;
++	frag1: fragment@1 {
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -51,7 +51,7 @@
+ 		target = <&sound>;
+ 		hifiberry_dacplus: __overlay__ {
+ 			compatible = "hifiberry,hifiberry-dacplus";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+@@ -59,7 +59,10 @@
+ 	__overrides__ {
+ 		24db_digital_gain =
+ 			<&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+-		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
++		slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?",
++			<&frag1>,"target:0=",<&i2s_clk_producer>,
++			<&hifiberry_dacplus>,"i2s-controller:0=",<&i2s_clk_producer>;
++
+ 		leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
+@@ -15,8 +15,8 @@
+ 		};
+ 	};
+ 
+-	fragment@1 {
+-		target = <&i2s>;
++	frag1: fragment@1 {
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -58,7 +58,7 @@
+ 		target = <&sound>;
+ 		hifiberry_dacplusadc: __overlay__ {
+ 			compatible = "hifiberry,hifiberry-dacplusadc";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+@@ -66,7 +66,9 @@
+ 	__overrides__ {
+ 		24db_digital_gain =
+ 			<&hifiberry_dacplusadc>,"hifiberry,24db_digital_gain?";
+-		slave = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,slave?";
++		slave = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,slave?",
++			<&frag1>,"target:0=",<&i2s_clk_producer>,
++			<&hifiberry_dacplusadc>,"i2s-controller:0=",<&i2s_clk_producer>;
+ 		leds_off = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,leds_off?";
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
+@@ -15,8 +15,8 @@
+ 		};
+ 	};
+ 
+-	fragment@1 {
+-		target = <&i2s>;
++	frag1: fragment@1 {
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -56,7 +56,7 @@
+ 		hifiberry_dacplusadcpro: __overlay__ {
+ 			compatible = "hifiberry,hifiberry-dacplusadcpro";
+ 			audio-codec = <&hb_dac &hb_adc>;
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+@@ -64,7 +64,9 @@
+ 	__overrides__ {
+ 		24db_digital_gain =
+ 			<&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,24db_digital_gain?";
+-		slave = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,slave?";
++		slave = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,slave?",
++			<&frag1>,"target:0=",<&i2s_clk_producer>,
++			<&hifiberry_dacplusadcpro>,"i2s-controller:0=",<&i2s_clk_producer>;
+ 		leds_off = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,leds_off?";
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -27,7 +27,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
+@@ -8,7 +8,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -84,7 +84,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-dacplushd";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			clocks = <&pll 0>;
+ 			reset-gpio = <&gpio 16 GPIO_ACTIVE_LOW>;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -34,7 +34,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-digi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -34,7 +34,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "hifiberry,hifiberry-digi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 			clock44-gpio = <&gpio 5 0>;
+ 			clock48-gpio = <&gpio 6 0>;
+--- a/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts
+@@ -9,13 +9,13 @@
+ 		target = <&sound>;
+ 		frag0: __overlay__ {
+ 			compatible = "audiophonics,i-sabre-q2m";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+ 
+ 	fragment@1 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c0-pi5-overlay.dts
+@@ -0,0 +1,34 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&i2c0>;
++		frag0: __overlay__ {
++			status = "okay";
++			clock-frequency = <100000>;
++		};
++	};
++
++	fragment@1 {
++		target = <&frag0>;
++		__overlay__ {
++			pinctrl-0 = <&rp1_i2c0_0_1>;
++		};
++	};
++
++	fragment@2 {
++		target = <&frag0>;
++		__dormant__ {
++			pinctrl-0 = <&rp1_i2c0_8_9>;
++		};
++	};
++
++	__overrides__ {
++		pins_0_1 = <0>,"+1-2";
++		pins_8_9 = <0>,"-1+2";
++		baudrate = <&frag0>, "clock-frequency:0";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c1-pi5-overlay.dts
+@@ -0,0 +1,34 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&i2c1>;
++		frag0: __overlay__ {
++			status = "okay";
++			clock-frequency = <100000>;
++		};
++	};
++
++	fragment@1 {
++		target = <&frag0>;
++		__overlay__ {
++			pinctrl-0 = <&rp1_i2c1_2_3>;
++		};
++	};
++
++	fragment@2 {
++		target = <&frag0>;
++		__dormant__ {
++			pinctrl-0 = <&rp1_i2c1_10_11>;
++		};
++	};
++
++	__overrides__ {
++		pins_2_3 = <0>,"+1-2";
++		pins_10_11 = <0>,"-1+2";
++		baudrate = <&frag0>, "clock-frequency:0";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c2-pi5-overlay.dts
+@@ -0,0 +1,21 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&i2c2>;
++		frag0: __overlay__ {
++			status = "okay";
++			clock-frequency = <100000>;
++			pinctrl-0 = <&rp1_i2c2_4_5>;
++		};
++	};
++
++	__overrides__ {
++		pins_4_5 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c2_4_5>;
++		pins_12_13 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c2_12_13>;
++		baudrate = <&frag0>, "clock-frequency:0";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c3-pi5-overlay.dts
+@@ -0,0 +1,22 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&i2c3>;
++		frag0: __overlay__ {
++			status = "okay";
++			clock-frequency = <100000>;
++			pinctrl-0 = <&rp1_i2c3_6_7>;
++		};
++	};
++
++	__overrides__ {
++		pins_6_7 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_6_7>;
++		pins_14_15 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_14_15>;
++		pins_22_23 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_22_23>;
++		baudrate = <&frag0>, "clock-frequency:0";
++	};
++};
+--- a/arch/arm/boot/dts/overlays/i2s-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2s-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -27,7 +27,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "rpi,rpi-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -69,7 +69,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx258-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx258-overlay.dts
+@@ -110,7 +110,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&reg_frag>, "target:0=",<&cam0_reg>,
+--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
+@@ -95,7 +95,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -94,7 +94,7 @@
+ 		rotation = <&imx296>,"rotation:0";
+ 		orientation = <&imx296>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&imx296>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -65,7 +65,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&reg_frag>, "target:0=",<&cam0_reg>,
+--- a/arch/arm/boot/dts/overlays/imx519-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx519-overlay.dts
+@@ -69,7 +69,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx708-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx708-overlay.dts
+@@ -79,12 +79,12 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&reg_frag>, "target:0=",<&cam0_reg>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+-		       <&cam_node>, "VANA1-supply:0=",<&cam0_reg>,
++		       <&cam_node>, "vana1-supply:0=",<&cam0_reg>,
+ 		       <&vcm_node>, "VDD-supply:0=",<&cam0_reg>;
+ 		vcm = <&vcm_node>, "status",
+ 		      <0>, "=4";
+--- a/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		iqaudio_dac: __overlay__ {
+ 			compatible = "iqaudio,iqaudio-codec";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -35,7 +35,7 @@
+ 		target = <&sound>;
+ 		frag2: __overlay__ {
+ 			compatible = "iqaudio,iqaudio-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -35,7 +35,7 @@
+ 		target = <&sound>;
+ 		iqaudio_dac: __overlay__ {
+ 			compatible = "iqaudio,iqaudio-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			mute-gpios = <&gpio 22 0>;
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -34,7 +34,7 @@
+ 		target = <&sound>;
+ 		wm8804_digi: __overlay__ {
+ 			compatible = "iqaudio,wm8804-digi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts
++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts
+@@ -82,7 +82,7 @@
+ 
+ 	__overrides__ {
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&irs1125>, "clocks:0=",<&cam0_clk>;
+--- a/arch/arm/boot/dts/overlays/justboom-both-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-both-overlay.dts
+@@ -7,7 +7,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -54,7 +54,7 @@
+ 		target = <&sound>;
+ 		frag3: __overlay__ {
+ 			compatible = "justboom,justboom-both";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -35,7 +35,7 @@
+ 		target = <&sound>;
+ 		frag2: __overlay__ {
+ 			compatible = "justboom,justboom-dac";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -34,7 +34,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "justboom,justboom-digi";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/max98357a-overlay.dts
++++ b/arch/arm/boot/dts/overlays/max98357a-overlay.dts
+@@ -12,7 +12,7 @@
+ 
+ 	/* Enable I2S */
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -52,7 +52,7 @@
+ 			simple-audio-card,name = "MAX98357A";
+ 			status = "okay";
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_producer>;
+ 			};
+ 			simple-audio-card,codec {
+ 				sound-dai = <&max98357a_dac>;
+@@ -69,7 +69,7 @@
+ 			simple-audio-card,name = "MAX98357A";
+ 			status = "okay";
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_producer>;
+ 			};
+ 			simple-audio-card,codec {
+ 				sound-dai = <&max98357a_nsd>;
+--- a/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "simple-audio-card";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 
+ 			simple-audio-card,name = "mbed-DAC";
+@@ -52,7 +52,7 @@
+ 			simple-audio-card,format = "i2s";
+ 
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_producer>;
+ 			};
+ 
+ 			sound_master: simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+@@ -9,7 +9,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -52,7 +52,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "merus,merus-amp";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart0-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ *   100000000*38400/31250 = 122880000
++ */
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target-path = "/";
++		__overlay__ {
++			midi_clk: midi_clk0 {
++				compatible = "fixed-clock";
++				#clock-cells = <0>;
++				clock-output-names = "uart0_pclk";
++				clock-frequency = <122880000>;
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&uart0>;
++		__overlay__ {
++			clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart1-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ *   100000000*38400/31250 = 122880000
++ */
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target-path = "/";
++		__overlay__ {
++			midi_clk: midi_clk1 {
++				compatible = "fixed-clock";
++				#clock-cells = <0>;
++				clock-output-names = "uart1_pclk";
++				clock-frequency = <122880000>;
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&uart1>;
++		__overlay__ {
++			clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart2-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ *   100000000*38400/31250 = 122880000
++ */
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target-path = "/";
++		__overlay__ {
++			midi_clk: midi_clk2 {
++				compatible = "fixed-clock";
++				#clock-cells = <0>;
++				clock-output-names = "uart2_pclk";
++				clock-frequency = <122880000>;
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&uart2>;
++		__overlay__ {
++			clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart3-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ *   100000000*38400/31250 = 122880000
++ */
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target-path = "/";
++		__overlay__ {
++			midi_clk: midi_clk3 {
++				compatible = "fixed-clock";
++				#clock-cells = <0>;
++				clock-output-names = "uart3_pclk";
++				clock-frequency = <122880000>;
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&uart3>;
++		__overlay__ {
++			clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++		};
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart4-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ *   100000000*38400/31250 = 122880000
++ */
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target-path = "/";
++		__overlay__ {
++			midi_clk: midi_clk4 {
++				compatible = "fixed-clock";
++				#clock-cells = <0>;
++				clock-output-names = "uart4_pclk";
++				clock-frequency = <122880000>;
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&uart4>;
++		__overlay__ {
++			clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++		};
++	};
++};
+--- a/arch/arm/boot/dts/overlays/ov2311-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov2311-overlay.dts
+@@ -60,7 +60,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -72,7 +72,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&reg_frag>, "target:0=",<&cam0_reg>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov7251-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov7251-overlay.dts
+@@ -60,7 +60,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts
+@@ -61,7 +61,7 @@
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -1,32 +1,100 @@
+ /dts-v1/;
+ 
+ / {
++	audremap {
++		bcm2835;
++		bcm2711;
++	};
++
++	balena-fin {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	bmp085_i2c-sensor {
+ 		deprecated = "use i2c-sensor,bmp085";
+ 	};
+ 
++	cm-swap-i2c0 {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	cutiepi-panel {
+ 		bcm2711;
+ 	};
+ 
++	disable-bt {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "disable-bt-pi5";
++	};
++
++	disable-bt-pi5 {
++		bcm2712;
++	};
++
+ 	disable-emmc2 {
+ 		bcm2711;
+ 	};
+ 
++	disable-wifi {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "disable-wifi-pi5";
++	};
++
++	disable-wifi-pi5 {
++		bcm2712;
++	};
++
+ 	highperi {
+ 		bcm2711;
+ 	};
+ 
++	i2c0 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "i2c0-pi5";
++	};
++
+ 	i2c0-bcm2708 {
+ 		deprecated = "use i2c0";
+ 	};
+ 
++	i2c0-pi5 {
++		bcm2712;
++	};
++
++	i2c1 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "i2c1-pi5";
++	};
++
+ 	i2c1-bcm2708 {
+ 		deprecated = "use i2c1";
+ 	};
+ 
++	i2c1-pi5 {
++		bcm2712;
++	};
++
++	i2c2 {
++		bcm2712 = "i2c2-pi5";
++	};
++
++	i2c2-pi5 {
++		bcm2712;
++	};
++
+ 	i2c3 {
+ 		bcm2711;
++		bcm2712 = "i2c3-pi5";
++	};
++
++	i2c3-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	i2c4 {
+@@ -41,26 +109,76 @@
+ 		bcm2711;
+ 	};
+ 
++	i2s-gpio28-31 {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	lirc-rpi {
+ 		deprecated = "use gpio-ir";
+ 	};
+ 
++	midi-uart0 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "midi-uart0-pi5";
++	};
++
++	midi-uart0-pi5 {
++		bcm2712;
++	};
++
++	midi-uart1 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "midi-uart1-pi5";
++	};
++
++	midi-uart1-pi5 {
++		bcm2712;
++	};
++
+ 	midi-uart2 {
+ 		bcm2711;
++		bcm2712 = "midi-uart2-pi5";
++	};
++
++	midi-uart2-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	midi-uart3 {
+ 		bcm2711;
++		bcm2712 = "midi-uart3-pi5";
++	};
++
++	midi-uart3-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	midi-uart4 {
+ 		bcm2711;
++		bcm2712 = "midi-uart4-pi5";
++	};
++
++	midi-uart4-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	midi-uart5 {
+ 		bcm2711;
+ 	};
+ 
++	miniuart-bt {
++		bcm2835;
++		bcm2711;
++	};
++
++	mmc {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	mpu6050 {
+ 		deprecated = "use i2c-sensor,mpu6050";
+ 	};
+@@ -118,6 +236,16 @@
+ 		deprecated = "no longer necessary";
+ 	};
+ 
++	sdhost {
++		bcm2835;
++		bcm2711;
++	};
++
++	sdio {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	sdio-1bit {
+ 		deprecated = "use sdio,bus_width=1,gpios_22_25";
+ 	};
+@@ -126,6 +254,21 @@
+ 		deprecated = "use 'dtparam=sd_poll_once' etc.";
+ 	};
+ 
++	smi {
++		bcm2835;
++		bcm2711;
++	};
++
++	smi-dev {
++		bcm2835;
++		bcm2711;
++	};
++
++	smi-nand {
++		bcm2835;
++		bcm2711;
++	};
++
+ 	spi0-cs {
+ 		renamed = "spi0-2cs";
+ 	};
+@@ -134,12 +277,42 @@
+ 		deprecated = "no longer necessary";
+ 	};
+ 
++	spi2-1cs {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "spi2-1cs-pi5";
++	};
++
++	spi2-1cs-pi5 {
++		bcm2712;
++	};
++
++	spi2-2cs {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "spi2-2cs-pi5";
++	};
++
++	spi2-2cs-pi5 {
++		bcm2712;
++	};
++
+ 	spi3-1cs {
+ 		bcm2711;
++		bcm2712 = "spi3-1cs-pi5";
++	};
++
++	spi3-1cs-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	spi3-2cs {
+ 		bcm2711;
++		bcm2712 = "spi3-2cs-pi5";
++	};
++
++	spi3-2cs-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	spi4-1cs {
+@@ -152,10 +325,20 @@
+ 
+ 	spi5-1cs {
+ 		bcm2711;
++		bcm2712 = "spi5-1cs-pi5";
++	};
++
++	spi5-1cs-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	spi5-2cs {
+ 		bcm2711;
++		bcm2712 = "spi5-2cs-pi5";
++	};
++
++	spi5-2cs-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	spi6-1cs {
+@@ -166,16 +349,51 @@
+ 		bcm2711;
+ 	};
+ 
++	uart0 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "uart0-pi5";
++	};
++
++	uart0-pi5 {
++		bcm2712;
++	};
++
++	uart1 {
++		bcm2835;
++		bcm2711;
++		bcm2712 = "uart1-pi5";
++	};
++
++	uart1-pi5 {
++		bcm2712;
++	};
++
+ 	uart2 {
+ 		bcm2711;
++		bcm2712 = "uart2-pi5";
++	};
++
++	uart2-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	uart3 {
+ 		bcm2711;
++		bcm2712 = "uart3-pi5";
++	};
++
++	uart3-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	uart4 {
+ 		bcm2711;
++		bcm2712 = "uart4-pi5";
++	};
++
++	uart4-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	uart5 {
+@@ -198,10 +416,12 @@
+ 	vc4-fkms-v3d {
+ 		bcm2835;
+ 		bcm2711 = "vc4-fkms-v3d-pi4";
++		bcm2712 = "vc4-fkms-v3d-pi4";
+ 	};
+ 
+ 	vc4-fkms-v3d-pi4 {
+ 		bcm2711;
++		bcm2712;
+ 	};
+ 
+ 	vc4-kms-dpi-at056tn53v1 {
+@@ -211,10 +431,16 @@
+ 	vc4-kms-v3d {
+ 		bcm2835;
+ 		bcm2711 = "vc4-kms-v3d-pi4";
++		bcm2712 = "vc4-kms-v3d-pi5";
+ 	};
+ 
+ 	vc4-kms-v3d-pi4 {
+ 		bcm2711;
++		bcm2712 = "vc4-kms-v3d-pi5";
++	};
++
++	vc4-kms-v3d-pi5 {
++		bcm2712;
+ 	};
+ 
+ 	vl805 {
+--- a/arch/arm/boot/dts/overlays/pibell-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pibell-overlay.dts
+@@ -24,7 +24,7 @@
+     };
+ 
+     fragment@1 {
+-        target = <&i2s>;
++        target = <&i2s_clk_producer>;
+         __overlay__ {
+             #sound-dai-cells = <0>;
+             status = "okay";
+@@ -43,7 +43,7 @@
+                 format = "i2s";
+ 
+                 r_cpu_dai: cpu {
+-                    sound-dai = <&i2s>;
++                    sound-dai = <&i2s_clk_producer>;
+ 
+ /* example TDM slot configuration
+                     dai-tdm-slot-num = <2>;
+@@ -60,7 +60,7 @@
+                 format = "i2s";
+ 
+                 p_cpu_dai: cpu {
+-                    sound-dai = <&i2s>;
++                    sound-dai = <&i2s_clk_producer>;
+ 
+ /* example TDM slot configuration
+                     dai-tdm-slot-num = <2>;
+--- a/arch/arm/boot/dts/overlays/pifi-40-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-40-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -42,7 +42,7 @@
+ 		pifi_40: __overlay__ {
+ 			compatible = "pifi,pifi-40";
+ 			audio-codec = <&tas5711l &tas5711r>;
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 			pdn-gpios = <&gpio 23 1>;
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -38,7 +38,7 @@
+ 			simple-audio-card,dai-link@1 {
+ 				format = "i2s";
+ 				cpu {
+-					sound-dai = <&i2s>;
++					sound-dai = <&i2s_clk_producer>;
+ 				};
+ 				codec {
+ 					sound-dai = <&pcm5142>;
+--- a/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts
+@@ -16,7 +16,7 @@
+ 				format = "i2s";
+ 
+ 				cpu {
+-					sound-dai = <&i2s>;
++					sound-dai = <&i2s_clk_producer>;
+ 					dai-tdm-slot-num = <2>;
+ 					dai-tdm-slot-width = <32>;
+ 				};
+@@ -40,7 +40,7 @@
+ 	};
+ 
+ 	fragment@2 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			#sound-dai-cells = <0>;
+ 			status = "okay";
+--- a/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -34,7 +34,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "pifi,pifi-mini-210";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_producer>;
+ 
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/pisound-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts
+@@ -75,7 +75,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "blokaslabs,pisound";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 
+ 			pinctrl-names = "default";
+@@ -108,7 +108,7 @@
+ 	};
+ 
+ 	fragment@7 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/proto-codec-overlay.dts
++++ b/arch/arm/boot/dts/overlays/proto-codec-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -32,7 +32,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "rpi,rpi-proto";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -42,7 +42,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "rra,digidac1-soundcard";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 		};
+ 	};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi2-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi2>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 0 1>;
++			status = "okay";
++
++			spidev2_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs0_spidev = <&spidev2_0>,"status";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi2-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi2>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 0 1>, <&gpio 24 1>;
++			status = "okay";
++
++			spidev2_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++
++			spidev2_1: spidev@1 {
++				compatible = "spidev";
++				reg = <1>;      /* CE1 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs1_pin  = <&frag1>,"cs-gpios:16";
++		cs0_spidev = <&spidev2_0>,"status";
++		cs1_spidev = <&spidev2_1>,"status";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi3-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi3>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 4 1>;
++			status = "okay";
++
++			spidev3_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs0_spidev = <&spidev3_0>,"status";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi3-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi3>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 4 1>, <&gpio 25 1>;
++			status = "okay";
++
++			spidev3_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++
++			spidev3_1: spidev@1 {
++				compatible = "spidev";
++				reg = <1>;      /* CE1 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs1_pin  = <&frag1>,"cs-gpios:16";
++		cs0_spidev = <&spidev3_0>,"status";
++		cs1_spidev = <&spidev3_1>,"status";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi5-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi5>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 12 1>;
++			status = "okay";
++
++			spidev5_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs0_spidev = <&spidev5_0>,"status";
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi5-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&spi5>;
++		frag1: __overlay__ {
++			/* needed to avoid dtc warning */
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			cs-gpios = <&gpio 12 1>, <&gpio 26 1>;
++			status = "okay";
++
++			spidev5_0: spidev@0 {
++				compatible = "spidev";
++				reg = <0>;      /* CE0 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++
++			spidev5_1: spidev@1 {
++				compatible = "spidev";
++				reg = <1>;      /* CE1 */
++				#address-cells = <1>;
++				#size-cells = <0>;
++				spi-max-frequency = <125000000>;
++				status = "okay";
++			};
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag1>,"cs-gpios:4";
++		cs1_pin  = <&frag1>,"cs-gpios:16";
++		cs0_spidev = <&spidev5_0>,"status";
++		cs1_spidev = <&spidev5_1>,"status";
++	};
++};
+--- a/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts
+@@ -9,7 +9,7 @@
+ 		target = <&sound>;
+ 		__overlay__ {
+ 			compatible = "simple-audio-card";
+-			i2s-controller = <&i2s>;
++			i2s-controller = <&i2s_clk_consumer>;
+ 			status = "okay";
+ 
+ 			simple-audio-card,name = "SuperAudioBoard";
+@@ -32,7 +32,7 @@
+ 			simple-audio-card,frame-master = <&sound_master>;
+ 
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_consumer>;
+ 				dai-tdm-slot-num = <2>;
+ 				dai-tdm-slot-width = <32>;
+ 			};
+@@ -45,7 +45,7 @@
+ 	};
+ 
+ 	fragment@1 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+--- a/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts
+@@ -8,7 +8,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -31,16 +31,16 @@
+ 			compatible = "simple-audio-card";
+ 			simple-audio-card,format = "i2s";
+ 			simple-audio-card,name = "tc358743";
+-			simple-audio-card,bitclock-master = <&dailink0_slave>;
+-			simple-audio-card,frame-master = <&dailink0_slave>;
++			simple-audio-card,bitclock-master = <&dailink0_master>;
++			simple-audio-card,frame-master = <&dailink0_master>;
+ 			status = "okay";
+ 
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_consumer>;
+ 				dai-tdm-slot-num = <2>;
+ 				dai-tdm-slot-width = <32>;
+ 			};
+-			dailink0_slave: simple-audio-card,codec {
++			dailink0_master: simple-audio-card,codec {
+ 				sound-dai = <&tc358743_codec>;
+ 			};
+ 		};
+--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
+@@ -101,7 +101,7 @@
+ 		4lane = <0>, "-2+3-7+8";
+ 		link-frequency = <&tc358743_0>,"link-frequencies#0";
+ 		media-controller = <&csi>,"brcm,media-controller?";
+-		cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++		cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&tc358743>, "clocks:0=",<&cam0_clk>;
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&uart0>;
++		frag0: __overlay__ {
++			status = "okay";
++		};
++	};
++
++	__overrides__ {
++		ctsrts = <&frag0>,"pinctrl-0:4=",<&uart0_ctsrts_pins>;
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&uart1>;
++		frag0: __overlay__ {
++			status = "okay";
++		};
++	};
++
++	__overrides__ {
++		ctsrts = <&frag0>,"pinctrl-0:4=",<&uart1_ctsrts_pins>;
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&uart2>;
++		frag0: __overlay__ {
++			status = "okay";
++		};
++	};
++
++	__overrides__ {
++		ctsrts = <&frag0>,"pinctrl-0:4=",<&uart2_ctsrts_pins>;
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&uart3>;
++		frag0: __overlay__ {
++			status = "okay";
++		};
++	};
++
++	__overrides__ {
++		ctsrts = <&frag0>,"pinctrl-0:4=",<&uart3_ctsrts_pins>;
++	};
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&uart4>;
++		frag0: __overlay__ {
++			status = "okay";
++		};
++	};
++
++	__overrides__ {
++		ctsrts = <&frag0>,"pinctrl-0:4=",<&uart4_ctsrts_pins>;
++	};
++};
+--- a/arch/arm/boot/dts/overlays/udrc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/udrc-overlay.dts
+@@ -9,7 +9,7 @@
+ / {
+     compatible = "brcm,bcm2835";
+     fragment@0 {
+-        target = <&i2s>;
++        target = <&i2s_clk_producer>;
+         __overlay__ {
+             clocks = <&clocks BCM2835_CLOCK_PCM>;
+             clock-names = "pcm";
+@@ -71,7 +71,7 @@
+         target = <&sound>;
+         snd: __overlay__ {
+             compatible = "simple-audio-card";
+-            i2s-controller = <&i2s>;
++            i2s-controller = <&i2s_clk_producer>;
+             status = "okay";
+ 
+             simple-audio-card,name = "udrc";
+@@ -93,7 +93,7 @@
+                 "Line Out", "LOL";
+ 
+             dailink0_master: simple-audio-card,cpu {
+-                sound-dai = <&i2s>;
++                sound-dai = <&i2s_clk_producer>;
+             };
+ 
+             simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_consumer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -29,14 +29,14 @@
+ 			compatible = "simple-audio-card";
+ 			simple-audio-card,format = "i2s";
+ 			simple-audio-card,name = "dabboard";
+-			simple-audio-card,bitclock-master = <&dailink0_slave>;
+-			simple-audio-card,frame-master = <&dailink0_slave>;
++			simple-audio-card,bitclock-master = <&dailink0_master>;
++			simple-audio-card,frame-master = <&dailink0_master>;
+ 			simple-audio-card,widgets = "Microphone", "Microphone Jack";
+ 			status = "okay";
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_consumer>;
+ 			};
+-			dailink0_slave: simple-audio-card,codec {
++			dailink0_master: simple-audio-card,codec {
+ 				#sound-dai-cells = <0>;
+ 				sound-dai = <&dmic_codec>;
+ 			};
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
+@@ -37,4 +37,10 @@
+ 			status = "okay";
+ 		};
+ 	};
++	fragment@5 {
++		target-path = "/chosen";
++		__overlay__  {
++			bootargs = "clk_ignore_unused";
++		};
++	};
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
+@@ -41,4 +41,10 @@
+ 			status = "okay";
+ 		};
+ 	};
++	fragment@5 {
++		target-path = "/chosen";
++		__overlay__  {
++			bootargs = "clk_ignore_unused";
++		};
++	};
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
+@@ -11,7 +11,7 @@
+ / {
+ 	/* No compatible as it will have come from edt-ft5406.dtsi */
+ 
+-	fragment@0 {
++	dsi_frag: fragment@0 {
+ 		target = <&dsi1>;
+ 		__overlay__ {
+ 			#address-cells = <1>;
+@@ -51,8 +51,8 @@
+ 	fragment@1 {
+ 		target-path = "/";
+ 		__overlay__ {
+-			panel_disp1: panel_disp1@0 {
+-				reg = <0>;
++			panel_disp: panel_disp@1 {
++				reg = <1>;
+ 				compatible = "raspberrypi,7inch-dsi", "simple-panel";
+ 				backlight = <&reg_display>;
+ 				power-supply = <&reg_display>;
+@@ -64,8 +64,8 @@
+ 				};
+ 			};
+ 
+-			reg_bridge: reg_bridge@0 {
+-				reg = <0>;
++			reg_bridge: reg_bridge@1 {
++				reg = <1>;
+ 				compatible = "regulator-fixed";
+ 				regulator-name = "bridge_reg";
+ 				gpio = <&reg_display 0 0>;
+@@ -75,7 +75,7 @@
+ 		};
+ 	};
+ 
+-	fragment@2 {
++	i2c_frag: fragment@2 {
+ 		target = <&i2c_csi_dsi>;
+ 		__overlay__ {
+ 			#address-cells = <1>;
+@@ -113,6 +113,12 @@
+ 	};
+ 
+ 	__overrides__ {
++		dsi0 = <&dsi_frag>, "target:0=",<&dsi0>,
++		       <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++		       <&ts_i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++		       <&panel_disp>, "reg:0=0",
++		       <&reg_bridge>, "reg:0=0",
++		       <&reg_bridge>, "regulator-name=bridge_reg_0";
+ 		disable_touch = <0>, "-10-11-12";
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
+@@ -9,7 +9,7 @@
+ / {
+ 	compatible = "brcm,bcm2835";
+ 
+-	fragment@0 {
++	dsi_frag: fragment@0 {
+ 		target = <&dsi1>;
+ 		__overlay__ {
+ 			#address-cells = <1>;
+@@ -29,7 +29,7 @@
+ 		};
+ 	};
+ 
+-	frag2: fragment@2 {
++	i2c_frag: fragment@2 {
+ 		target = <&i2c_csi_dsi>;
+ 		__overlay__ {
+ 			#address-cells = <1>;
+@@ -112,12 +112,14 @@
+ 				   <&touch>, "touchscreen-size-y:0=1480",
+ 				   <&touch>, "touchscreen-inverted-x?",
+ 				   <&touch>, "touchscreen-swapped-x-y?";
+-		i2c1 = <&frag2>, "target:0=",<&i2c1>,
++		i2c1 = <&i2c_frag>, "target:0=",<&i2c1>,
+ 		       <0>, "-3-4+5";
+ 		disable_touch = <&touch>, "status=disabled";
+ 		rotation = <&panel>, "rotation:0";
+ 		invx = <&touch>,"touchscreen-inverted-x?";
+ 		invy = <&touch>,"touchscreen-inverted-y?";
+ 		swapxy = <&touch>,"touchscreen-swapped-x-y?";
++		dsi0 = <&dsi_frag>, "target:0=",<&dsi0>,
++		       <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>;
+ 	};
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
+@@ -0,0 +1,147 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include "cma-overlay.dts"
++
++&frag0 {
++	size = <((320-4)*1024*1024)>;
++};
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@1 {
++		target = <&fb>;
++		__overlay__ {
++			status = "disabled";
++		};
++	};
++
++	fragment@2 {
++		target = <&aon_intr>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@3 {
++		target = <&ddc0>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@4 {
++		target = <&ddc1>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@5 {
++		target = <&hdmi0>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@6 {
++		target = <&hdmi1>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@7 {
++		target = <&hvs>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@8 {
++		target = <&mop>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@9 {
++		target = <&moplet>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@10 {
++		target = <&pixelvalve0>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@11 {
++		target = <&pixelvalve1>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@12 {
++		target = <&v3d>;
++		__overlay__ {
++			status = "okay";
++		};
++	};
++
++	fragment@13 {
++		target = <&vec>;
++		frag13: __overlay__ {
++			status = "disabled";
++		};
++	};
++
++	fragment@14 {
++		target = <&hdmi0>;
++		__dormant__  {
++			dmas;
++		};
++	};
++
++	fragment@15 {
++		target = <&hdmi1>;
++		__dormant__  {
++			dmas;
++		};
++	};
++
++	fragment@16 {
++		target = <&disp_intr>;
++		__overlay__  {
++			status = "okay";
++		};
++	};
++
++	fragment@17 {
++		target = <&vc4>;
++		__overlay__  {
++			/* IOMMU attaches here, where we allocate DMA buffers */
++			iommus = <&iommu4>;
++		};
++	};
++
++	__overrides__ {
++		audio   = <0>,"!14";
++		audio1   = <0>,"!15";
++		noaudio = <0>,"=14", <0>,"=15";
++		composite = <0>, "!3",
++			    <0>, "!4",
++			    <0>, "!5",
++			    <0>, "!6",
++			    <0>, "!10",
++			    <0>, "!11",
++			    <&frag13>, "status";
++		nohdmi0 =   <0>, "-3-5-10";
++		nohdmi1 =   <0>, "-4-6-11";
++		nohdmi =    <0>, "-3-4-5-6-10-11";
++	};
++};
+--- a/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts
+@@ -94,7 +94,14 @@
+ 		};
+ 	};
+ 
++	fragment@5 {
++		target = <&i2c_vc>;
++		__dormant__ {
++			status = "okay";
++		};
++	};
++
+ 	__overrides__ {
+-		ddc = <0>,"=2", <0>,"=3", <0>,"=4";
++		ddc = <0>,"=2", <0>,"=3", <0>,"=4", <0>,"=5";
+ 	};
+ };
+--- a/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ 	compatible = "brcm,bcm2835";
+ 
+ 	fragment@0 {
+-		target = <&i2s>;
++		target = <&i2s_clk_producer>;
+ 		__overlay__ {
+ 			status = "okay";
+ 		};
+@@ -65,7 +65,7 @@
+ 				"RINPUT2", "Mic Jack";
+ 
+ 			simple-audio-card,cpu {
+-				sound-dai = <&i2s>;
++				sound-dai = <&i2s_clk_producer>;
+ 			};
+ 			dailink0_slave: simple-audio-card,codec {
+ 				sound-dai = <&wm8960>;
+--- /dev/null
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -0,0 +1,1168 @@
++#include <dt-bindings/clock/rp1.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/mfd/rp1.h>
++
++&rp1_target {
++	rp1: rp1 {
++		compatible = "simple-bus";
++		#address-cells = <2>;
++		#size-cells = <2>;
++		#interrupt-cells = <2>;
++		interrupt-controller;
++		interrupt-parent = <&rp1>;
++
++		// ranges and dma-ranges must be provided by the includer
++
++		rp1_clocks: clocks@18000 {
++			compatible = "raspberrypi,rp1-clocks";
++			#clock-cells = <1>;
++			reg = <0xc0 0x40018000 0x0 0x10038>;
++			clocks = <&clk_xosc>;
++
++			assigned-clocks = <&rp1_clocks RP1_PLL_SYS_CORE>,
++					  <&rp1_clocks RP1_PLL_AUDIO_CORE>,
++					  // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers
++					  <&rp1_clocks RP1_PLL_SYS>,
++					  <&rp1_clocks RP1_PLL_SYS_SEC>,
++					  <&rp1_clocks RP1_PLL_AUDIO>,
++					  <&rp1_clocks RP1_PLL_AUDIO_SEC>,
++					  <&rp1_clocks RP1_CLK_SYS>,
++					  <&rp1_clocks RP1_PLL_SYS_PRI_PH>,
++					  // RP1_CLK_SLOW_SYS is used for the frequency counter (FC0)
++					  <&rp1_clocks RP1_CLK_SLOW_SYS>,
++					  <&rp1_clocks RP1_CLK_SDIO_TIMER>,
++					  <&rp1_clocks RP1_CLK_SDIO_ALT_SRC>,
++					  <&rp1_clocks RP1_CLK_ETH_TSU>;
++
++			assigned-clock-rates = <1000000000>, // RP1_PLL_SYS_CORE
++					       <1536000000>, // RP1_PLL_AUDIO_CORE
++					       <200000000>,  // RP1_PLL_SYS
++					       <125000000>,  // RP1_PLL_SYS_SEC
++					       <61440000>,   // RP1_PLL_AUDIO
++					       <192000000>,  // RP1_PLL_AUDIO_SEC
++					       <200000000>,  // RP1_CLK_SYS
++					       <100000000>,  // RP1_PLL_SYS_PRI_PH
++					       // Must match the XOSC frequency
++					       <50000000>, // RP1_CLK_SLOW_SYS
++					       <1000000>, // RP1_CLK_SDIO_TIMER
++					       <200000000>, // RP1_CLK_SDIO_ALT_SRC
++					       <50000000>; // RP1_CLK_ETH_TSU
++		};
++
++		rp1_uart0: serial@30000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x40030000  0x0 0x100>;
++			interrupts = <RP1_INT_UART0 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			dmas = <&rp1_dma RP1_DMA_UART0_TX>,
++			       <&rp1_dma RP1_DMA_UART0_RX>;
++			dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_uart1: serial@34000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x40034000  0x0 0x100>;
++			interrupts = <RP1_INT_UART1 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			// dmas = <&rp1_dma RP1_DMA_UART1_TX>,
++			//        <&rp1_dma RP1_DMA_UART1_RX>;
++			// dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_uart2: serial@38000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x40038000  0x0 0x100>;
++			interrupts = <RP1_INT_UART2 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			// dmas = <&rp1_dma RP1_DMA_UART2_TX>,
++			//        <&rp1_dma RP1_DMA_UART2_RX>;
++			// dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_uart3: serial@3c000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x4003c000  0x0 0x100>;
++			interrupts = <RP1_INT_UART3 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			// dmas = <&rp1_dma RP1_DMA_UART3_TX>,
++			//        <&rp1_dma RP1_DMA_UART3_RX>;
++			// dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_uart4: serial@40000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x40040000  0x0 0x100>;
++			interrupts = <RP1_INT_UART4 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			// dmas = <&rp1_dma RP1_DMA_UART4_TX>,
++			//        <&rp1_dma RP1_DMA_UART4_RX>;
++			// dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_uart5: serial@44000 {
++			compatible = "arm,pl011-axi";
++			reg = <0xc0 0x40044000  0x0 0x100>;
++			interrupts = <RP1_INT_UART5 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++			clock-names = "uartclk", "apb_pclk";
++			// dmas = <&rp1_dma RP1_DMA_UART5_TX>,
++			//        <&rp1_dma RP1_DMA_UART5_RX>;
++			// dma-names = "tx", "rx";
++			pinctrl-names = "default";
++			arm,primecell-periphid = <0x00541011>;
++			uart-has-rtscts;
++			cts-event-workaround;
++			skip-init;
++			status = "disabled";
++		};
++
++		rp1_spi8: spi@4c000 {
++			reg = <0xc0 0x4004c000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI8 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI8_TX>,
++			       <&rp1_dma RP1_DMA_SPI8_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_spi0: spi@50000 {
++			reg = <0xc0 0x40050000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI0 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI0_TX>,
++			       <&rp1_dma RP1_DMA_SPI0_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_spi1: spi@54000 {
++			reg = <0xc0 0x40054000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI1 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <0>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI1_TX>,
++			       <&rp1_dma RP1_DMA_SPI1_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_spi2: spi@58000 {
++			reg = <0xc0 0x40058000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI2 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI2_TX>,
++			       <&rp1_dma RP1_DMA_SPI2_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_spi3: spi@5c000 {
++			reg = <0xc0 0x4005c000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI3 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI3_TX>,
++			       <&rp1_dma RP1_DMA_SPI3_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		// SPI4 is a target/slave interface
++		rp1_spi4: spi@60000 {
++			reg = <0xc0 0x40060000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI4 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <0>;
++			#size-cells = <0>;
++			num-cs = <1>;
++			spi-slave;
++			dmas = <&rp1_dma RP1_DMA_SPI4_TX>,
++			       <&rp1_dma RP1_DMA_SPI4_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++
++			slave {
++				compatible = "spidev";
++				spi-max-frequency = <1000000>;
++			};
++		};
++
++		rp1_spi5: spi@64000 {
++			reg = <0xc0 0x40064000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI5 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			num-cs = <2>;
++			dmas = <&rp1_dma RP1_DMA_SPI5_TX>,
++			       <&rp1_dma RP1_DMA_SPI5_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		// SPI7 is a target/slave interface
++		rp1_spi7: spi@6c000 {
++			reg = <0xc0 0x4006c000  0x0 0x130>;
++			compatible = "snps,dw-apb-ssi";
++			interrupts = <RP1_INT_SPI7 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			clock-names = "ssi_clk";
++			#address-cells = <0>;
++			#size-cells = <0>;
++			num-cs = <1>;
++			spi-slave;
++			dmas = <&rp1_dma RP1_DMA_SPI7_TX>,
++			       <&rp1_dma RP1_DMA_SPI7_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++
++			slave {
++				compatible = "spidev";
++				spi-max-frequency = <1000000>;
++			};
++		};
++
++		rp1_i2c0: i2c@70000 {
++			reg = <0xc0 0x40070000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C0 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c1: i2c@74000 {
++			reg = <0xc0 0x40074000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C1 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c2: i2c@78000 {
++			reg = <0xc0 0x40078000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C2 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c3: i2c@7c000 {
++			reg = <0xc0 0x4007c000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C3 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c4: i2c@80000 {
++			reg = <0xc0 0x40080000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C4 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c5: i2c@84000 {
++			reg = <0xc0 0x40084000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C5 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_i2c6: i2c@88000 {
++			reg = <0xc0 0x40088000  0x0 0x1000>;
++			compatible = "snps,designware-i2c";
++			interrupts = <RP1_INT_I2C6 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS>;
++			status = "disabled";
++		};
++
++		rp1_pwm0: pwm@98000 {
++			compatible = "raspberrypi,rp1-pwm";
++			reg = <0xc0 0x40098000  0x0 0x100>;
++			#pwm-cells = <3>;
++			clocks = <&rp1_clocks RP1_CLK_PWM0>;
++			assigned-clocks = <&rp1_clocks RP1_CLK_PWM0>;
++			assigned-clock-rates = <6144000>;
++			status = "disabled";
++		};
++
++		rp1_pwm1: pwm@9c000 {
++			compatible = "raspberrypi,rp1-pwm";
++			reg = <0xc0 0x4009c000  0x0 0x100>;
++			#pwm-cells = <3>;
++			clocks = <&rp1_clocks RP1_CLK_PWM1>;
++			assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>;
++			assigned-clock-rates = <6144000>;
++			status = "disabled";
++		};
++
++		rp1_i2s0: i2s@a0000 {
++			reg = <0xc0 0x400a0000  0x0 0x1000>;
++			compatible = "snps,designware-i2s";
++			// Providing an interrupt disables DMA
++			// interrupts = <RP1_INT_I2S0 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_I2S>;
++			clock-names = "i2sclk";
++			#sound-dai-cells = <0>;
++			dmas = <&rp1_dma RP1_DMA_I2S0_TX>,<&rp1_dma RP1_DMA_I2S0_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_i2s1: i2s@a4000 {
++			reg = <0xc0 0x400a4000  0x0 0x1000>;
++			compatible = "snps,designware-i2s";
++			// Providing an interrupt disables DMA
++			// interrupts = <RP1_INT_I2S1 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_I2S>;
++			clock-names = "i2sclk";
++			#sound-dai-cells = <0>;
++			dmas = <&rp1_dma RP1_DMA_I2S1_TX>,<&rp1_dma RP1_DMA_I2S1_RX>;
++			dma-names = "tx", "rx";
++			status = "disabled";
++		};
++
++		rp1_i2s2: i2s@a8000 {
++			reg = <0xc0 0x400a8000  0x0 0x1000>;
++			compatible = "snps,designware-i2s";
++			// Providing an interrupt disables DMA
++			// interrupts = <RP1_INT_I2S2 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_I2S>;
++			status = "disabled";
++		};
++
++		rp1_sdio_clk0: sdio_clk0@b0004 {
++			compatible = "raspberrypi,rp1-sdio-clk";
++			reg = <0xc0 0x400b0004 0x0 0x1c>;
++			clocks = <&sdio_src &sdhci_core>;
++			clock-names = "src", "base";
++			#clock-cells = <0>;
++			status = "disabled";
++		};
++
++		rp1_sdio_clk1: sdio_clk1@b4004 {
++			compatible = "raspberrypi,rp1-sdio-clk";
++			reg = <0xc0 0x400b4004 0x0 0x1c>;
++			clocks = <&sdio_src &sdhci_core>;
++			clock-names = "src", "base";
++			#clock-cells = <0>;
++			status = "disabled";
++		};
++
++		rp1_adc: adc@c8000 {
++			compatible = "raspberrypi,rp1-adc";
++			reg = <0xc0 0x400c8000 0x0 0x4000>;
++			clocks = <&rp1_clocks RP1_CLK_ADC>;
++			clock-names = "adcclk";
++			#clock-cells = <0>;
++			vref-supply = <&rp1_vdd_3v3>;
++			status = "disabled";
++		};
++
++		rp1_gpio: gpio@d0000 {
++			reg = <0xc0 0x400d0000  0x0 0xc000>,
++			      <0xc0 0x400e0000  0x0 0xc000>,
++			      <0xc0 0x400f0000  0x0 0xc000>;
++			compatible = "raspberrypi,rp1-gpio";
++			interrupts = <RP1_INT_IO_BANK0 IRQ_TYPE_LEVEL_HIGH>,
++				     <RP1_INT_IO_BANK1 IRQ_TYPE_LEVEL_HIGH>,
++			             <RP1_INT_IO_BANK2 IRQ_TYPE_LEVEL_HIGH>;
++			gpio-controller;
++			#gpio-cells = <2>;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++
++			rp1_uart0_14_15: rp1_uart0_14_15 {
++				pin_txd {
++					function = "uart0";
++					pins = "gpio14";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart0";
++					pins = "gpio15";
++					bias-pull-up;
++				};
++			};
++			rp1_uart0_ctsrts_16_17: rp1_uart0_ctsrts_16_17 {
++				pin_cts {
++					function = "uart0";
++					pins = "gpio16";
++					bias-pull-up;
++				};
++				pin_rts {
++					function = "uart0";
++					pins = "gpio17";
++					bias-disable;
++				};
++			};
++			rp1_uart1_0_1: rp1_uart1_0_1 {
++				pin_txd {
++					function = "uart1";
++					pins = "gpio0";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart1";
++					pins = "gpio1";
++					bias-pull-up;
++				};
++			};
++			rp1_uart1_ctsrts_2_3: rp1_uart1_ctsrts_2_3 {
++				pin_cts {
++					function = "uart1";
++					pins = "gpio2";
++					bias-pull-up;
++				};
++				pin_rts {
++					function = "uart1";
++					pins = "gpio3";
++					bias-disable;
++				};
++			};
++			rp1_uart2_4_5: rp1_uart2_4_5 {
++				pin_txd {
++					function = "uart2";
++					pins = "gpio4";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart2";
++					pins = "gpio5";
++					bias-pull-up;
++				};
++			};
++			rp1_uart2_ctsrts_6_7: rp1_uart2_ctsrts_6_7 {
++				pin_cts {
++					function = "uart2";
++					pins = "gpio6";
++					bias-pull-up;
++				};
++				pin_rts {
++					function = "uart2";
++					pins = "gpio7";
++					bias-disable;
++				};
++			};
++			rp1_uart3_8_9: rp1_uart3_8_9 {
++				pin_txd {
++					function = "uart3";
++					pins = "gpio8";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart3";
++					pins = "gpio9";
++					bias-pull-up;
++				};
++			};
++			rp1_uart3_ctsrts_10_11: rp1_uart3_ctsrts_10_11 {
++				pin_cts {
++					function = "uart3";
++					pins = "gpio10";
++					bias-pull-up;
++				};
++				pin_rts {
++					function = "uart3";
++					pins = "gpio11";
++					bias-disable;
++				};
++			};
++			rp1_uart4_12_13: rp1_uart4_12_13 {
++				pin_txd {
++					function = "uart4";
++					pins = "gpio12";
++					bias-disable;
++				};
++				pin_rxd {
++					function = "uart4";
++					pins = "gpio13";
++					bias-pull-up;
++				};
++			};
++			rp1_uart4_ctsrts_14_15: rp1_uart4_ctsrts_14_15 {
++				pin_cts {
++					function = "uart4";
++					pins = "gpio14";
++					bias-pull-up;
++				};
++				pin_rts {
++					function = "uart4";
++					pins = "gpio15";
++					bias-disable;
++				};
++			};
++
++			rp1_sdio0_22_27: rp1_sdio0_22_27 {
++				pin_clk {
++					function = "sd0";
++					pins = "gpio22";
++					bias-disable;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++				pin_cmd {
++					function = "sd0";
++					pins = "gpio23";
++					bias-pull-up;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++				pins_dat {
++					function = "sd0";
++					pins = "gpio24", "gpio25", "gpio26", "gpio27";
++					bias-pull-up;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++			};
++
++			rp1_sdio1_28_33: rp1_sdio1_28_33 {
++				pin_clk {
++					function = "sd1";
++					pins = "gpio28";
++					bias-disable;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++				pin_cmd {
++					function = "sd1";
++					pins = "gpio29";
++					bias-pull-up;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++				pins_dat {
++					function = "sd1";
++					pins = "gpio30", "gpio31", "gpio32", "gpio33";
++					bias-pull-up;
++					drive-strength = <12>;
++					slew-rate = <1>;
++				};
++			};
++
++			rp1_i2s0_18_21: rp1_i2s0_18_21 {
++				function = "i2s0";
++				pins = "gpio18", "gpio19", "gpio20", "gpio21";
++				bias-disable;
++			};
++
++			rp1_i2s1_18_21: rp1_i2s1_18_21 {
++				function = "i2s1";
++				pins = "gpio18", "gpio19", "gpio20", "gpio21";
++				bias-disable;
++			};
++
++			rp1_i2c4_34_35: rp1_i2c4_34_35 {
++				function = "i2c4";
++				pins = "gpio34", "gpio35";
++				bias-pull-up;
++			};
++			rp1_i2c6_38_39: rp1_i2c6_38_39 {
++				function = "i2c6";
++				pins = "gpio38", "gpio39";
++				bias-pull-up;
++			};
++			rp1_i2c4_40_41: rp1_i2c4_40_41 {
++				function = "i2c4";
++				pins = "gpio40", "gpio41";
++				bias-pull-up;
++			};
++			rp1_i2c5_44_45: rp1_i2c5_44_45 {
++				function = "i2c5";
++				pins = "gpio44", "gpio45";
++				bias-pull-up;
++			};
++			rp1_i2c0_0_1: rp1_i2c0_0_1 {
++				function = "i2c0";
++				pins = "gpio0", "gpio1";
++				bias-pull-up;
++			};
++			rp1_i2c0_8_9: rp1_i2c0_8_9 {
++				function = "i2c0";
++				pins = "gpio8", "gpio9";
++				bias-pull-up;
++			};
++			rp1_i2c1_2_3: rp1_i2c1_2_3 {
++				function = "i2c1";
++				pins = "gpio2", "gpio3";
++				bias-pull-up;
++			};
++			rp1_i2c1_10_11: rp1_i2c1_10_11 {
++				function = "i2c1";
++				pins = "gpio10", "gpio11";
++				bias-pull-up;
++			};
++			rp1_i2c2_4_5: rp1_i2c2_4_5 {
++				function = "i2c2";
++				pins = "gpio4", "gpio5";
++				bias-pull-up;
++			};
++			rp1_i2c2_12_13: rp1_i2c2_12_13 {
++				function = "i2c2";
++				pins = "gpio12", "gpio13";
++				bias-pull-up;
++			};
++			rp1_i2c3_6_7: rp1_i2c3_6_7 {
++				function = "i2c3";
++				pins = "gpio6", "gpio7";
++				bias-pull-up;
++			};
++			rp1_i2c3_14_15: rp1_i2c3_14_15 {
++				function = "i2c3";
++				pins = "gpio14", "gpio15";
++				bias-pull-up;
++			};
++			rp1_i2c3_22_23: rp1_i2c3_22_23 {
++				function = "i2c3";
++				pins = "gpio22", "gpio23";
++				bias-pull-up;
++			};
++
++			// DPI mappings with HSYNC,VSYNC but without PIXCLK,DE
++			rp1_dpi_16bit_gpio2: rp1_dpi_16bit_gpio2 { /* Mode 2, not fully supported by RP1 */
++				function = "dpi";
++				pins = "gpio2", "gpio3", "gpio4", "gpio5",
++				       "gpio6", "gpio7", "gpio8", "gpio9",
++				       "gpio10", "gpio11", "gpio12", "gpio13",
++				       "gpio14", "gpio15", "gpio16", "gpio17",
++				       "gpio18", "gpio19";
++				bias-disable;
++			};
++			rp1_dpi_16bit_cpadhi_gpio2: rp1_dpi_16bit_cpadhi_gpio2 { /* Mode 3 */
++				function = "dpi";
++				pins = "gpio2", "gpio3", "gpio4", "gpio5",
++				       "gpio6", "gpio7", "gpio8",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio20", "gpio21", "gpio22", "gpio23",
++				       "gpio24";
++				bias-disable;
++			};
++			rp1_dpi_16bit_pad666_gpio2: rp1_dpi_16bit_pad666_gpio2 { /* Mode 4 */
++				function = "dpi";
++				pins = "gpio2", "gpio3",
++				       "gpio5", "gpio6", "gpio7", "gpio8",
++				       "gpio9",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio21", "gpio22", "gpio23", "gpio24",
++				       "gpio25";
++				bias-disable;
++			};
++			rp1_dpi_18bit_gpio2: rp1_dpi_18bit_gpio2 { /* Mode 5, not fully supported by RP1 */
++				function = "dpi";
++				pins = "gpio2", "gpio3", "gpio4", "gpio5",
++				       "gpio6", "gpio7", "gpio8", "gpio9",
++				       "gpio10", "gpio11", "gpio12", "gpio13",
++				       "gpio14", "gpio15", "gpio16", "gpio17",
++				       "gpio18", "gpio19", "gpio20", "gpio21";
++				bias-disable;
++			};
++			rp1_dpi_18bit_cpadhi_gpio2: rp1_dpi_18bit_cpadhi_gpio2 { /* Mode 6 */
++				function = "dpi";
++				pins = "gpio2", "gpio3", "gpio4", "gpio5",
++				       "gpio6", "gpio7", "gpio8", "gpio9",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio20", "gpio21", "gpio22", "gpio23",
++				       "gpio24", "gpio25";
++				bias-disable;
++			};
++			rp1_dpi_24bit_gpio2: rp1_dpi_24bit_gpio2 { /* Mode 7 */
++				function = "dpi";
++				pins = "gpio2", "gpio3", "gpio4", "gpio5",
++				       "gpio6", "gpio7", "gpio8", "gpio9",
++				       "gpio10", "gpio11", "gpio12", "gpio13",
++				       "gpio14", "gpio15", "gpio16", "gpio17",
++				       "gpio18", "gpio19", "gpio20", "gpio21",
++				       "gpio22", "gpio23", "gpio24", "gpio25",
++				       "gpio26", "gpio27";
++				bias-disable;
++			};
++			rp1_dpi_hvsync: rp1_dpi_hvsync { /* Sync only, for use with int VDAC */
++				function = "dpi";
++				pins = "gpio2", "gpio3";
++				bias-disable;
++			};
++
++			// More DPI mappings, including PIXCLK,DE on GPIOs 0,1
++			rp1_dpi_16bit_gpio0: rp1_dpi_16bit_gpio0 { /* Mode 2, not fully supported by RP1 */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio4", "gpio5", "gpio6", "gpio7",
++				       "gpio8", "gpio9", "gpio10", "gpio11",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17", "gpio18", "gpio19";
++				bias-disable;
++			};
++			rp1_dpi_16bit_cpadhi_gpio0: rp1_dpi_16bit_cpadhi_gpio0 { /* Mode 3 */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio4", "gpio5", "gpio6", "gpio7",
++				       "gpio8",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio20", "gpio21", "gpio22", "gpio23",
++				       "gpio24";
++				bias-disable;
++			};
++			rp1_dpi_16bit_pad666_gpio0: rp1_dpi_16bit_pad666_gpio0 { /* Mode 4 */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio5", "gpio6", "gpio7", "gpio8",
++				       "gpio9",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio21", "gpio22", "gpio23", "gpio24",
++				       "gpio25";
++				bias-disable;
++			};
++			rp1_dpi_18bit_gpio0: rp1_dpi_18bit_gpio0 { /* Mode 5, not fully supported by RP1 */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio4", "gpio5", "gpio6", "gpio7",
++				       "gpio8", "gpio9", "gpio10", "gpio11",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17", "gpio18", "gpio19",
++				       "gpio20", "gpio21";
++				bias-disable;
++			};
++			rp1_dpi_18bit_cpadhi_gpio0: rp1_dpi_18bit_cpadhi_gpio0 { /* Mode 6 */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio4", "gpio5", "gpio6", "gpio7",
++				       "gpio8", "gpio9",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17",
++				       "gpio20", "gpio21", "gpio22", "gpio23",
++				       "gpio24", "gpio25";
++				bias-disable;
++			};
++			rp1_dpi_24bit_gpio0: rp1_dpi_24bit_gpio0 { /* Mode 7 -- All GPIOs used! */
++				function = "dpi";
++				pins = "gpio0", "gpio1", "gpio2", "gpio3",
++				       "gpio4", "gpio5", "gpio6", "gpio7",
++				       "gpio8", "gpio9", "gpio10", "gpio11",
++				       "gpio12", "gpio13", "gpio14", "gpio15",
++				       "gpio16", "gpio17", "gpio18", "gpio19",
++				       "gpio20", "gpio21", "gpio22", "gpio23",
++				       "gpio24", "gpio25", "gpio26", "gpio27";
++				bias-disable;
++			};
++
++			rp1_pwm1_gpio45: rp1_pwm1_gpio45 {
++				function = "pwm1";
++				pins = "gpio45";
++				bias-pull-down;
++			};
++
++			rp1_spi0_gpio9: rp1_spi0_gpio9 {
++				function = "spi0";
++				pins = "gpio9", "gpio10", "gpio11";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi0_cs_gpio7: rp1_spi0_cs_gpio7 {
++				function = "spi0";
++				pins = "gpio7", "gpio8";
++				bias-pull-up;
++			};
++
++			rp1_spi1_gpio19: rp1_spi1_gpio19 {
++				function = "spi1";
++				pins = "gpio19", "gpio20", "gpio21";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi2_gpio1: rp1_spi2_gpio1 {
++				function = "spi2";
++				pins = "gpio1", "gpio2", "gpio3";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi3_gpio5: rp1_spi3_gpio5 {
++				function = "spi3";
++				pins = "gpio5", "gpio6", "gpio7";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi4_gpio9: rp1_spi4_gpio9 {
++				function = "spi4";
++				pins = "gpio9", "gpio10", "gpio11";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi5_gpio13: rp1_spi5_gpio13 {
++				function = "spi5";
++				pins = "gpio13", "gpio14", "gpio15";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi8_gpio49: rp1_spi8_gpio49 {
++				function = "spi8";
++				pins = "gpio49", "gpio50", "gpio51";
++				bias-disable;
++				drive-strength = <12>;
++				slew-rate = <1>;
++			};
++
++			rp1_spi8_cs_gpio52: rp1_spi8_cs_gpio52 {
++				function = "spi0";
++				pins = "gpio52", "gpio53";
++				bias-pull-up;
++			};
++		};
++
++		rp1_eth: ethernet@100000 {
++			reg = <0xc0 0x40100000  0x0 0x4000>;
++			compatible = "cdns,macb";
++			#address-cells = <1>;
++			#size-cells = <0>;
++			interrupts = <RP1_INT_ETH IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&macb_pclk &macb_hclk &rp1_clocks RP1_CLK_ETH_TSU>;
++			clock-names = "pclk", "hclk", "tsu_clk";
++			phy-mode = "rgmii-id";
++			cdns,aw2w-max-pipe = /bits/ 8 <8>;
++			cdns,ar2r-max-pipe = /bits/ 8 <8>;
++			cdns,use-aw2b-fill;
++			local-mac-address = [00 00 00 00 00 00];
++			status = "disabled";
++		};
++
++		rp1_csi0: csi@110000 {
++			compatible = "raspberrypi,rp1-cfe";
++			reg = <0xc0 0x40110000  0x0 0x100>, // CSI2 DMA address
++			      <0xc0 0x40114000  0x0 0x100>, // PHY/CSI Host address
++			      <0xc0 0x40120000  0x0 0x100>, // MIPI CFG address
++			      <0xc0 0x40124000  0x0 0x1000>; // PiSP FE address
++
++			// interrupts must match rp1_pisp_fe setup
++			interrupts = <RP1_INT_MIPI0 IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>;
++			assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>;
++			assigned-clock-rates = <25000000>;
++
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		rp1_csi1: csi@128000 {
++			compatible = "raspberrypi,rp1-cfe";
++			reg = <0xc0 0x40128000  0x0 0x100>, // CSI2 DMA address
++			      <0xc0 0x4012c000  0x0 0x100>, // PHY/CSI Host address
++			      <0xc0 0x40138000  0x0 0x100>, // MIPI CFG address
++			      <0xc0 0x4013c000  0x0 0x1000>; // PiSP FE address
++
++			// interrupts must match rp1_pisp_fe setup
++			interrupts = <RP1_INT_MIPI1 IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>;
++			assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>;
++			assigned-clock-rates = <25000000>;
++
++			#address-cells = <1>;
++			#size-cells = <0>;
++			status = "disabled";
++		};
++
++		rp1_mmc0: mmc@180000 {
++			reg = <0xc0 0x40180000  0x0 0x100>;
++			compatible = "snps,dwcmshc-sdhci";
++			interrupts = <RP1_INT_SDIO0 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
++			          &rp1_clocks RP1_CLK_SDIO_TIMER
++			          &rp1_sdio_clk0>;
++			clock-names = "bus", "core", "timeout", "sdio";
++			/* Bank 0 VDDIO is fixed */
++			no-1-8-v;
++			bus-width = <4>;
++			vmmc-supply = <&rp1_vdd_3v3>;
++			broken-cd;
++			status = "disabled";
++		};
++
++		rp1_mmc1: mmc@184000 {
++			reg = <0xc0 0x40184000  0x0 0x100>;
++			compatible = "snps,dwcmshc-sdhci";
++			interrupts = <RP1_INT_SDIO1 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
++			          &rp1_clocks RP1_CLK_SDIO_TIMER
++			          &rp1_sdio_clk1>;
++			clock-names = "bus", "core", "timeout", "sdio";
++			bus-width = <4>;
++			vmmc-supply = <&rp1_vdd_3v3>;
++			/* Nerf SDR speeds */
++			sdhci-caps-mask = <0x3 0x0>;
++			broken-cd;
++			status = "disabled";
++		};
++
++		rp1_dma: dma@188000 {
++			reg = <0xc0 0x40188000  0x0 0x1000>;
++			compatible = "snps,axi-dma-1.01a";
++			interrupts = <RP1_INT_DMA IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&sdhci_core &rp1_clocks RP1_CLK_SYS>;
++			clock-names = "core-clk", "cfgr-clk";
++
++			#dma-cells = <1>;
++			dma-channels = <8>;
++			snps,dma-masters = <1>;
++			snps,dma-targets = <64>;
++			snps,data-width = <4>; // (8 << 4) == 128 bits
++			snps,block-size = <0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000>;
++			snps,priority = <0 1 2 3 4 5 6 7>;
++			snps,axi-max-burst-len = <8>;
++			status = "disabled";
++		};
++
++		rp1_usb0: usb@200000 {
++			reg = <0xc0 0x40200000  0x0 0x100000>;
++			compatible = "snps,dwc3";
++			dr_mode = "host";
++			usb3-lpm-capable;
++			snps,axi-pipe-limit = /bits/ 8 <8>;
++			snps,dis_rxdet_inp3_quirk;
++			snps,tx-max-burst-prd = <8>;
++			snps,tx-thr-num-pkt-prd = <2>;
++			interrupts = <RP1_INT_USBHOST0_0 IRQ_TYPE_EDGE_RISING>;
++			status = "disabled";
++		};
++
++		rp1_usb1: usb@300000 {
++			reg = <0xc0 0x40300000  0x0 0x100000>;
++			compatible = "snps,dwc3";
++			dr_mode = "host";
++			usb3-lpm-capable;
++			snps,axi-pipe-limit = /bits/ 8 <8>;
++			snps,dis_rxdet_inp3_quirk;
++			snps,tx-max-burst-prd = <8>;
++			snps,tx-thr-num-pkt-prd = <2>;
++			interrupts = <RP1_INT_USBHOST1_0 IRQ_TYPE_EDGE_RISING>;
++			status = "disabled";
++		};
++
++		rp1_dsi0: dsi@110000 {
++			compatible = "raspberrypi,rp1dsi";
++			status = "disabled";
++			reg = <0xc0 0x40118000  0x0 0x1000>,  // MIPI0 DSI DMA (ArgonDPI)
++			      <0xc0 0x4011c000  0x0 0x1000>,  // MIPI0 DSI Host (SNPS)
++			      <0xc0 0x40120000  0x0 0x1000>;  // MIPI0 CFG
++
++			interrupts = <RP1_INT_MIPI0 IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>,  // required, config bus clock
++				 <&rp1_clocks RP1_CLK_MIPI0_DPI>,  // required, pixel clock
++				 <&clksrc_mipi0_dsi_byteclk>,    // internal, parent for divide
++				 <&clk_xosc>;                    // hardwired to DSI "refclk"
++			clock-names = "cfgclk", "dpiclk", "byteclk", "refclk";
++
++			assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>,
++					  <&rp1_clocks RP1_CLK_MIPI0_DPI>;
++			assigned-clock-rates = <25000000>;
++			assigned-clock-parents = <0>, <&clksrc_mipi0_dsi_byteclk>;
++		};
++
++		rp1_dsi1: dsi@128000 {
++			compatible = "raspberrypi,rp1dsi";
++			status = "disabled";
++			reg = <0xc0 0x40130000  0x0 0x1000>,  // MIPI1 DSI DMA (ArgonDPI)
++		              <0xc0 0x40134000  0x0 0x1000>,  // MIPI1 DSI Host (SNPS)
++		              <0xc0 0x40138000  0x0 0x1000>;  // MIPI1 CFG
++
++			interrupts = <RP1_INT_MIPI1 IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>,  // required, config bus clock
++				 <&rp1_clocks RP1_CLK_MIPI1_DPI>,  // required, pixel clock
++				 <&clksrc_mipi1_dsi_byteclk>,    // internal, parent for divide
++				 <&clk_xosc>;                    // hardwired to DSI "refclk"
++			clock-names = "cfgclk", "dpiclk", "byteclk", "refclk";
++
++			assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>,
++					  <&rp1_clocks RP1_CLK_MIPI1_DPI>;
++			assigned-clock-rates = <25000000>;
++			assigned-clock-parents = <0>, <&clksrc_mipi1_dsi_byteclk>;
++		};
++
++		/* VEC and DPI both need to control PLL_VIDEO and cannot work together;   */
++		/* config.txt should enable one or other using dtparam=vec or an overlay. */
++		rp1_vec: vec@144000 {
++			compatible = "raspberrypi,rp1vec";
++			status = "disabled";
++			reg = <0xc0 0x40144000  0x0 0x1000>, // VIDEO_OUT_VEC
++			      <0xc0 0x40140000  0x0 0x1000>; // VIDEO_OUT_CFG
++
++			interrupts = <RP1_INT_VIDEO_OUT IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_VEC>;
++
++			assigned-clocks = <&rp1_clocks RP1_PLL_VIDEO_CORE>,
++					  <&rp1_clocks RP1_PLL_VIDEO_SEC>,
++					  <&rp1_clocks RP1_CLK_VEC>;
++			assigned-clock-rates = <1188000000>,
++					       <108000000>,
++					       <108000000>;
++			assigned-clock-parents = <0>,
++						 <&rp1_clocks RP1_PLL_VIDEO_CORE>,
++						 <&rp1_clocks RP1_PLL_VIDEO_SEC>;
++		};
++
++		rp1_dpi: dpi@148000 {
++			compatible = "raspberrypi,rp1dpi";
++			status = "disabled";
++			reg = <0xc0 0x40148000  0x0 0x1000>, // VIDEO_OUT DPI
++			      <0xc0 0x40140000  0x0 0x1000>; // VIDEO_OUT_CFG
++
++			interrupts = <RP1_INT_VIDEO_OUT IRQ_TYPE_LEVEL_HIGH>;
++
++			clocks = <&rp1_clocks RP1_CLK_DPI>,        // DPI pixel clock
++				 <&rp1_clocks RP1_PLL_VIDEO>,      // PLL primary divider, and
++				 <&rp1_clocks RP1_PLL_VIDEO_CORE>; // VCO, which we also control
++			clock-names = "dpiclk", "plldiv", "pllcore";
++
++			assigned-clocks        = <&rp1_clocks RP1_CLK_DPI>;
++			assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>;
++		};
++	};
++};
++
++&clocks {
++	clk_xosc: clk_xosc {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "xosc";
++		clock-frequency = <50000000>;
++	};
++	macb_pclk: macb_pclk {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "pclk";
++		clock-frequency = <200000000>;
++	};
++	macb_hclk: macb_hclk {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "hclk";
++		clock-frequency = <200000000>;
++	};
++	sdio_src: sdio_src {
++		// 400 MHz on FPGA. PLL sys VCO on asic
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "src";
++		clock-frequency = <1000000000>;
++	};
++	sdhci_core: sdhci_core {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "core";
++		clock-frequency = <50000000>;
++	};
++	clksrc_mipi0_dsi_byteclk: clksrc_mipi0_dsi_byteclk {
++		// This clock is synthesized by MIPI0 D-PHY, when DSI is running.
++		// Its frequency is not known a priori (until a panel driver attaches)
++		// so assign a made-up frequency of 72MHz so it can be divided for DPI.
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "clksrc_mipi0_dsi_byteclk";
++		clock-frequency = <72000000>;
++	};
++	clksrc_mipi1_dsi_byteclk: clksrc_mipi1_dsi_byteclk {
++		// This clock is synthesized by MIPI1 D-PHY, when DSI is running.
++		// Its frequency is not known a priori (until a panel driver attaches)
++		// so assign a made-up frequency of 72MHz so it can be divided for DPI.
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-output-names = "clksrc_mipi1_dsi_byteclk";
++		clock-frequency = <72000000>;
++	};
++};
++
++/ {
++	rp1_vdd_3v3: rp1_vdd_3v3 {
++		compatible = "regulator-fixed";
++		regulator-name = "vdd-3v3";
++		regulator-min-microvolt = <3300000>;
++		regulator-max-microvolt = <3300000>;
++		regulator-always-on;
++	};
++};
+--- a/arch/arm64/boot/dts/broadcom/Makefile
++++ b/arch/arm64/boot/dts/broadcom/Makefile
+@@ -16,6 +16,7 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rp
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-cm3.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4s.dtb
++dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-5-b.dtb
+ 
+ subdir-y	+= bcmbca
+ subdir-y	+= northstar2
+--- /dev/null
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -0,0 +1 @@
++#include "../../../../arm/boot/dts/bcm2712-rpi-5-b.dts"

+ 282 - 0
target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch

@@ -0,0 +1,282 @@
+From fa18902ee1e53ad391a455a01be3ab2ea1c5af5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Fri, 21 May 2021 12:33:38 +0100
+Subject: [PATCH] gpio_brcmstb: Allow to build for ARCH_BCM2835
+
+gpio-brcmstb: Report the correct bank width
+
+gpio: brcmstb: Use bank address as gpiochip label
+
+If the path to the device node is used as gpiochip label then
+gpio-brcmstb instances with multiple banks end up with duplicated
+names. Instead, use a combination of the driver name with the physical
+address of the bank, which is both unique and helpful for devmem
+debugging.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+gpio: mmio: Add DIRECT mode for shared access
+
+The generic MMIO GPIO library uses shadow registers for efficiency,
+but this breaks attempts by raspi-gpio to change other GPIOs in the
+same bank. Add a DIRECT mode that makes fewer assumptions about the
+existing register contents, but note that genuinely simultaneous
+accesses are likely to lose updates.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+gpio: brcmstb: Don't always clear interrupt mask
+
+If the GPIO controller is not being used as an interrupt source
+leave the interrupt mask register alone. On BCM2712 it might be used
+to generate interrupts to the VPU firmware, and on other devices it
+doesn't matter since no interrupts will be generated.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/gpio/Kconfig        |   2 +-
+ drivers/gpio/gpio-brcmstb.c |  14 ++--
+ drivers/gpio/gpio-mmio.c    | 124 ++++++++++++++++++++++++++++++++++--
+ include/linux/gpio/driver.h |   1 +
+ 4 files changed, 131 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -203,7 +203,7 @@ config GPIO_BCM_VIRT
+ config GPIO_BRCMSTB
+ 	tristate "BRCMSTB GPIO support"
+ 	default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
+-	depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST)
++	depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM2835 || COMPILE_TEST)
+ 	select GPIO_GENERIC
+ 	select IRQ_DOMAIN
+ 	help
+--- a/drivers/gpio/gpio-brcmstb.c
++++ b/drivers/gpio/gpio-brcmstb.c
+@@ -640,6 +640,8 @@ static int brcmstb_gpio_probe(struct pla
+ #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
+ 	flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
+ #endif
++	if (of_property_read_bool(np, "brcm,gpio-direct"))
++	    flags |= BGPIOF_REG_DIRECT;
+ 
+ 	of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p,
+ 			bank_width) {
+@@ -689,7 +691,9 @@ static int brcmstb_gpio_probe(struct pla
+ 		}
+ 
+ 		gc->owner = THIS_MODULE;
+-		gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np);
++		gc->label = devm_kasprintf(dev, GFP_KERNEL, "gpio-brcmstb@%zx",
++					   (size_t)res->start +
++					   GIO_BANK_OFF(bank->id, 0));
+ 		if (!gc->label) {
+ 			err = -ENOMEM;
+ 			goto fail;
+@@ -698,7 +702,7 @@ static int brcmstb_gpio_probe(struct pla
+ 		gc->of_gpio_n_cells = 2;
+ 		gc->of_xlate = brcmstb_gpio_of_xlate;
+ 		/* not all ngpio lines are valid, will use bank width later */
+-		gc->ngpio = MAX_GPIO_PER_BANK;
++		gc->ngpio = bank_width;
+ 		gc->offset = bank->id * MAX_GPIO_PER_BANK;
+ 		if (priv->parent_irq > 0)
+ 			gc->to_irq = brcmstb_gpio_to_irq;
+@@ -707,8 +711,10 @@ static int brcmstb_gpio_probe(struct pla
+ 		 * Mask all interrupts by default, since wakeup interrupts may
+ 		 * be retained from S5 cold boot
+ 		 */
+-		need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
+-		gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
++		if (priv->parent_irq > 0) {
++			need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
++			gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
++		}
+ 
+ 		err = gpiochip_add_data(gc, bank);
+ 		if (err) {
+--- a/drivers/gpio/gpio-mmio.c
++++ b/drivers/gpio/gpio-mmio.c
+@@ -232,6 +232,25 @@ static void bgpio_set(struct gpio_chip *
+ 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ }
+ 
++static void bgpio_set_direct(struct gpio_chip *gc, unsigned int gpio, int val)
++{
++	unsigned long mask = bgpio_line2mask(gc, gpio);
++	unsigned long flags;
++
++	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++	gc->bgpio_data = gc->read_reg(gc->reg_dat);
++
++	if (val)
++		gc->bgpio_data |= mask;
++	else
++		gc->bgpio_data &= ~mask;
++
++	gc->write_reg(gc->reg_dat, gc->bgpio_data);
++
++	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
+ 				 int val)
+ {
+@@ -324,6 +343,27 @@ static void bgpio_set_multiple_with_clea
+ 		gc->write_reg(gc->reg_clr, clear_mask);
+ }
+ 
++static void bgpio_set_multiple_direct(struct gpio_chip *gc,
++				      unsigned long *mask,
++				      unsigned long *bits)
++{
++	unsigned long flags;
++	unsigned long set_mask, clear_mask;
++
++	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++	bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
++
++	gc->bgpio_data = gc->read_reg(gc->reg_dat);
++
++	gc->bgpio_data |= set_mask;
++	gc->bgpio_data &= ~clear_mask;
++
++	gc->write_reg(gc->reg_dat, gc->bgpio_data);
++
++	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
+ {
+ 	return 0;
+@@ -361,6 +401,29 @@ static int bgpio_dir_in(struct gpio_chip
+ 	return 0;
+ }
+ 
++static int bgpio_dir_in_direct(struct gpio_chip *gc, unsigned int gpio)
++{
++	unsigned long flags;
++
++	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++	if (gc->reg_dir_in)
++		gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in);
++	if (gc->reg_dir_out)
++		gc->bgpio_dir = gc->read_reg(gc->reg_dir_out);
++
++	gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio);
++
++	if (gc->reg_dir_in)
++		gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
++	if (gc->reg_dir_out)
++		gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
++
++	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++
++	return 0;
++}
++
+ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
+ {
+ 	/* Return 0 if output, 1 if input */
+@@ -399,6 +462,28 @@ static void bgpio_dir_out(struct gpio_ch
+ 	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ }
+ 
++static void bgpio_dir_out_direct(struct gpio_chip *gc, unsigned int gpio,
++				 int val)
++{
++	unsigned long flags;
++
++	raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++	if (gc->reg_dir_in)
++		gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in);
++	if (gc->reg_dir_out)
++		gc->bgpio_dir = gc->read_reg(gc->reg_dir_out);
++
++	gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
++
++	if (gc->reg_dir_in)
++		gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
++	if (gc->reg_dir_out)
++		gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
++
++	raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
+ 				   int val)
+ {
+@@ -415,6 +500,22 @@ static int bgpio_dir_out_val_first(struc
+ 	return 0;
+ }
+ 
++static int bgpio_dir_out_dir_first_direct(struct gpio_chip *gc,
++					  unsigned int gpio, int val)
++{
++	bgpio_dir_out_direct(gc, gpio, val);
++	gc->set(gc, gpio, val);
++	return 0;
++}
++
++static int bgpio_dir_out_val_first_direct(struct gpio_chip *gc,
++					  unsigned int gpio, int val)
++{
++	gc->set(gc, gpio, val);
++	bgpio_dir_out_direct(gc, gpio, val);
++	return 0;
++}
++
+ static int bgpio_setup_accessors(struct device *dev,
+ 				 struct gpio_chip *gc,
+ 				 bool byte_be)
+@@ -508,6 +609,9 @@ static int bgpio_setup_io(struct gpio_ch
+ 	} else if (flags & BGPIOF_NO_OUTPUT) {
+ 		gc->set = bgpio_set_none;
+ 		gc->set_multiple = NULL;
++	} else if (flags & BGPIOF_REG_DIRECT) {
++		gc->set = bgpio_set_direct;
++		gc->set_multiple = bgpio_set_multiple_direct;
+ 	} else {
+ 		gc->set = bgpio_set;
+ 		gc->set_multiple = bgpio_set_multiple;
+@@ -544,11 +648,21 @@ static int bgpio_setup_direction(struct
+ 	if (dirout || dirin) {
+ 		gc->reg_dir_out = dirout;
+ 		gc->reg_dir_in = dirin;
+-		if (flags & BGPIOF_NO_SET_ON_INPUT)
+-			gc->direction_output = bgpio_dir_out_dir_first;
+-		else
+-			gc->direction_output = bgpio_dir_out_val_first;
+-		gc->direction_input = bgpio_dir_in;
++		if (flags & BGPIOF_REG_DIRECT) {
++			if (flags & BGPIOF_NO_SET_ON_INPUT)
++				gc->direction_output =
++					bgpio_dir_out_dir_first_direct;
++			else
++				gc->direction_output =
++					bgpio_dir_out_val_first_direct;
++			gc->direction_input = bgpio_dir_in_direct;
++		} else {
++			if (flags & BGPIOF_NO_SET_ON_INPUT)
++				gc->direction_output = bgpio_dir_out_dir_first;
++			else
++				gc->direction_output = bgpio_dir_out_val_first;
++			gc->direction_input = bgpio_dir_in;
++		}
+ 		gc->get_direction = bgpio_get_dir;
+ 	} else {
+ 		if (flags & BGPIOF_NO_OUTPUT)
+--- a/include/linux/gpio/driver.h
++++ b/include/linux/gpio/driver.h
+@@ -690,6 +690,7 @@ int bgpio_init(struct gpio_chip *gc, str
+ #define BGPIOF_READ_OUTPUT_REG_SET	BIT(4) /* reg_set stores output value */
+ #define BGPIOF_NO_OUTPUT		BIT(5) /* only input */
+ #define BGPIOF_NO_SET_ON_INPUT		BIT(6)
++#define BGPIOF_REG_DIRECT		BIT(7) /* ignore shadow registers */
+ 
+ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ 		     irq_hw_number_t hwirq);

+ 20 - 0
target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch

@@ -0,0 +1,20 @@
+From 22ae3b2ee3293278e647877b269a5aebad3f077d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 27 May 2021 11:46:30 +0100
+Subject: [PATCH] Allow RESET_BRCMSTB on ARCH_BCM2835
+
+---
+ drivers/reset/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/reset/Kconfig
++++ b/drivers/reset/Kconfig
+@@ -51,7 +51,7 @@ config RESET_BERLIN
+ 
+ config RESET_BRCMSTB
+ 	tristate "Broadcom STB reset controller"
+-	depends on ARCH_BRCMSTB || COMPILE_TEST
++	depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ 	default ARCH_BRCMSTB
+ 	help
+ 	  This enables the reset controller driver for Broadcom STB SoCs using

+ 1324 - 0
target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch

@@ -0,0 +1,1324 @@
+From af7e60a33f0b5ce84bffb69ba084ba1edd180195 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 9 Jun 2021 15:48:28 +0100
+Subject: [PATCH] pinctrl: bcm2712 pinctrl/pinconf driver
+
+pinctrl: bcm2712: Reject invalid pulls
+
+Reject attempts to set pulls on aon-sgpios, and fix pull shift
+values.
+
+pinctrl: bcm2712: Add 7712 support, fix 2712 count
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl-bcm2712: add EMMC pins so pulls can be set
+
+These pins have pad controls but not mux controls. They look enough like
+GPIOs to squeeze in at the end of the list though.
+
+pinctrl: bcm2712: correct BCM2712C0 AON_GPIO pad pull control offset
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+pinctrl: bcm2712: on C0 the regular GPIO pad control register moves too
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+pinctrl: bcm2712: Implement (partially) pinconf_get
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl: bcm2712: Convert to generic pinconf
+
+Remove the legacy brcm,* pin configuration support and replace it with
+a proper generic pinconf interface, using named functions instead of
+alt function numbers. This is nicer for users, less error-prone, and
+immune to some of the C0->D0 changes.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl: bcm2712: Remove vestigial pull parameter
+
+Now the legacy brcm, pinconf parameters are no longer supported, this
+custom pin config parameter is not needed.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl: bcm2712: Guard against bad func numbers
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl: bcm2712: A better attempt at D0 support
+
+The BCM2712D0 sparse pinctrl maps play havoc with the old GPIO_REGS
+macro, so make the bit positions explicit. And delete the unwanted
+GPIO and pinmux declarations on D0.
+
+Note that a Pi 5 with D0 requires a separate DTS file with "bcm2712d0"
+compatible strings.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pinctrl: bcm2712: Delete base register constants
+
+BCM2712D0 deletes many GPIOs and their associated mux and pad bits,
+so much so that the offsets to the start of the pad control registers
+changes. Remove the constant offsets from the *GPIO_REGS macros,
+compensating by adjusting the per-GPIO values.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/pinctrl/bcm/Kconfig           |    9 +
+ drivers/pinctrl/bcm/Makefile          |    1 +
+ drivers/pinctrl/bcm/pinctrl-bcm2712.c | 1216 +++++++++++++++++++++++++
+ 3 files changed, 1226 insertions(+)
+ create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm2712.c
+
+--- a/drivers/pinctrl/bcm/Kconfig
++++ b/drivers/pinctrl/bcm/Kconfig
+@@ -3,6 +3,15 @@
+ # Broadcom pinctrl drivers
+ #
+ 
++config PINCTRL_BCM2712
++	bool "Broadcom BCM2712 PINCONF driver"
++	depends on OF && (ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST)
++	select PINMUX
++	select PINCONF
++	select GENERIC_PINCONF
++	help
++	   Say Y here to enable the Broadcom BCM2835 GPIO driver.
++
+ config PINCTRL_BCM281XX
+ 	bool "Broadcom BCM281xx pinctrl driver"
+ 	depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST)
+--- a/drivers/pinctrl/bcm/Makefile
++++ b/drivers/pinctrl/bcm/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ # Broadcom pinctrl support
+ 
++obj-$(CONFIG_PINCTRL_BCM2712)		+= pinctrl-bcm2712.o
+ obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
+ obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+ obj-$(CONFIG_PINCTRL_BCM4908)		+= pinctrl-bcm4908.o
+--- /dev/null
++++ b/drivers/pinctrl/bcm/pinctrl-bcm2712.c
+@@ -0,0 +1,1216 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for Broadcom BCM2712 GPIO units (pinctrl only)
++ *
++ * Copyright (C) 2021-3 Raspberry Pi Ltd.
++ * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
++ *
++ * Based heavily on the BCM2835 GPIO & pinctrl driver, which was inspired by:
++ * pinctrl-nomadik.c, please see original file for copyright information
++ * pinctrl-tegra.c, please see original file for copyright information
++ */
++
++#include <linux/bitmap.h>
++#include <linux/bug.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/of_address.h>
++#include <linux/of.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/pinctrl/machine.h>
++#include <linux/pinctrl/pinconf.h>
++#include <linux/pinctrl/pinctrl.h>
++#include <linux/pinctrl/pinmux.h>
++#include <linux/pinctrl/pinconf-generic.h>
++#include <linux/platform_device.h>
++#include <linux/seq_file.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#define MODULE_NAME "pinctrl-bcm2712"
++
++/* Register offsets */
++
++#define BCM2712_PULL_NONE	0
++#define BCM2712_PULL_DOWN	1
++#define BCM2712_PULL_UP		2
++#define BCM2712_PULL_MASK	0x3
++
++#define BCM2712_FSEL_COUNT 9
++#define BCM2712_FSEL_MASK  0xf
++
++#define FUNC(f) \
++	[func_##f] = #f
++#define PIN(i, f1, f2, f3, f4, f5, f6, f7, f8) \
++	[i] = { \
++		.funcs = { \
++			func_##f1, \
++			func_##f2, \
++			func_##f3, \
++			func_##f4, \
++			func_##f5, \
++			func_##f6, \
++			func_##f7, \
++			func_##f8, \
++		}, \
++	}
++
++#define REG_BIT_INVALID 0xffff
++
++#define BIT_TO_REG(b) (((b) >> 5) << 2)
++#define BIT_TO_SHIFT(b) ((b) & 0x1f)
++
++#define GPIO_REGS(n, mr, mb, pr, pb) \
++	[n] = { ((mr)*4)*8 + (mb)*4, ((pr)*4)*8 + (pb)*2 }
++
++#define EMMC_REGS(n, r, b) \
++	[n] = { 0, ((r)*4)*8 + (b)*2 }
++
++#define AGPIO_REGS(n, mr, mb, pr, pb) \
++	[n] = { ((mr)*4)*8 + (mb)*4, ((pr)*4)*8 + (pb)*2 }
++
++#define SGPIO_REGS(n, mr, mb) \
++	[n+32] = { ((mr)*4)*8 + (mb)*4, REG_BIT_INVALID }
++
++#define GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
++#define AGPIO_PIN(a) PINCTRL_PIN(a, "aon_gpio" #a)
++#define SGPIO_PIN(a) PINCTRL_PIN(a+32, "aon_sgpio" #a)
++
++struct pin_regs {
++	u16 mux_bit;
++	u16 pad_bit;
++};
++
++struct bcm2712_pinctrl {
++	struct device *dev;
++	void __iomem *base;
++	struct pinctrl_dev *pctl_dev;
++	struct pinctrl_desc pctl_desc;
++	const struct pin_regs *pin_regs;
++	const struct bcm2712_pin_funcs *pin_funcs;
++	const char *const *gpio_groups;
++	struct pinctrl_gpio_range gpio_range;
++	spinlock_t lock;
++};
++
++struct bcm_plat_data {
++	const struct pinctrl_desc *pctl_desc;
++	const struct pinctrl_gpio_range *gpio_range;
++	const struct pin_regs *pin_regs;
++	const struct bcm2712_pin_funcs *pin_funcs;
++};
++
++struct bcm2712_pin_funcs {
++	u8 funcs[BCM2712_FSEL_COUNT - 1];
++};
++
++enum bcm2712_funcs {
++	func_gpio,
++	func_alt1,
++	func_alt2,
++	func_alt3,
++	func_alt4,
++	func_alt5,
++	func_alt6,
++	func_alt7,
++	func_alt8,
++	func_aon_cpu_standbyb,
++	func_aon_fp_4sec_resetb,
++	func_aon_gpclk,
++	func_aon_pwm,
++	func_arm_jtag,
++	func_aud_fs_clk0,
++	func_avs_pmu_bsc,
++	func_bsc_m0,
++	func_bsc_m1,
++	func_bsc_m2,
++	func_bsc_m3,
++	func_clk_observe,
++	func_ctl_hdmi_5v,
++	func_enet0,
++	func_enet0_mii,
++	func_enet0_rgmii,
++	func_ext_sc_clk,
++	func_fl0,
++	func_fl1,
++	func_gpclk0,
++	func_gpclk1,
++	func_gpclk2,
++	func_hdmi_tx0_auto_i2c,
++	func_hdmi_tx0_bsc,
++	func_hdmi_tx1_auto_i2c,
++	func_hdmi_tx1_bsc,
++	func_i2s_in,
++	func_i2s_out,
++	func_ir_in,
++	func_mtsif,
++	func_mtsif_alt,
++	func_mtsif_alt1,
++	func_pdm,
++	func_pkt,
++	func_pm_led_out,
++	func_sc0,
++	func_sd0,
++	func_sd2,
++	func_sd_card_a,
++	func_sd_card_b,
++	func_sd_card_c,
++	func_sd_card_d,
++	func_sd_card_e,
++	func_sd_card_f,
++	func_sd_card_g,
++	func_spdif_out,
++	func_spi_m,
++	func_spi_s,
++	func_sr_edm_sense,
++	func_te0,
++	func_te1,
++	func_tsio,
++	func_uart0,
++	func_uart1,
++	func_uart2,
++	func_usb_pwr,
++	func_usb_vbus,
++	func_uui,
++	func_vc_i2c0,
++	func_vc_i2c3,
++	func_vc_i2c4,
++	func_vc_i2c5,
++	func_vc_i2csl,
++	func_vc_pcm,
++	func_vc_pwm0,
++	func_vc_pwm1,
++	func_vc_spi0,
++	func_vc_spi3,
++	func_vc_spi4,
++	func_vc_spi5,
++	func_vc_uart0,
++	func_vc_uart2,
++	func_vc_uart3,
++	func_vc_uart4,
++	func__,
++	func_count = func__
++};
++
++static const struct pin_regs bcm2712_c0_gpio_pin_regs[] = {
++	GPIO_REGS(0, 0, 0, 7, 7),
++	GPIO_REGS(1, 0, 1, 7, 8),
++	GPIO_REGS(2, 0, 2, 7, 9),
++	GPIO_REGS(3, 0, 3, 7, 10),
++	GPIO_REGS(4, 0, 4, 7, 11),
++	GPIO_REGS(5, 0, 5, 7, 12),
++	GPIO_REGS(6, 0, 6, 7, 13),
++	GPIO_REGS(7, 0, 7, 7, 14),
++	GPIO_REGS(8, 1, 0, 8, 0),
++	GPIO_REGS(9, 1, 1, 8, 1),
++	GPIO_REGS(10, 1, 2, 8, 2),
++	GPIO_REGS(11, 1, 3, 8, 3),
++	GPIO_REGS(12, 1, 4, 8, 4),
++	GPIO_REGS(13, 1, 5, 8, 5),
++	GPIO_REGS(14, 1, 6, 8, 6),
++	GPIO_REGS(15, 1, 7, 8, 7),
++	GPIO_REGS(16, 2, 0, 8, 8),
++	GPIO_REGS(17, 2, 1, 8, 9),
++	GPIO_REGS(18, 2, 2, 8, 10),
++	GPIO_REGS(19, 2, 3, 8, 11),
++	GPIO_REGS(20, 2, 4, 8, 12),
++	GPIO_REGS(21, 2, 5, 8, 13),
++	GPIO_REGS(22, 2, 6, 8, 14),
++	GPIO_REGS(23, 2, 7, 9, 0),
++	GPIO_REGS(24, 3, 0, 9, 1),
++	GPIO_REGS(25, 3, 1, 9, 2),
++	GPIO_REGS(26, 3, 2, 9, 3),
++	GPIO_REGS(27, 3, 3, 9, 4),
++	GPIO_REGS(28, 3, 4, 9, 5),
++	GPIO_REGS(29, 3, 5, 9, 6),
++	GPIO_REGS(30, 3, 6, 9, 7),
++	GPIO_REGS(31, 3, 7, 9, 8),
++	GPIO_REGS(32, 4, 0, 9, 9),
++	GPIO_REGS(33, 4, 1, 9, 10),
++	GPIO_REGS(34, 4, 2, 9, 11),
++	GPIO_REGS(35, 4, 3, 9, 12),
++	GPIO_REGS(36, 4, 4, 9, 13),
++	GPIO_REGS(37, 4, 5, 9, 14),
++	GPIO_REGS(38, 4, 6, 10, 0),
++	GPIO_REGS(39, 4, 7, 10, 1),
++	GPIO_REGS(40, 5, 0, 10, 2),
++	GPIO_REGS(41, 5, 1, 10, 3),
++	GPIO_REGS(42, 5, 2, 10, 4),
++	GPIO_REGS(43, 5, 3, 10, 5),
++	GPIO_REGS(44, 5, 4, 10, 6),
++	GPIO_REGS(45, 5, 5, 10, 7),
++	GPIO_REGS(46, 5, 6, 10, 8),
++	GPIO_REGS(47, 5, 7, 10, 9),
++	GPIO_REGS(48, 6, 0, 10, 10),
++	GPIO_REGS(49, 6, 1, 10, 11),
++	GPIO_REGS(50, 6, 2, 10, 12),
++	GPIO_REGS(51, 6, 3, 10, 13),
++	GPIO_REGS(52, 6, 4, 10, 14),
++	GPIO_REGS(53, 6, 5, 11, 0),
++	EMMC_REGS(54, 11, 1), /* EMMC_CMD */
++	EMMC_REGS(55, 11, 2), /* EMMC_DS */
++	EMMC_REGS(56, 11, 3), /* EMMC_CLK */
++	EMMC_REGS(57, 11, 4), /* EMMC_DAT0 */
++	EMMC_REGS(58, 11, 5), /* EMMC_DAT1 */
++	EMMC_REGS(59, 11, 6), /* EMMC_DAT2 */
++	EMMC_REGS(60, 11, 7), /* EMMC_DAT3 */
++	EMMC_REGS(61, 11, 8), /* EMMC_DAT4 */
++	EMMC_REGS(62, 11, 9), /* EMMC_DAT5 */
++	EMMC_REGS(63, 11, 10), /* EMMC_DAT6 */
++	EMMC_REGS(64, 11, 11), /* EMMC_DAT7 */
++};
++
++static struct pin_regs bcm2712_c0_aon_gpio_pin_regs[] = {
++	AGPIO_REGS(0, 3, 0, 6, 10),
++	AGPIO_REGS(1, 3, 1, 6, 11),
++	AGPIO_REGS(2, 3, 2, 6, 12),
++	AGPIO_REGS(3, 3, 3, 6, 13),
++	AGPIO_REGS(4, 3, 4, 6, 14),
++	AGPIO_REGS(5, 3, 5, 7, 0),
++	AGPIO_REGS(6, 3, 6, 7, 1),
++	AGPIO_REGS(7, 3, 7, 7, 2),
++	AGPIO_REGS(8, 4, 0, 7, 3),
++	AGPIO_REGS(9, 4, 1, 7, 4),
++	AGPIO_REGS(10, 4, 2, 7, 5),
++	AGPIO_REGS(11, 4, 3, 7, 6),
++	AGPIO_REGS(12, 4, 4, 7, 7),
++	AGPIO_REGS(13, 4, 5, 7, 8),
++	AGPIO_REGS(14, 4, 6, 7, 9),
++	AGPIO_REGS(15, 4, 7, 7, 10),
++	AGPIO_REGS(16, 5, 0, 7, 11),
++	SGPIO_REGS(0, 0, 0),
++	SGPIO_REGS(1, 0, 1),
++	SGPIO_REGS(2, 0, 2),
++	SGPIO_REGS(3, 0, 3),
++	SGPIO_REGS(4, 1, 0),
++	SGPIO_REGS(5, 2, 0),
++};
++
++static const struct pinctrl_pin_desc bcm2712_c0_gpio_pins[] = {
++	GPIO_PIN(0),
++	GPIO_PIN(1),
++	GPIO_PIN(2),
++	GPIO_PIN(3),
++	GPIO_PIN(4),
++	GPIO_PIN(5),
++	GPIO_PIN(6),
++	GPIO_PIN(7),
++	GPIO_PIN(8),
++	GPIO_PIN(9),
++	GPIO_PIN(10),
++	GPIO_PIN(11),
++	GPIO_PIN(12),
++	GPIO_PIN(13),
++	GPIO_PIN(14),
++	GPIO_PIN(15),
++	GPIO_PIN(16),
++	GPIO_PIN(17),
++	GPIO_PIN(18),
++	GPIO_PIN(19),
++	GPIO_PIN(20),
++	GPIO_PIN(21),
++	GPIO_PIN(22),
++	GPIO_PIN(23),
++	GPIO_PIN(24),
++	GPIO_PIN(25),
++	GPIO_PIN(26),
++	GPIO_PIN(27),
++	GPIO_PIN(28),
++	GPIO_PIN(29),
++	GPIO_PIN(30),
++	GPIO_PIN(31),
++	GPIO_PIN(32),
++	GPIO_PIN(33),
++	GPIO_PIN(34),
++	GPIO_PIN(35),
++	GPIO_PIN(36),
++	GPIO_PIN(37),
++	GPIO_PIN(38),
++	GPIO_PIN(39),
++	GPIO_PIN(40),
++	GPIO_PIN(41),
++	GPIO_PIN(42),
++	GPIO_PIN(43),
++	GPIO_PIN(44),
++	GPIO_PIN(45),
++	GPIO_PIN(46),
++	GPIO_PIN(47),
++	GPIO_PIN(48),
++	GPIO_PIN(49),
++	GPIO_PIN(50),
++	GPIO_PIN(51),
++	GPIO_PIN(52),
++	GPIO_PIN(53),
++	PINCTRL_PIN(54, "emmc_cmd"),
++	PINCTRL_PIN(55, "emmc_ds"),
++	PINCTRL_PIN(56, "emmc_clk"),
++	PINCTRL_PIN(57, "emmc_dat0"),
++	PINCTRL_PIN(58, "emmc_dat1"),
++	PINCTRL_PIN(59, "emmc_dat2"),
++	PINCTRL_PIN(60, "emmc_dat3"),
++	PINCTRL_PIN(61, "emmc_dat4"),
++	PINCTRL_PIN(62, "emmc_dat5"),
++	PINCTRL_PIN(63, "emmc_dat6"),
++	PINCTRL_PIN(64, "emmc_dat7"),
++};
++
++static struct pinctrl_pin_desc bcm2712_c0_aon_gpio_pins[] = {
++	AGPIO_PIN(0),
++	AGPIO_PIN(1),
++	AGPIO_PIN(2),
++	AGPIO_PIN(3),
++	AGPIO_PIN(4),
++	AGPIO_PIN(5),
++	AGPIO_PIN(6),
++	AGPIO_PIN(7),
++	AGPIO_PIN(8),
++	AGPIO_PIN(9),
++	AGPIO_PIN(10),
++	AGPIO_PIN(11),
++	AGPIO_PIN(12),
++	AGPIO_PIN(13),
++	AGPIO_PIN(14),
++	AGPIO_PIN(15),
++	AGPIO_PIN(16),
++	SGPIO_PIN(0),
++	SGPIO_PIN(1),
++	SGPIO_PIN(2),
++	SGPIO_PIN(3),
++	SGPIO_PIN(4),
++	SGPIO_PIN(5),
++};
++
++static const struct pin_regs bcm2712_d0_gpio_pin_regs[] = {
++	GPIO_REGS(1, 0, 0, 4, 5),
++	GPIO_REGS(2, 0, 1, 4, 6),
++	GPIO_REGS(3, 0, 2, 4, 7),
++	GPIO_REGS(4, 0, 3, 4, 8),
++	GPIO_REGS(10, 0, 4, 4, 9),
++	GPIO_REGS(11, 0, 5, 4, 10),
++	GPIO_REGS(12, 0, 6, 4, 11),
++	GPIO_REGS(13, 0, 7, 4, 12),
++	GPIO_REGS(14, 1, 0, 4, 13),
++	GPIO_REGS(15, 1, 1, 4, 14),
++	GPIO_REGS(18, 1, 2, 5, 0),
++	GPIO_REGS(19, 1, 3, 5, 1),
++	GPIO_REGS(20, 1, 4, 5, 2),
++	GPIO_REGS(21, 1, 5, 5, 3),
++	GPIO_REGS(22, 1, 6, 5, 4),
++	GPIO_REGS(23, 1, 7, 5, 5),
++	GPIO_REGS(24, 2, 0, 5, 6),
++	GPIO_REGS(25, 2, 1, 5, 7),
++	GPIO_REGS(26, 2, 2, 5, 8),
++	GPIO_REGS(27, 2, 3, 5, 9),
++	GPIO_REGS(28, 2, 4, 5, 10),
++	GPIO_REGS(29, 2, 5, 5, 11),
++	GPIO_REGS(30, 2, 6, 5, 12),
++	GPIO_REGS(31, 2, 7, 5, 13),
++	GPIO_REGS(32, 3, 0, 5, 14),
++	GPIO_REGS(33, 3, 1, 6, 0),
++	GPIO_REGS(34, 3, 2, 6, 1),
++	GPIO_REGS(35, 3, 3, 6, 2),
++};
++
++static struct pin_regs bcm2712_d0_aon_gpio_pin_regs[] = {
++	AGPIO_REGS(0, 3, 0, 5, 9),
++	AGPIO_REGS(1, 3, 1, 5, 10),
++	AGPIO_REGS(2, 3, 2, 5, 11),
++	AGPIO_REGS(3, 3, 3, 5, 12),
++	AGPIO_REGS(4, 3, 4, 5, 13),
++	AGPIO_REGS(5, 3, 5, 5, 14),
++	AGPIO_REGS(6, 3, 6, 6, 0),
++	AGPIO_REGS(8, 3, 7, 6, 1),
++	AGPIO_REGS(9, 4, 0, 6, 2),
++	AGPIO_REGS(12, 4, 1, 6, 3),
++	AGPIO_REGS(13, 4, 2, 6, 4),
++	AGPIO_REGS(14, 4, 3, 6, 5),
++	SGPIO_REGS(0, 0, 0),
++	SGPIO_REGS(1, 0, 1),
++	SGPIO_REGS(2, 0, 2),
++	SGPIO_REGS(3, 0, 3),
++	SGPIO_REGS(4, 1, 0),
++	SGPIO_REGS(5, 2, 0),
++};
++
++static const struct pinctrl_pin_desc bcm2712_d0_gpio_pins[] = {
++	GPIO_PIN(1),
++	GPIO_PIN(2),
++	GPIO_PIN(3),
++	GPIO_PIN(4),
++	GPIO_PIN(10),
++	GPIO_PIN(11),
++	GPIO_PIN(12),
++	GPIO_PIN(13),
++	GPIO_PIN(14),
++	GPIO_PIN(15),
++	GPIO_PIN(18),
++	GPIO_PIN(19),
++	GPIO_PIN(20),
++	GPIO_PIN(21),
++	GPIO_PIN(22),
++	GPIO_PIN(23),
++	GPIO_PIN(24),
++	GPIO_PIN(25),
++	GPIO_PIN(26),
++	GPIO_PIN(27),
++	GPIO_PIN(28),
++	GPIO_PIN(29),
++	GPIO_PIN(30),
++	GPIO_PIN(31),
++	GPIO_PIN(32),
++	GPIO_PIN(33),
++	GPIO_PIN(34),
++	GPIO_PIN(35),
++};
++
++static struct pinctrl_pin_desc bcm2712_d0_aon_gpio_pins[] = {
++	AGPIO_PIN(0),
++	AGPIO_PIN(1),
++	AGPIO_PIN(2),
++	AGPIO_PIN(3),
++	AGPIO_PIN(4),
++	AGPIO_PIN(5),
++	AGPIO_PIN(6),
++	AGPIO_PIN(8),
++	AGPIO_PIN(9),
++	AGPIO_PIN(12),
++	AGPIO_PIN(13),
++	AGPIO_PIN(14),
++	SGPIO_PIN(0),
++	SGPIO_PIN(1),
++	SGPIO_PIN(2),
++	SGPIO_PIN(3),
++	SGPIO_PIN(4),
++	SGPIO_PIN(5),
++};
++
++static const char * const bcm2712_func_names[] = {
++	FUNC(gpio),
++	FUNC(alt1),
++	FUNC(alt2),
++	FUNC(alt3),
++	FUNC(alt4),
++	FUNC(alt5),
++	FUNC(alt6),
++	FUNC(alt7),
++	FUNC(alt8),
++	FUNC(aon_cpu_standbyb),
++	FUNC(aon_fp_4sec_resetb),
++	FUNC(aon_gpclk),
++	FUNC(aon_pwm),
++	FUNC(arm_jtag),
++	FUNC(aud_fs_clk0),
++	FUNC(avs_pmu_bsc),
++	FUNC(bsc_m0),
++	FUNC(bsc_m1),
++	FUNC(bsc_m2),
++	FUNC(bsc_m3),
++	FUNC(clk_observe),
++	FUNC(ctl_hdmi_5v),
++	FUNC(enet0),
++	FUNC(enet0_mii),
++	FUNC(enet0_rgmii),
++	FUNC(ext_sc_clk),
++	FUNC(fl0),
++	FUNC(fl1),
++	FUNC(gpclk0),
++	FUNC(gpclk1),
++	FUNC(gpclk2),
++	FUNC(hdmi_tx0_auto_i2c),
++	FUNC(hdmi_tx0_bsc),
++	FUNC(hdmi_tx1_auto_i2c),
++	FUNC(hdmi_tx1_bsc),
++	FUNC(i2s_in),
++	FUNC(i2s_out),
++	FUNC(ir_in),
++	FUNC(mtsif),
++	FUNC(mtsif_alt),
++	FUNC(mtsif_alt1),
++	FUNC(pdm),
++	FUNC(pkt),
++	FUNC(pm_led_out),
++	FUNC(sc0),
++	FUNC(sd0),
++	FUNC(sd2),
++	FUNC(sd_card_a),
++	FUNC(sd_card_b),
++	FUNC(sd_card_c),
++	FUNC(sd_card_d),
++	FUNC(sd_card_e),
++	FUNC(sd_card_f),
++	FUNC(sd_card_g),
++	FUNC(spdif_out),
++	FUNC(spi_m),
++	FUNC(spi_s),
++	FUNC(sr_edm_sense),
++	FUNC(te0),
++	FUNC(te1),
++	FUNC(tsio),
++	FUNC(uart0),
++	FUNC(uart1),
++	FUNC(uart2),
++	FUNC(usb_pwr),
++	FUNC(usb_vbus),
++	FUNC(uui),
++	FUNC(vc_i2c0),
++	FUNC(vc_i2c3),
++	FUNC(vc_i2c4),
++	FUNC(vc_i2c5),
++	FUNC(vc_i2csl),
++	FUNC(vc_pcm),
++	FUNC(vc_pwm0),
++	FUNC(vc_pwm1),
++	FUNC(vc_spi0),
++	FUNC(vc_spi3),
++	FUNC(vc_spi4),
++	FUNC(vc_spi5),
++	FUNC(vc_uart0),
++	FUNC(vc_uart2),
++	FUNC(vc_uart3),
++	FUNC(vc_uart4),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_aon_gpio_pin_funcs[] = {
++	PIN(0, ir_in, vc_spi0, vc_uart3, vc_i2c3, te0, vc_i2c0, _, _),
++	PIN(1, vc_pwm0, vc_spi0, vc_uart3, vc_i2c3, te1, aon_pwm, vc_i2c0, vc_pwm1),
++	PIN(2, vc_pwm0, vc_spi0, vc_uart3, ctl_hdmi_5v, fl0, aon_pwm, ir_in, vc_pwm1),
++	PIN(3, ir_in, vc_spi0, vc_uart3, aon_fp_4sec_resetb, fl1, sd_card_g, aon_gpclk, _),
++	PIN(4, gpclk0, vc_spi0, vc_i2csl, aon_gpclk, pm_led_out, aon_pwm, sd_card_g, vc_pwm0),
++	PIN(5, gpclk1, ir_in, vc_i2csl, clk_observe, aon_pwm, sd_card_g, vc_pwm0, _),
++	PIN(6, uart1, vc_uart4, gpclk2, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _),
++	PIN(7, uart1, vc_uart4, gpclk0, aon_pwm, vc_uart0, vc_spi3, _, _),
++	PIN(8, uart1, vc_uart4, vc_i2csl, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _),
++	PIN(9, uart1, vc_uart4, vc_i2csl, aon_pwm, vc_uart0, vc_spi3, _, _),
++	PIN(10, tsio, ctl_hdmi_5v, sc0, spdif_out, vc_spi5, usb_pwr, aon_gpclk, sd_card_f),
++	PIN(11, tsio, uart0, sc0, aud_fs_clk0, vc_spi5, usb_vbus, vc_uart2, sd_card_f),
++	PIN(12, tsio, uart0, vc_uart0, tsio, vc_spi5, usb_pwr, vc_uart2, sd_card_f),
++	PIN(13, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3),
++	PIN(14, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3),
++	PIN(15, ir_in, aon_fp_4sec_resetb, vc_uart0, pm_led_out, ctl_hdmi_5v, aon_pwm, aon_gpclk, _),
++	PIN(16, aon_cpu_standbyb, gpclk0, pm_led_out, ctl_hdmi_5v, vc_pwm0, usb_pwr, aud_fs_clk0, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_aon_sgpio_pin_funcs[] = {
++	PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++	PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++	PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, ctl_hdmi_5v, _, _, _),
++	PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, _, _, _, _),
++	PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c5, ctl_hdmi_5v, _, _, _, _),
++	PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c5, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_gpio_pin_funcs[] = {
++	PIN(0, bsc_m3, vc_i2c0, gpclk0, enet0, vc_pwm1, vc_spi0, ir_in, _),
++	PIN(1, bsc_m3, vc_i2c0, gpclk1, enet0, vc_pwm1, sr_edm_sense, vc_spi0, vc_uart3),
++	PIN(2, pdm, i2s_in, gpclk2, vc_spi4, pkt, vc_spi0, vc_uart3, _),
++	PIN(3, pdm, i2s_in, vc_spi4, pkt, vc_spi0, vc_uart3, _, _),
++	PIN(4, pdm, i2s_in, arm_jtag, vc_spi4, pkt, vc_spi0, vc_uart3, _),
++	PIN(5, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5),
++	PIN(6, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5),
++	PIN(7, i2s_out, spdif_out, arm_jtag, sd_card_e, vc_i2c3, enet0_rgmii, vc_pcm, vc_spi4),
++	PIN(8, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, vc_i2c3, enet0_mii, vc_pcm, vc_spi4),
++	PIN(9, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, enet0_mii, sd_card_c, vc_spi4, _),
++	PIN(10, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4),
++	PIN(11, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4),
++	PIN(12, spi_s, mtsif_alt1, i2s_in, i2s_out, vc_spi5, vc_i2csl, sd0, sd_card_d),
++	PIN(13, spi_s, mtsif_alt1, i2s_out, usb_vbus, vc_spi5, vc_i2csl, sd0, sd_card_d),
++	PIN(14, spi_s, vc_i2csl, enet0_rgmii, arm_jtag, vc_spi5, vc_pwm0, vc_i2c4, sd_card_d),
++	PIN(15, spi_s, vc_i2csl, vc_spi3, arm_jtag, vc_pwm0, vc_i2c4, gpclk0, _),
++	PIN(16, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, gpclk1, _),
++	PIN(17, sd_card_b, i2s_out, vc_spi3, i2s_in, ext_sc_clk, sd0, enet0_rgmii, gpclk2),
++	PIN(18, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, vc_pwm1, _),
++	PIN(19, sd_card_b, usb_pwr, vc_spi3, pkt, spdif_out, sd0, ir_in, vc_pwm1),
++	PIN(20, sd_card_b, uui, vc_uart0, arm_jtag, uart2, usb_pwr, vc_pcm, vc_uart4),
++	PIN(21, usb_pwr, uui, vc_uart0, arm_jtag, uart2, sd_card_b, vc_pcm, vc_uart4),
++	PIN(22, usb_pwr, enet0, vc_uart0, mtsif, uart2, usb_vbus, vc_pcm, vc_i2c5),
++	PIN(23, usb_vbus, enet0, vc_uart0, mtsif, uart2, i2s_out, vc_pcm, vc_i2c5),
++	PIN(24, mtsif, pkt, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3, _),
++	PIN(25, mtsif, pkt, sc0, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3),
++	PIN(26, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _),
++	PIN(27, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _),
++	PIN(28, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _),
++	PIN(29, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _),
++	PIN(30, mtsif, pkt, sc0, sd2, enet0_rgmii, gpclk0, vc_pwm0, _),
++	PIN(31, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_pwm0, _),
++	PIN(32, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_uart3, _),
++	PIN(33, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_uart3, _, _),
++	PIN(34, mtsif, pkt, ext_sc_clk, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _),
++	PIN(35, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _, _),
++	PIN(36, sd0, mtsif, sc0, i2s_in, vc_uart3, vc_uart2, _, _),
++	PIN(37, sd0, mtsif, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _),
++	PIN(38, sd0, mtsif_alt, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _),
++	PIN(39, sd0, mtsif_alt, sc0, vc_spi0, vc_uart3, vc_uart2, _, _),
++	PIN(40, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _),
++	PIN(41, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _),
++	PIN(42, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++	PIN(43, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++	PIN(44, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++	PIN(45, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++	PIN(46, vc_spi0, mtsif_alt, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m, _),
++	PIN(47, enet0, mtsif_alt, i2s_out, mtsif_alt1, arm_jtag, _, _, _),
++	PIN(48, sc0, usb_pwr, spdif_out, mtsif, _, _, _, _),
++	PIN(49, sc0, usb_pwr, aud_fs_clk0, mtsif, _, _, _, _),
++	PIN(50, sc0, usb_vbus, sc0, _, _, _, _, _),
++	PIN(51, sc0, enet0, sc0, sr_edm_sense, _, _, _, _),
++	PIN(52, sc0, enet0, vc_pwm1, _, _, _, _, _),
++	PIN(53, sc0, enet0_rgmii, ext_sc_clk, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_aon_gpio_pin_funcs[] = {
++	PIN(0, ir_in, vc_spi0, vc_uart0, vc_i2c3, uart0, vc_i2c0, _, _),
++	PIN(1, vc_pwm0, vc_spi0, vc_uart0, vc_i2c3, uart0, aon_pwm, vc_i2c0, vc_pwm1),
++	PIN(2, vc_pwm0, vc_spi0, vc_uart0, ctl_hdmi_5v, uart0, aon_pwm, ir_in, vc_pwm1),
++	PIN(3, ir_in, vc_spi0, vc_uart0, uart0, sd_card_g, aon_gpclk, _, _),
++	PIN(4, gpclk0, vc_spi0, pm_led_out, aon_pwm, sd_card_g, vc_pwm0, _, _),
++	PIN(5, gpclk1, ir_in, aon_pwm, sd_card_g, vc_pwm0, _, _, _),
++	PIN(6, uart1, vc_uart2, ctl_hdmi_5v, gpclk2, vc_spi3, _, _, _),
++	PIN(7, _, _, _, _, _, _, _, _),
++	PIN(8, uart1, vc_uart2, ctl_hdmi_5v, vc_spi0, vc_spi3, _, _, _),
++	PIN(9, uart1, vc_uart2, vc_uart0, aon_pwm, vc_spi0, vc_uart2, vc_spi3, _),
++	PIN(10, _, _, _, _, _, _, _, _),
++	PIN(11, _, _, _, _, _, _, _, _),
++	PIN(12, uart1, vc_uart2, vc_uart0, vc_spi0, usb_pwr, vc_uart2, vc_spi3, _),
++	PIN(13, bsc_m1, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3, _),
++	PIN(14, bsc_m1, aon_gpclk, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_aon_sgpio_pin_funcs[] = {
++	PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++	PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++	PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, ctl_hdmi_5v, _, _, _),
++	PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, _, _, _, _),
++	PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c3, ctl_hdmi_5v, _, _, _, _),
++	PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c3, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_gpio_pin_funcs[] = {
++	PIN(1, vc_i2c0, usb_pwr, gpclk0, sd_card_e, vc_spi3, sr_edm_sense, vc_spi0, vc_uart0),
++	PIN(2, vc_i2c0, usb_pwr, gpclk1, sd_card_e, vc_spi3, clk_observe, vc_spi0, vc_uart0),
++	PIN(3, vc_i2c3, usb_vbus, gpclk2, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _),
++	PIN(4, vc_i2c3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _),
++	PIN(10, bsc_m3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, gpclk0, _, _),
++	PIN(11, bsc_m3, vc_spi3, clk_observe, sd_card_c, gpclk1, _, _, _),
++	PIN(12, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _),
++	PIN(13, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _),
++	PIN(14, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, sd_card_d, _, _),
++	PIN(15, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, gpclk0, _, _),
++	PIN(18, sd_card_f, vc_pwm1, _, _, _, _, _, _),
++	PIN(19, sd_card_f, usb_pwr, vc_pwm1, _, _, _, _, _),
++	PIN(20, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _),
++	PIN(21, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _),
++	PIN(22, sd_card_f, vc_uart0, vc_i2c3, _, _, _, _, _),
++	PIN(23, vc_uart0, vc_i2c3, _, _, _, _, _, _),
++	PIN(24, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _),
++	PIN(25, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _),
++	PIN(26, sd_card_b, vc_spi0, arm_jtag, uart0, usb_vbus, vc_uart2, vc_spi0, _),
++	PIN(27, sd_card_b, vc_spi0, arm_jtag, uart0, vc_uart2, vc_spi0, _, _),
++	PIN(28, sd_card_b, vc_spi0, arm_jtag, vc_i2c0, vc_spi0, _, _, _),
++	PIN(29, arm_jtag, vc_i2c0, vc_spi0, _, _, _, _, _),
++	PIN(30, sd2, gpclk0, vc_pwm0, _, _, _, _, _),
++	PIN(31, sd2, vc_spi3, vc_pwm0, _, _, _, _, _),
++	PIN(32, sd2, vc_spi3, vc_uart3, _, _, _, _, _),
++	PIN(33, sd2, vc_spi3, vc_uart3, _, _, _, _, _),
++	PIN(34, sd2, vc_spi3, vc_i2c5, _, _, _, _, _),
++	PIN(35, sd2, vc_spi3, vc_i2c5, _, _, _, _, _),
++};
++
++static inline u32 bcm2712_reg_rd(struct bcm2712_pinctrl *pc, unsigned reg)
++{
++	return readl(pc->base + reg);
++}
++
++static inline void bcm2712_reg_wr(struct bcm2712_pinctrl *pc, unsigned reg,
++		u32 val)
++{
++	writel(val, pc->base + reg);
++}
++
++static enum bcm2712_funcs bcm2712_pinctrl_fsel_get(
++	struct bcm2712_pinctrl *pc, unsigned pin)
++{
++	u32 bit = pc->pin_regs[pin].mux_bit;
++	enum bcm2712_funcs func;
++	int fsel;
++	u32 val;
++
++	if (!bit)
++		return func_gpio;
++
++	val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++	fsel = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK;
++	func = pc->pin_funcs[pin].funcs[fsel];
++	if (func >= func_count)
++		func = (enum bcm2712_funcs)fsel;
++
++	dev_dbg(pc->dev, "get %04x: %08x (%u => %s)\n",
++		BIT_TO_REG(bit), val, pin,
++		bcm2712_func_names[func]);
++
++	return func;
++}
++
++static void bcm2712_pinctrl_fsel_set(
++	struct bcm2712_pinctrl *pc, unsigned pin,
++	enum bcm2712_funcs func)
++{
++	u32 bit = pc->pin_regs[pin].mux_bit, val;
++	const u8 *pin_funcs;
++	unsigned long flags;
++	int fsel;
++	int cur;
++	int i;
++
++	if (!bit || func >= func_count)
++		return;
++
++	fsel = BCM2712_FSEL_COUNT;
++
++	if (func >= BCM2712_FSEL_COUNT) {
++		/* Convert to an fsel number */
++		pin_funcs = pc->pin_funcs[pin].funcs;
++		for (i = 1; i < BCM2712_FSEL_COUNT; i++) {
++			if (pin_funcs[i - 1] == func) {
++				fsel = i;
++				break;
++			}
++		}
++	} else {
++		fsel = (enum bcm2712_funcs)func;
++	}
++	if (fsel >= BCM2712_FSEL_COUNT)
++		return;
++
++	spin_lock_irqsave(&pc->lock, flags);
++
++	val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++	cur = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK;
++
++	dev_dbg(pc->dev, "read %04x: %08x (%u => %s)\n",
++		BIT_TO_REG(bit), val, pin,
++		bcm2712_func_names[cur]);
++
++	if (cur != fsel) {
++		val &= ~(BCM2712_FSEL_MASK << BIT_TO_SHIFT(bit));
++		val |= fsel << BIT_TO_SHIFT(bit);
++
++		dev_dbg(pc->dev, "write %04x: %08x (%u <= %s)\n",
++			BIT_TO_REG(bit), val, pin,
++			bcm2712_func_names[fsel]);
++		bcm2712_reg_wr(pc, BIT_TO_REG(bit), val);
++	}
++
++	spin_unlock_irqrestore(&pc->lock, flags);
++}
++
++static int bcm2712_pctl_get_groups_count(struct pinctrl_dev *pctldev)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	return pc->pctl_desc.npins;
++}
++
++static const char *bcm2712_pctl_get_group_name(struct pinctrl_dev *pctldev,
++		unsigned selector)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	return pc->gpio_groups[selector];
++}
++
++static int bcm2712_pctl_get_group_pins(struct pinctrl_dev *pctldev,
++		unsigned selector,
++		const unsigned **pins,
++		unsigned *num_pins)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	*pins = &pc->pctl_desc.pins[selector].number;
++	*num_pins = 1;
++
++	return 0;
++}
++
++static void bcm2712_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
++		struct seq_file *s,
++		unsigned offset)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	enum bcm2712_funcs fsel = bcm2712_pinctrl_fsel_get(pc, offset);
++	const char *fname = bcm2712_func_names[fsel];
++
++	seq_printf(s, "function %s", fname);
++}
++
++static void bcm2712_pctl_dt_free_map(struct pinctrl_dev *pctldev,
++		struct pinctrl_map *maps, unsigned num_maps)
++{
++	int i;
++
++	for (i = 0; i < num_maps; i++)
++		if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
++			kfree(maps[i].data.configs.configs);
++
++	kfree(maps);
++}
++
++static const struct pinctrl_ops bcm2712_pctl_ops = {
++	.get_groups_count = bcm2712_pctl_get_groups_count,
++	.get_group_name = bcm2712_pctl_get_group_name,
++	.get_group_pins = bcm2712_pctl_get_group_pins,
++	.pin_dbg_show = bcm2712_pctl_pin_dbg_show,
++	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
++	.dt_free_map = bcm2712_pctl_dt_free_map,
++};
++
++static int bcm2712_pmx_free(struct pinctrl_dev *pctldev,
++		unsigned offset)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	/* disable by setting to GPIO */
++	bcm2712_pinctrl_fsel_set(pc, offset, func_gpio);
++	return 0;
++}
++
++static int bcm2712_pmx_get_functions_count(struct pinctrl_dev *pctldev)
++{
++	return func_count;
++}
++
++static const char *bcm2712_pmx_get_function_name(struct pinctrl_dev *pctldev,
++		unsigned selector)
++{
++	return (selector < func_count) ? bcm2712_func_names[selector] : NULL;
++}
++
++static int bcm2712_pmx_get_function_groups(struct pinctrl_dev *pctldev,
++		unsigned selector,
++		const char * const **groups,
++		unsigned * const num_groups)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	/* every pin can do every function */
++	*groups = pc->gpio_groups;
++	*num_groups = pc->pctl_desc.npins;
++
++	return 0;
++}
++
++static int bcm2712_pmx_set(struct pinctrl_dev *pctldev,
++		unsigned func_selector,
++		unsigned group_selector)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	bcm2712_pinctrl_fsel_set(pc, group_selector, func_selector);
++
++	return 0;
++}
++static int bcm2712_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
++					   struct pinctrl_gpio_range *range,
++					   unsigned pin)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	bcm2712_pinctrl_fsel_set(pc, pin, func_gpio);
++
++	return 0;
++}
++
++static void bcm2712_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
++		struct pinctrl_gpio_range *range,
++		unsigned offset)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	/* disable by setting to GPIO */
++	bcm2712_pinctrl_fsel_set(pc, offset, func_gpio);
++}
++
++static const struct pinmux_ops bcm2712_pmx_ops = {
++	.free = bcm2712_pmx_free,
++	.get_functions_count = bcm2712_pmx_get_functions_count,
++	.get_function_name = bcm2712_pmx_get_function_name,
++	.get_function_groups = bcm2712_pmx_get_function_groups,
++	.set_mux = bcm2712_pmx_set,
++	.gpio_request_enable = bcm2712_pmx_gpio_request_enable,
++	.gpio_disable_free = bcm2712_pmx_gpio_disable_free,
++};
++
++static unsigned int bcm2712_pull_config_get(struct bcm2712_pinctrl *pc,
++					    unsigned int pin)
++{
++	u32 bit = pc->pin_regs[pin].pad_bit, val;
++
++	if (unlikely(bit == REG_BIT_INVALID))
++	    return BCM2712_PULL_NONE;
++
++	val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++	return (val >> BIT_TO_SHIFT(bit)) & BCM2712_PULL_MASK;
++}
++
++static void bcm2712_pull_config_set(struct bcm2712_pinctrl *pc,
++				    unsigned int pin, unsigned int arg)
++{
++	u32 bit = pc->pin_regs[pin].pad_bit, val;
++	unsigned long flags;
++
++	if (unlikely(bit == REG_BIT_INVALID)) {
++	    dev_warn(pc->dev, "can't set pulls for %s\n", pc->gpio_groups[pin]);
++	    return;
++	}
++
++	spin_lock_irqsave(&pc->lock, flags);
++
++	val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++	val &= ~(BCM2712_PULL_MASK << BIT_TO_SHIFT(bit));
++	val |= (arg << BIT_TO_SHIFT(bit));
++	bcm2712_reg_wr(pc, BIT_TO_REG(bit), val);
++
++	spin_unlock_irqrestore(&pc->lock, flags);
++}
++
++static int bcm2712_pinconf_get(struct pinctrl_dev *pctldev,
++			unsigned pin, unsigned long *config)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	enum pin_config_param param = pinconf_to_config_param(*config);
++	u32 arg;
++
++	switch (param) {
++	case PIN_CONFIG_BIAS_DISABLE:
++		arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_NONE);
++		break;
++	case PIN_CONFIG_BIAS_PULL_DOWN:
++		arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_DOWN);
++		break;
++	case PIN_CONFIG_BIAS_PULL_UP:
++		arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_UP);
++		break;
++	default:
++		return -ENOTSUPP;
++	}
++
++	*config = pinconf_to_config_packed(param, arg);
++
++	return -ENOTSUPP;
++}
++
++static int bcm2712_pinconf_set(struct pinctrl_dev *pctldev,
++			       unsigned int pin, unsigned long *configs,
++			       unsigned int num_configs)
++{
++	struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	u32 param, arg;
++	int i;
++
++	for (i = 0; i < num_configs; i++) {
++		param = pinconf_to_config_param(configs[i]);
++		arg = pinconf_to_config_argument(configs[i]);
++
++		switch (param) {
++		case PIN_CONFIG_BIAS_DISABLE:
++			bcm2712_pull_config_set(pc, pin, BCM2712_PULL_NONE);
++			break;
++		case PIN_CONFIG_BIAS_PULL_DOWN:
++			bcm2712_pull_config_set(pc, pin, BCM2712_PULL_DOWN);
++			break;
++		case PIN_CONFIG_BIAS_PULL_UP:
++			bcm2712_pull_config_set(pc, pin, BCM2712_PULL_UP);
++			break;
++		default:
++			return -ENOTSUPP;
++		}
++	} /* for each config */
++
++	return 0;
++}
++
++static const struct pinconf_ops bcm2712_pinconf_ops = {
++	.is_generic = true,
++	.pin_config_get = bcm2712_pinconf_get,
++	.pin_config_set = bcm2712_pinconf_set,
++};
++
++static const struct pinctrl_desc bcm2712_c0_pinctrl_desc = {
++	.name = "pinctrl-bcm2712",
++	.pins = bcm2712_c0_gpio_pins,
++	.npins = ARRAY_SIZE(bcm2712_c0_gpio_pins),
++	.pctlops = &bcm2712_pctl_ops,
++	.pmxops = &bcm2712_pmx_ops,
++	.confops = &bcm2712_pinconf_ops,
++	.owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_c0_aon_pinctrl_desc = {
++	.name = "aon-pinctrl-bcm2712",
++	.pins = bcm2712_c0_aon_gpio_pins,
++	.npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins),
++	.pctlops = &bcm2712_pctl_ops,
++	.pmxops = &bcm2712_pmx_ops,
++	.confops = &bcm2712_pinconf_ops,
++	.owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_d0_pinctrl_desc = {
++	.name = "pinctrl-bcm2712",
++	.pins = bcm2712_d0_gpio_pins,
++	.npins = ARRAY_SIZE(bcm2712_d0_gpio_pins),
++	.pctlops = &bcm2712_pctl_ops,
++	.pmxops = &bcm2712_pmx_ops,
++	.confops = &bcm2712_pinconf_ops,
++	.owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_d0_aon_pinctrl_desc = {
++	.name = "aon-pinctrl-bcm2712",
++	.pins = bcm2712_d0_aon_gpio_pins,
++	.npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins),
++	.pctlops = &bcm2712_pctl_ops,
++	.pmxops = &bcm2712_pmx_ops,
++	.confops = &bcm2712_pinconf_ops,
++	.owner = THIS_MODULE,
++};
++
++static const struct pinctrl_gpio_range bcm2712_c0_pinctrl_gpio_range = {
++	.name = "pinctrl-bcm2712",
++	.npins = ARRAY_SIZE(bcm2712_c0_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_c0_aon_pinctrl_gpio_range = {
++	.name = "aon-pinctrl-bcm2712",
++	.npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_d0_pinctrl_gpio_range = {
++	.name = "pinctrl-bcm2712",
++	.npins = ARRAY_SIZE(bcm2712_d0_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_d0_aon_pinctrl_gpio_range = {
++	.name = "aon-pinctrl-bcm2712",
++	.npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins),
++};
++
++static const struct bcm_plat_data bcm2712_c0_plat_data = {
++	.pctl_desc = &bcm2712_c0_pinctrl_desc,
++	.gpio_range = &bcm2712_c0_pinctrl_gpio_range,
++	.pin_regs = bcm2712_c0_gpio_pin_regs,
++	.pin_funcs = bcm2712_c0_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_c0_aon_plat_data = {
++	.pctl_desc = &bcm2712_c0_aon_pinctrl_desc,
++	.gpio_range = &bcm2712_c0_aon_pinctrl_gpio_range,
++	.pin_regs = bcm2712_c0_aon_gpio_pin_regs,
++	.pin_funcs = bcm2712_c0_aon_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_d0_plat_data = {
++	.pctl_desc = &bcm2712_d0_pinctrl_desc,
++	.gpio_range = &bcm2712_d0_pinctrl_gpio_range,
++	.pin_regs = bcm2712_d0_gpio_pin_regs,
++	.pin_funcs = bcm2712_d0_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_d0_aon_plat_data = {
++	.pctl_desc = &bcm2712_d0_aon_pinctrl_desc,
++	.gpio_range = &bcm2712_d0_aon_pinctrl_gpio_range,
++	.pin_regs = bcm2712_d0_aon_gpio_pin_regs,
++	.pin_funcs = bcm2712_d0_aon_gpio_pin_funcs,
++};
++
++static const struct of_device_id bcm2712_pinctrl_match[] = {
++	{
++		.compatible = "brcm,bcm2712-pinctrl",
++		.data = &bcm2712_c0_plat_data,
++	},
++	{
++		.compatible = "brcm,bcm2712-aon-pinctrl",
++		.data = &bcm2712_c0_aon_plat_data,
++	},
++
++	{
++		.compatible = "brcm,bcm2712c0-pinctrl",
++		.data = &bcm2712_c0_plat_data,
++	},
++	{
++		.compatible = "brcm,bcm2712c0-aon-pinctrl",
++		.data = &bcm2712_c0_aon_plat_data,
++	},
++
++	{
++		.compatible = "brcm,bcm2712d0-pinctrl",
++		.data = &bcm2712_d0_plat_data,
++	},
++	{
++		.compatible = "brcm,bcm2712d0-aon-pinctrl",
++		.data = &bcm2712_d0_aon_plat_data,
++	},
++	{}
++};
++
++static int bcm2712_pinctrl_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++	const struct bcm_plat_data *pdata;
++	const struct of_device_id *match;
++	struct bcm2712_pinctrl *pc;
++	const char **names;
++	int num_pins, i;
++
++	match = of_match_node(bcm2712_pinctrl_match, np);
++	if (!match)
++		return -EINVAL;
++	pdata = match->data;
++
++	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
++	if (!pc)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, pc);
++	pc->dev = dev;
++	spin_lock_init(&pc->lock);
++
++	pc->base = devm_of_iomap(dev, np, 0, NULL);
++	if (IS_ERR(pc->base)) {
++		dev_err(dev, "could not get IO memory\n");
++		return PTR_ERR(pc->base);
++	}
++
++	pc->pctl_desc = *pdata->pctl_desc;
++	num_pins = pc->pctl_desc.npins;
++	names = devm_kmalloc_array(dev, num_pins, sizeof(const char *),
++				   GFP_KERNEL);
++	if (!names)
++		return -ENOMEM;
++	for (i = 0; i < num_pins; i++)
++		names[i] = pc->pctl_desc.pins[i].name;
++	pc->gpio_groups = names;
++	pc->pin_regs = pdata->pin_regs;
++	pc->pin_funcs = pdata->pin_funcs;
++	pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
++	if (IS_ERR(pc->pctl_dev))
++		return PTR_ERR(pc->pctl_dev);
++
++	pc->gpio_range = *pdata->gpio_range;
++	pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
++
++	return 0;
++}
++
++static struct platform_driver bcm2712_pinctrl_driver = {
++	.probe = bcm2712_pinctrl_probe,
++	.driver = {
++		.name = MODULE_NAME,
++		.of_match_table = bcm2712_pinctrl_match,
++		.suppress_bind_attrs = true,
++	},
++};
++builtin_platform_driver(bcm2712_pinctrl_driver);

+ 498 - 0
target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch

@@ -0,0 +1,498 @@
+From b627647c4500d39cb026924b608841fdf4d4d7e9 Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <[email protected]>
+Date: Thu, 29 Oct 2020 09:57:16 +0800
+Subject: [PATCH] mmc: brcmstb: add support for BCM2712
+
+BCM2712 has an SD Express capable SDHCI implementation and uses
+the SDIO CFG register block present on other STB chips.
+
+Add plumbing for SD Express handover and BCM2712-specific functions.
+
+Due to the common bus infrastructure between BCM2711 and BCM2712,
+the driver also needs to implement 32-bit IO accessors.
+
+mmc: brcmstb: override card presence if broken-cd is set
+
+Not just if the card is declared as nonremovable.
+
+sdhci: brcmstb: align SD express switchover with SD spec v8.00
+
+Part 1 of the Physical specification, figure 3-24, details the switch
+sequence for cards initially probed as SD. Add a missing check for DAT2
+level after switching VDD2 on.
+
+sdhci: brcmstb: clean up SD Express probe and error handling
+
+Refactor to avoid spurious error messages in dmesg if the requisite SD
+Express DT nodes aren't present.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+mmc: sdhci-brcmstb: only use the delay line PHY for tuneable speeds
+
+The MMC core has a 200MHz core clock which allows the use of DDR50 and
+below without incremental phase tuning. SDR50/SDR104 and the EMMC HS200
+speeds require tuning.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/mmc/host/Kconfig         |   2 +
+ drivers/mmc/host/sdhci-brcmstb.c | 356 +++++++++++++++++++++++++++++++
+ 2 files changed, 358 insertions(+)
+
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -1082,7 +1082,9 @@ config MMC_SDHCI_BRCMSTB
+ 	tristate "Broadcom SDIO/SD/MMC support"
+ 	depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ 	depends on MMC_SDHCI_PLTFM
++	select MMC_SDHCI_IO_ACCESSORS
+ 	select MMC_CQHCI
++	select OF_DYNAMIC
+ 	default y
+ 	help
+ 	  This selects support for the SDIO/SD/MMC Host Controller on
+--- a/drivers/mmc/host/sdhci-brcmstb.c
++++ b/drivers/mmc/host/sdhci-brcmstb.c
+@@ -11,6 +11,8 @@
+ #include <linux/of.h>
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/regulator/consumer.h>
+ 
+ #include "sdhci-cqhci.h"
+ #include "sdhci-pltfm.h"
+@@ -26,18 +28,43 @@
+ 
+ #define BRCMSTB_PRIV_FLAGS_HAS_CQE		BIT(0)
+ #define BRCMSTB_PRIV_FLAGS_GATE_CLOCK		BIT(1)
++#define BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS	BIT(2)
+ 
+ #define SDHCI_ARASAN_CQE_BASE_ADDR		0x200
+ 
++#define SDIO_CFG_CTRL				0x0
++#define  SDIO_CFG_CTRL_SDCD_N_TEST_EN		BIT(31)
++#define  SDIO_CFG_CTRL_SDCD_N_TEST_LEV		BIT(30)
++
++#define SDIO_CFG_SD_PIN_SEL			0x44
++#define  SDIO_CFG_SD_PIN_SEL_MASK		0x3
++#define  SDIO_CFG_SD_PIN_SEL_CARD		BIT(1)
++
++#define SDIO_CFG_MAX_50MHZ_MODE			0x1ac
++#define  SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE	BIT(31)
++#define  SDIO_CFG_MAX_50MHZ_MODE_ENABLE		BIT(0)
++
+ struct sdhci_brcmstb_priv {
+ 	void __iomem *cfg_regs;
+ 	unsigned int flags;
+ 	struct clk *base_clk;
+ 	u32 base_freq_hz;
++	u32 shadow_cmd;
++	u32 shadow_blk;
++	bool is_cmd_shadowed;
++	bool is_blk_shadowed;
++	struct regulator *sde_1v8;
++	struct device_node *sde_pcie;
++	void *__iomem sde_ioaddr;
++	void *__iomem sde_ioaddr2;
++	struct pinctrl *pinctrl;
++	struct pinctrl_state *pins_default;
++	struct pinctrl_state *pins_sdex;
+ };
+ 
+ struct brcmstb_match_priv {
+ 	void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
++	void (*cfginit)(struct sdhci_host *host);
+ 	struct sdhci_ops *ops;
+ 	const unsigned int flags;
+ };
+@@ -94,6 +121,124 @@ static void sdhci_brcmstb_set_clock(stru
+ 	sdhci_enable_clk(host, clk);
+ }
+ 
++#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
++
++static inline u32 sdhci_brcmstb_32only_readl(struct sdhci_host *host, int reg)
++{
++	u32 val = readl(host->ioaddr + reg);
++
++	pr_debug("%s: readl [0x%02x] 0x%08x\n",
++		 mmc_hostname(host->mmc), reg, val);
++	return val;
++}
++
++static u16 sdhci_brcmstb_32only_readw(struct sdhci_host *host, int reg)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++	struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++	u32 val;
++	u16 word;
++
++	if ((reg == SDHCI_TRANSFER_MODE) && brcmstb_priv->is_cmd_shadowed) {
++		/* Get the saved transfer mode */
++		val = brcmstb_priv->shadow_cmd;
++	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
++		   brcmstb_priv->is_blk_shadowed) {
++		/* Get the saved block info */
++		val = brcmstb_priv->shadow_blk;
++	} else {
++		val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++	}
++	word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
++	return word;
++}
++
++static u8 sdhci_brcmstb_32only_readb(struct sdhci_host *host, int reg)
++{
++	u32 val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++	u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
++	return byte;
++}
++
++static inline void sdhci_brcmstb_32only_writel(struct sdhci_host *host, u32 val, int reg)
++{
++	pr_debug("%s: writel [0x%02x] 0x%08x\n",
++		 mmc_hostname(host->mmc), reg, val);
++
++	writel(val, host->ioaddr + reg);
++}
++
++/*
++ * BCM2712 unfortunately carries with it a perennial bug with the SD controller
++ * register interface present on previous chips (2711/2709/2708). Accesses must
++ * be dword-sized and a read-modify-write cycle to the 32-bit registers
++ * containing the COMMAND, TRANSFER_MODE, BLOCK_SIZE and BLOCK_COUNT registers
++ * tramples the upper/lower 16 bits of data written. BCM2712 does not seem to
++ * need the extreme delay between each write as on previous chips, just the
++ * serialisation of writes to these registers in a single 32-bit operation.
++ */
++static void sdhci_brcmstb_32only_writew(struct sdhci_host *host, u16 val, int reg)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++	struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++	u32 word_shift = REG_OFFSET_IN_BITS(reg);
++	u32 mask = 0xffff << word_shift;
++	u32 oldval, newval;
++
++	if (reg == SDHCI_COMMAND) {
++		/* Write the block now as we are issuing a command */
++		if (brcmstb_priv->is_blk_shadowed) {
++			sdhci_brcmstb_32only_writel(host, brcmstb_priv->shadow_blk,
++				SDHCI_BLOCK_SIZE);
++			brcmstb_priv->is_blk_shadowed = false;
++		}
++		oldval = brcmstb_priv->shadow_cmd;
++		brcmstb_priv->is_cmd_shadowed = false;
++	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
++		   brcmstb_priv->is_blk_shadowed) {
++		/* Block size and count are stored in shadow reg */
++		oldval = brcmstb_priv->shadow_blk;
++	} else {
++		/* Read reg, all other registers are not shadowed */
++		oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++	}
++	newval = (oldval & ~mask) | (val << word_shift);
++
++	if (reg == SDHCI_TRANSFER_MODE) {
++		/* Save the transfer mode until the command is issued */
++		brcmstb_priv->shadow_cmd = newval;
++		brcmstb_priv->is_cmd_shadowed = true;
++	} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
++		/* Save the block info until the command is issued */
++		brcmstb_priv->shadow_blk = newval;
++		brcmstb_priv->is_blk_shadowed = true;
++	} else {
++		/* Command or other regular 32-bit write */
++		sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
++	}
++}
++
++static void sdhci_brcmstb_32only_writeb(struct sdhci_host *host, u8 val, int reg)
++{
++	u32 oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++	u32 byte_shift = REG_OFFSET_IN_BITS(reg);
++	u32 mask = 0xff << byte_shift;
++	u32 newval = (oldval & ~mask) | (val << byte_shift);
++
++	sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
++}
++
++static void sdhci_brcmstb_set_power(struct sdhci_host *host, unsigned char mode,
++				  unsigned short vdd)
++{
++	if (!IS_ERR(host->mmc->supply.vmmc)) {
++		struct mmc_host *mmc = host->mmc;
++
++		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
++	}
++	sdhci_set_power_noreg(host, mode, vdd);
++}
++
+ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
+ 					    unsigned int timing)
+ {
+@@ -123,6 +268,146 @@ static void sdhci_brcmstb_set_uhs_signal
+ 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ }
+ 
++static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++	struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++	bool want_dll = false;
++	u32 uhs_mask = (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104);
++	u32 hsemmc_mask = (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR |
++			   MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V);
++	u32 reg;
++
++	if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)) {
++	    if((host->mmc->caps & uhs_mask) || (host->mmc->caps2 & hsemmc_mask))
++		want_dll = true;
++	}
++
++	/*
++	 * If we want a speed that requires tuning,
++	 * then select the delay line PHY as the clock source.
++	 */
++	if (want_dll) {
++		reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
++		reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE;
++		reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE;
++		writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
++	}
++
++	if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
++	    (host->mmc->caps & MMC_CAP_NEEDS_POLL)) {
++		/* Force presence */
++		reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
++		reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
++		reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
++		writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
++	} else {
++		/* Enable card detection line */
++		reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
++		reg &= ~SDIO_CFG_SD_PIN_SEL_MASK;
++		reg |= SDIO_CFG_SD_PIN_SEL_CARD;
++		writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
++	}
++}
++
++static int bcm2712_init_sd_express(struct sdhci_host *host, struct mmc_ios *ios)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++	struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++	struct device *dev = host->mmc->parent;
++	u32 ctrl_val;
++	u32 present_state;
++	int ret;
++
++	if (!brcmstb_priv->sde_ioaddr || !brcmstb_priv->sde_ioaddr2)
++		return -EINVAL;
++
++	if (!brcmstb_priv->pinctrl)
++		return -EINVAL;
++
++	/* Turn off the SD clock first */
++	sdhci_set_clock(host, 0);
++
++	/* Disable SD DAT0-3 pulls */
++	pinctrl_select_state(brcmstb_priv->pinctrl, brcmstb_priv->pins_sdex);
++
++	ctrl_val = readl(brcmstb_priv->sde_ioaddr);
++	dev_dbg(dev, "ctrl_val 1 %08x\n", ctrl_val);
++
++	/* Tri-state the SD pins */
++	ctrl_val |= 0x1ff8;
++	writel(ctrl_val, brcmstb_priv->sde_ioaddr);
++	dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
++	/* Let voltages settle */
++	udelay(100);
++
++	/* Enable the PCIe sideband pins */
++	ctrl_val &= ~0x6000;
++	writel(ctrl_val, brcmstb_priv->sde_ioaddr);
++	dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
++	/* Let voltages settle */
++	udelay(100);
++
++	/* Turn on the 1v8 VDD2 regulator */
++	ret = regulator_enable(brcmstb_priv->sde_1v8);
++	if (ret)
++		return ret;
++
++	/* Wait for Tpvcrl */
++	msleep(1);
++
++	/* Sample DAT2 (CLKREQ#) - if low, card is in PCIe mode */
++	present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
++	present_state = (present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT;
++	dev_dbg(dev, "state = 0x%08x\n", present_state);
++
++	if (present_state & BIT(2)) {
++		dev_err(dev, "DAT2 still high, abandoning SDex switch\n");
++		return -ENODEV;
++	}
++
++	/* Turn on the LCPLL PTEST mux */
++	ctrl_val = readl(brcmstb_priv->sde_ioaddr2 + 20); // misc5
++	ctrl_val &= ~(0x7 << 7);
++	ctrl_val |= 3 << 7;
++	writel(ctrl_val, brcmstb_priv->sde_ioaddr2 + 20);
++	dev_dbg(dev, "misc 5->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2 + 20));
++
++	/* PTEST diff driver enable */
++	ctrl_val = readl(brcmstb_priv->sde_ioaddr2);
++	ctrl_val |= BIT(21);
++	writel(ctrl_val, brcmstb_priv->sde_ioaddr2);
++
++	dev_dbg(dev, "misc 0->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2));
++
++	/* Wait for more than the minimum Tpvpgl time */
++	msleep(100);
++
++	if (brcmstb_priv->sde_pcie) {
++		struct of_changeset changeset;
++		static struct property okay_property = {
++			.name = "status",
++			.value = "okay",
++			.length = 5,
++		};
++
++		/* Enable the pcie controller */
++		of_changeset_init(&changeset);
++		ret = of_changeset_update_property(&changeset,
++						   brcmstb_priv->sde_pcie,
++						   &okay_property);
++		if (ret) {
++			dev_err(dev, "%s: failed to update property - %d\n", __func__,
++			       ret);
++			return -ENODEV;
++		}
++		ret = of_changeset_apply(&changeset);
++	}
++
++	dev_dbg(dev, "%s -> %d\n", __func__, ret);
++	return ret;
++}
++
+ static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
+ {
+ 	sdhci_dumpregs(mmc_priv(mmc));
+@@ -155,6 +440,21 @@ static struct sdhci_ops sdhci_brcmstb_op
+ 	.set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+ 
++static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
++	.read_l = sdhci_brcmstb_32only_readl,
++	.read_w = sdhci_brcmstb_32only_readw,
++	.read_b = sdhci_brcmstb_32only_readb,
++	.write_l = sdhci_brcmstb_32only_writel,
++	.write_w = sdhci_brcmstb_32only_writew,
++	.write_b = sdhci_brcmstb_32only_writeb,
++	.set_clock = sdhci_set_clock,
++	.set_power = sdhci_brcmstb_set_power,
++	.set_bus_width = sdhci_set_bus_width,
++	.reset = sdhci_reset,
++	.set_uhs_signaling = sdhci_set_uhs_signaling,
++	.init_sd_express = bcm2712_init_sd_express,
++};
++
+ static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
+ 	.set_clock = sdhci_brcmstb_set_clock,
+ 	.set_bus_width = sdhci_set_bus_width,
+@@ -179,10 +479,16 @@ static const struct brcmstb_match_priv m
+ 	.ops = &sdhci_brcmstb_ops_7216,
+ };
+ 
++static const struct brcmstb_match_priv match_priv_2712 = {
++	.cfginit = sdhci_brcmstb_cfginit_2712,
++	.ops = &sdhci_brcmstb_ops_2712,
++};
++
+ static const struct of_device_id sdhci_brcm_of_match[] = {
+ 	{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
+ 	{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
+ 	{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
++	{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
+ 	{},
+ };
+ 
+@@ -256,6 +562,7 @@ static int sdhci_brcmstb_probe(struct pl
+ 	u32 actual_clock_mhz;
+ 	struct sdhci_host *host;
+ 	struct resource *iomem;
++	bool no_pinctrl = false;
+ 	struct clk *clk;
+ 	struct clk *base_clk = NULL;
+ 	int res;
+@@ -290,6 +597,11 @@ static int sdhci_brcmstb_probe(struct pl
+ 		match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
+ 	}
+ 
++	priv->sde_pcie = of_parse_phandle(pdev->dev.of_node,
++					  "sde-pcie", 0);
++	if (priv->sde_pcie)
++		priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++
+ 	/* Map in the non-standard CFG registers */
+ 	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ 	priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
+@@ -303,6 +615,43 @@ static int sdhci_brcmstb_probe(struct pl
+ 	if (res)
+ 		goto err;
+ 
++	priv->sde_1v8 = devm_regulator_get_optional(&pdev->dev, "sde-1v8");
++	if (IS_ERR(priv->sde_1v8))
++		priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++
++	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
++	if (iomem) {
++		priv->sde_ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
++		if (IS_ERR(priv->sde_ioaddr))
++			priv->sde_ioaddr = NULL;
++	}
++
++	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 3);
++	if (iomem) {
++		priv->sde_ioaddr2 = devm_ioremap_resource(&pdev->dev, iomem);
++		if (IS_ERR(priv->sde_ioaddr2))
++			priv->sde_ioaddr = NULL;
++	}
++
++	priv->pinctrl = devm_pinctrl_get(&pdev->dev);
++	if (IS_ERR(priv->pinctrl)) {
++			no_pinctrl = true;
++	}
++	priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default");
++	if (IS_ERR(priv->pins_default)) {
++			dev_dbg(&pdev->dev, "No pinctrl default state\n");
++			no_pinctrl = true;
++	}
++	priv->pins_sdex = pinctrl_lookup_state(priv->pinctrl, "sd-express");
++	if (IS_ERR(priv->pins_sdex)) {
++			dev_dbg(&pdev->dev, "No pinctrl sd-express state\n");
++			no_pinctrl = true;
++	}
++	if (no_pinctrl || !priv->sde_ioaddr || !priv->sde_ioaddr2) {
++		priv->pinctrl = NULL;
++		priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++	}
++
+ 	/*
+ 	 * Automatic clock gating does not work for SD cards that may
+ 	 * voltage switch so only enable it for non-removable devices.
+@@ -319,6 +668,13 @@ static int sdhci_brcmstb_probe(struct pl
+ 	    (host->mmc->caps2 & MMC_CAP2_HS400_ES))
+ 		host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
+ 
++	if (host->ops->init_sd_express &&
++	    (priv->flags & BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS))
++		host->mmc->caps2 |= MMC_CAP2_SD_EXP;
++
++	if(match_priv->cfginit)
++		match_priv->cfginit(host);
++
+ 	/*
+ 	 * Supply the existing CAPS, but clear the UHS modes. This
+ 	 * will allow these modes to be specified by device tree

+ 90 - 0
target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch

@@ -0,0 +1,90 @@
+From 9564939f1a92e5f9807461539de28c50e5bee440 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 6 Jul 2021 09:45:36 +0100
+Subject: [PATCH] sdhci: Add SD Express hook
+
+sdhci: remove PYA0_INTR_BUG quirk. Add quirks to disable some of the higher SDR speeds at 1.8v.
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c |  5 ++++-
+ drivers/mmc/host/sdhci.c            | 19 +++++++++++++++++++
+ drivers/mmc/host/sdhci.h            |  6 ++++++
+ 3 files changed, 29 insertions(+), 1 deletion(-)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -363,7 +363,10 @@ static const struct sdhci_pltfm_data sdh
+ 	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ 		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+-		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
++		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
++	            SDHCI_QUIRK2_NO_SDR50 |
++	            SDHCI_QUIRK2_NO_SDR104 |
++	            SDHCI_QUIRK2_NO_SDR25,
+ };
+ 
+ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -3071,6 +3071,15 @@ static void sdhci_card_event(struct mmc_
+ 	spin_unlock_irqrestore(&host->lock, flags);
+ }
+ 
++static int sdhci_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++	struct sdhci_host *host = mmc_priv(mmc);
++
++	if (!host->ops->init_sd_express)
++		return -EOPNOTSUPP;
++	return host->ops->init_sd_express(host, ios);
++}
++
+ static const struct mmc_host_ops sdhci_ops = {
+ 	.request	= sdhci_request,
+ 	.post_req	= sdhci_post_req,
+@@ -3086,6 +3095,7 @@ static const struct mmc_host_ops sdhci_o
+ 	.execute_tuning			= sdhci_execute_tuning,
+ 	.card_event			= sdhci_card_event,
+ 	.card_busy	= sdhci_card_busy,
++	.init_sd_express = sdhci_init_sd_express,
+ };
+ 
+ /*****************************************************************************\
+@@ -4605,6 +4615,15 @@ int sdhci_setup_host(struct sdhci_host *
+ 	    !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
+ 		mmc->caps |= MMC_CAP_UHS_DDR50;
+ 
++	if (host->quirks2 & SDHCI_QUIRK2_NO_SDR25)
++		mmc->caps &= ~MMC_CAP_UHS_SDR25;
++
++	if (host->quirks2 & SDHCI_QUIRK2_NO_SDR50)
++		mmc->caps &= ~MMC_CAP_UHS_SDR50;
++
++	if (host->quirks2 & SDHCI_QUIRK2_NO_SDR104)
++		mmc->caps &= ~MMC_CAP_UHS_SDR104;
++
+ 	/* Does the host need tuning for SDR50? */
+ 	if (host->caps1 & SDHCI_USE_SDR50_TUNING)
+ 		host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+--- a/drivers/mmc/host/sdhci.h
++++ b/drivers/mmc/host/sdhci.h
+@@ -481,6 +481,11 @@ struct sdhci_host {
+ /* Issue CMD and DATA reset together */
+ #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER	(1<<19)
+ 
++/* Quirks to ignore a speed if a that speed is unreliable */
++#define SDHCI_QUIRK2_NO_SDR25	(1<<19)
++#define SDHCI_QUIRK2_NO_SDR50  (1<<20)
++#define SDHCI_QUIRK2_NO_SDR104	(1<<21)
++
+ 	int irq;		/* Device IRQ */
+ 	void __iomem *ioaddr;	/* Mapped address */
+ 	phys_addr_t mapbase;	/* physical address base */
+@@ -663,6 +668,7 @@ struct sdhci_ops {
+ 	void	(*request_done)(struct sdhci_host *host,
+ 				struct mmc_request *mrq);
+ 	void    (*dump_vendor_regs)(struct sdhci_host *host);
++	int	(*init_sd_express)(struct sdhci_host *host, struct mmc_ios *ios);
+ };
+ 
+ #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS

+ 3788 - 0
target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch

@@ -0,0 +1,3788 @@
+From ce14be51d71bf39893786d380cbb82e81d2a10d5 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Wed, 14 Jul 2021 09:32:49 +0100
+Subject: [PATCH] Add new "pispbe" driver (though not yet the Makesfiles or DT
+ required to use it)
+
+media: bcm2712: Initial commit of the PiSP BE driver
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: bcm2712_pisp_be: PiSP driver updates.
+
+- Start registering video nodes from /dev/video20
+- Formatting fixes
+- Define MODULE_DEVICE_TABLE() to probe correctly
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: pisp_be: Improve image format support
+
+Add a new format table that lists the V4L2 format enums and their properties.
+Keep the exising 'RPBP' format to support the userland verification tools.
+This format requires userland to fill all plane properties. Standard V4L2
+formats will derive these properties from the format table.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: pisp_be: Advertise the meta output format explictily.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: pisp_be: Various updates and cleanups
+
+- Switch to a single node group for now.
+- Add a node description table to simplify node handling.
+- Switch HoG output to V4L2_CAP_META_CAPTURE type.
+- Use string descriptions for node names in logging messages.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+pisp_be: Updates for libcamera usage:
+
+- Remove indexes from device entity names
+- Add enumframesize and enumfmts ioctls
+- Add default format to all nodes.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+v4l2: pisp_be: Move format definitions into v4l2 core
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: raspberrypi: Move PiSP common headers to a single location
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: raspberrypi: Remove old pispbe driver.
+
+This is now supersede by the driver in drivers/media/platform/raspberrypi/
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+PISP-BE Driver: Automate buffer-cycling for TDN and Stitch state.
+Remove "tdn-input" and "stitch-input" nodes altogether (the output
+nodes must still be opened and REQBUFS called with 1 or 2 buffers).
+Also, a bit of tidying of buffer address handling and locking.
+
+PISP-BE driver: Turn debug level right down to reduce overly-chatty messages
+
+media: bcm2712: Depend on CONFIG_PM
+
+Depend on CONFIG_PM as the driver uses the runtime_pm infrastructure.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+drivers: media: pisp_be: Move BE driver to a raspberrypi directory
+
+Move the pisp_be driver from drivers/media/platform/raspberrypi/ to
+drivers/media/platform/raspberrypi/pisp_be/. This seems the accepted
+convention in the drivers/media/platform/ directory structure.
+
+Also rename the driver module from bcm2712_pisp_be to pisp_be.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+pisp_be: Updates for libcamera streaming:
+
+- Add some required v4l2 formats
+- Add buf_prepare ioctl
+- Set plane offsets correctly before reprogramming
+
+pisp_be: Reduce logging verbosity
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+pisp_be: Add buffer timestamps
+
+While at it, remove duplicate code when checking if the HW has completed
+multiple jobs.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+pisp_be: Remove queue size allocation constraint
+
+PISP-BE driver: Fix ISR to handle multiple done/start events.
+
+PISP-BE: Fix variable-name shadowing bugette
+
+PISP-BE: Support for two node groups. Reorganize the driver.
+
+To support 2 concurrent libcamera applications, we need 2 node groups,
+need to allow multiple opens of each node (because libcamera does this)
+and create a separate media device per group (to support file-locking).
+
+This triggered significant rearrangement of the driver. Some calls
+that we formerly intercepted have been delegated back to v4l2/vb2.
+Logging changes arising from multiple v4l2_dev. Refactored probe()
+and initialization. Avoid dynamically-allocated entity name strings.
+
+drivers: media: pisp_be: Add vidioc_enum_fmt_meta_out
+
+This was missing in the struct v4l2_ioctl_ops definition.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: media: pispe_be: Add Bayer compressed formats
+
+Add PiSP Bayer compressed formats to the list of supported pixel formats
+for the PiSP backend driver.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: meida: pisp_be: Fix overflow in plane size calculations
+
+The calculations for buffer plane sizes can overflow because of the
+plane factor shift. Fix this by using u64 integers for the calculations.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: media: pisp_be: Use 0P3 for plane factors
+
+Use less precision for the plane factors to avoid any nasty overflows.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: pisp: Checkpatch and coding style fixups
+
+Signed-off-by: Dave Stevenson <[email protected]>
+
+media: pisp_be: More coding style fixups
+
+media: platform: bcm2712: pisp_be: Fix crash when buffer format not set
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+
+media: platform: bcm2712: pisp_be: Allow non-SRGB colour spaces on RGB outputs
+
+Allow colour spaces other than SRGB when the output format in question
+is an RGB output. This commit merely ports over existing changes from
+the vc4 ISP driver.
+
+Signed-off-by: David Plowman <[email protected]>
+
+media: platform: bcm2712: Tweak list of BE supported image formats
+
+Remove RGB565 and 10- and 12-bit packed raw formats, which ISP-BE
+can't support for input or output. Add NV12M and NV21M which it can.
+(I didn't bother adding YUV422P, which apparently is not widely used.)
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+
+pisp_be: Fill the hardware revision in the media entity struct
+
+This can be used by userland to determine the hardware capabilities.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+bcm2712: Use BIT() macro
+
+Use the BIT() macro instead of plain bit shifting.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+bcm2712: Invert condition in pispbe_schedule_internal()
+
+Return earlier and save one indentation level
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+bcm2712: Invert condition in for loop
+
+Save one indentation level by continuing if the node is not streaming.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+bcm2712: Do not declare a local variable
+
+There already is a truct pispbe_node *node in the function scope.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+bcm21712: Siplify pispbe_schedule_one()
+
+A little more verbose but easier to follow ?
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+bcm2712: Rename pispbe_schedule_all() to pispbe_schedule_any()
+
+The pispbe_schedule_all() function name is misleading, as the function
+schedule a single job from any of the node groups. Rename it.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: platform: bcm2712: Remove buffer auto-cycling from ISP-BE
+
+Previously, the ISP-BE driver tried to automate "ping pong" buffers
+for TDN and HDR state, but did not fully conceal them from users.
+
+The automation has been removed: there are now separate output and
+capture queues for each of TDN and Stitch, which must be managed by
+user code (DMABUFs may be used to circulate buffers between queues).
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+
+drivers: media: pisp_be: Cache BE config buffer vaddr
+
+When programming a new job, we access at the config buffer, possibly
+from ISR context. So fetch and the virtual address when queuing the
+buffer.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: media: pisp_be: Remove all traces of ctrls and request API
+
+These APIs are not (and will not) be used by the driver, so remove them.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: bcm2712: Replace v4l2_dbg with dev_dbg
+
+Replace the v4l2 debug helpers with the device debug once, which are
+preferred.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Remove of_match_ptr()
+
+The of_match_ptr() usage could cause a compiler warning if
+CONFIG_OF is not enabled, as the pispbe_of_match variable would
+result unused.
+
+As the of_match_table field of struct platform_driver exists
+unconditionally, drop of_match_ptr() to avoid a warning.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+drivers: media: pispbe: Add local config buffer DMA allocation
+
+When initialiasing the driver, allocate a number of tiles + config
+structures used for storing hardware config internally in the driver.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: media: pispbe: Use local config buffers
+
+Store a copy of the config + tiles buffer locally when the buffer gets
+queued. This resolves the security issue where a userland process may
+modify the config buffer after it has been queued.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+drivers: media: pispbe: Validate config buffers
+
+Perform a basic config validation on the device output nodes to ensure
+the buffer size and stride values do not result in a buffer overrun.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: bcm2712: Rework probe sequence order
+
+Rework the probe sequence to:
+1) Use dev_err_probe() when failing to get clocks
+2) Disable clock on error path
+3) Disable the node groups if they have been enabled and
+   propagate the error up
+
+Also disable clocks in the remove() function.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Use pm_runtime_ops
+
+Introduce usage of runtime resume and suspend operations.
+
+The diver only uses a single clock source which is enable/disabled
+at resume and suspend time.
+
+Implement file open and release operations to control enablement of
+the clock provider.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Demote info message
+
+Demote info message about clock enablement to dev_dbg()
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Move pm_runtime calls to streamon/streamoff
+
+Move the calls to pm_runtime_resume_and_get() and pm_runtime_put()
+to the streamon and streamoff ioctl handlers.
+
+Remove custom handlers for the open and close file operations and use
+the framework provided helpers.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Use pm_runtime_autosuspend()
+
+Use the _autosuspend() version of runtime_pm_put() in order to avoid
+resuming and suspending the peripheral in between streaming sessions
+closely apart one from the other.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+drivers: media: pisp_be: Conditionally check buffers when preparing jobs
+
+When preparing a job, check the global enables in the config structure
+to see if the Output0/1, Tdn and Stitch blocks are enabled, and only
+test for a buffer queued if they are.
+
+This will allow userland to control the outputs selectively without
+disabling/re-enabling the respective device nodes.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: bcm2712: Rework media controller registration
+
+The current implementation register the v4l2_device and the video
+devices first, then creates the media controller and manually registers
+entities there.
+
+Rework the registration procedure to first create the v4l2_device and
+register the media_device with it. Then create the video nodes which
+gets automatically registered in the media graph by the core.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Create v4l2_subdev for ISP entity
+
+Create a v4l2 subdevice to represent the PISPBE ISP entity.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Fix v4l2-compliance warn on QUERYCAP
+
+Fix:
+
+warn: v4l2-compliance.cpp(669): media bus_info
+'platform:1000880000.pisp_be' differs from V4L2 bus_info
+'platform:pispbe'
+
+by populating the driver caps bus_info by using dev_name().
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Fix v4l2-compliance warn on invalid pixfmt
+
+The V4L2 API for the TRY_FMT/S_FMT ioctl allows the ioctl handler to
+return an error code only in specific conditions. If an invalid pixel
+format is supplied it should be adjusted instead of an error being
+returned.
+
+Albeit, v4l2-compliance treats this situation as a warning and not as
+an error because the behaviour has been discussed in length in the past.
+
+warn: v4l2-test-formats.cpp(794): TRY_FMT cannot handle an invalid pixelformat.
+warn: v4l2-test-formats.cpp(795): This may or may not be a problem. For more information see:
+warn: v4l2-test-formats.cpp(796): http://www.mail-archive.com/[email protected]/msg56550.html
+VIDIOC_TRY_FMT returned -1 (Invalid argument)
+
+Regardless of the warning vs failure decision, adjust the try_format()
+function implementation to use V4L2_PIX_FMT_YUV420M as default pixel
+format if the supplied one is invalid.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Fix v4l2-compliance warn on HOG pix format
+
+The try_format() implementation for the HOG video device node returns
+an error if the supplied pixel format is not correct.
+
+As per the video device output and capture video nodes, this contradicts
+the V4L2 specification even if it is treated as a warning by
+v4l2-compliance.
+
+Fix this by forcing the buffer pixel format and size to the default
+supported one. While at here, use the BIT() macro in the format
+initialization function.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Fix formats enumeration
+
+Right now a single implementation of enum_fmt() is used for all nodes
+in a group. This means that all the BE supported formats are listed for
+all the nodes. This is incorrect as the meta capture and output node
+formats should be restricted, and the meta formats should not be
+enumerated for video output and capture devices.
+
+Fix this by restricting the enumeration of META formats to the config
+and hog nodes. Split out from the list of supported_formats the
+V4L2_META_FMT_RPI_BE_CFG which is only used for the meta_out node, while
+V4L2_PIX_FMT_RPI_BE is kept in the list of supported_formats as it can
+be used as an opaque format for both meta_cap, video_cap and video_out
+nodes.
+
+Signed-off-by: Jacopo Mondi <[email protected]>
+
+media: bcm2712: Minor fixes to support PiSP regression tests
+
+Allow RGB input, not just Bayer (but only of those at once);
+Allow Wallpaper image formats. XXX They are not yet size-checked;
+Set "chicken bits" to test BURST_TRIM and AXI AWID/BID variation.
+Convert some v4l2_err() to dev_err()
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+
+drivers: media: pisp_be: Use the maximum number of config buffers
+
+Set PISP_BE_NUM_CONFIG_BUFFERS the the maximum number of possible
+buffers.  In the worst case, this overallocates config buffers, but
+given their size, it's not too much of a problem.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+
+media: pisp_be: Fix extra PM runtime put
+
+vidioc_streamoff callback can be called even if vidioc_streamon was
+never called. The driver currently does PM runtime get/put in these
+callbacks, which may lead to a put without a matching get.
+
+Fix this by moving the PM runtime get/put to vb2_ops's start_streaming &
+stop_streaming, which the framework makes sure won't get extra calls.
+
+Signed-off-by: Tomi Valkeinen <[email protected]>
+
+drivers: media: pisp_be: Don't report V4L2_PIX_FMT_RPI_BE format
+
+This is an internal opaque format, not to be reported in enum_fmt.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/platform/Kconfig                |    1 +
+ drivers/media/platform/Makefile               |    1 +
+ drivers/media/platform/raspberrypi/Kconfig    |    5 +
+ drivers/media/platform/raspberrypi/Makefile   |    3 +
+ .../platform/raspberrypi/pisp_be/Kconfig      |   12 +
+ .../platform/raspberrypi/pisp_be/Makefile     |    6 +
+ .../platform/raspberrypi/pisp_be/pisp_be.c    | 1985 +++++++++++++++++
+ .../raspberrypi/pisp_be/pisp_be_config.h      |  533 +++++
+ .../raspberrypi/pisp_be/pisp_be_formats.h     |  469 ++++
+ drivers/media/v4l2-core/v4l2-ioctl.c          |    2 +
+ include/media/raspberrypi/pisp_common.h       |   65 +
+ include/media/raspberrypi/pisp_types.h        |  144 ++
+ include/uapi/linux/videodev2.h                |    6 +
+ 13 files changed, 3232 insertions(+)
+ create mode 100644 drivers/media/platform/raspberrypi/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+ create mode 100644 include/media/raspberrypi/pisp_common.h
+ create mode 100644 include/media/raspberrypi/pisp_types.h
+
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -76,6 +76,7 @@ source "drivers/media/platform/mediatek/
+ source "drivers/media/platform/nvidia/Kconfig"
+ source "drivers/media/platform/nxp/Kconfig"
+ source "drivers/media/platform/qcom/Kconfig"
++source "drivers/media/platform/raspberrypi/Kconfig"
+ source "drivers/media/platform/renesas/Kconfig"
+ source "drivers/media/platform/rockchip/Kconfig"
+ source "drivers/media/platform/samsung/Kconfig"
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -19,6 +19,7 @@ obj-y += mediatek/
+ obj-y += nvidia/
+ obj-y += nxp/
+ obj-y += qcom/
++obj-y += raspberrypi/
+ obj-y += renesas/
+ obj-y += rockchip/
+ obj-y += samsung/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/Kconfig
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++comment "Raspberry Pi media platform drivers"
++
++source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0
++
++obj-y += pisp_be/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
+@@ -0,0 +1,12 @@
++config VIDEO_RASPBERRYPI_PISP_BE
++	tristate "Raspberry Pi PiSP Backend (BE) ISP driver"
++	depends on VIDEO_DEV && PM
++	select VIDEO_V4L2_SUBDEV_API
++	select MEDIA_CONTROLLER
++	select VIDEOBUF2_DMA_CONTIG
++	select V4L2_FWNODE
++	help
++	  Say Y here to enable support for the PiSP Backend (BE) ISP driver.
++
++	  To compile this driver as a module, choose M here. The module will be
++	  called pisp-be.
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for Raspberry Pi PiSP Backend driver
++#
++pisp-be-objs := pisp_be.o
++obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+@@ -0,0 +1,1985 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PiSP Back End driver.
++ * Copyright (c) 2021-2022 Raspberry Pi Limited.
++ *
++ */
++#include <linux/clk.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/videobuf2-dma-contig.h>
++#include <media/videobuf2-vmalloc.h>
++
++#include "pisp_be_config.h"
++#include "pisp_be_formats.h"
++
++MODULE_DESCRIPTION("PiSP Back End driver");
++MODULE_AUTHOR("David Plowman <[email protected]>");
++MODULE_AUTHOR("Nick Hollinghurst <[email protected]>");
++MODULE_LICENSE("GPL v2");
++
++/* Offset to use when registering the /dev/videoX node */
++#define PISPBE_VIDEO_NODE_OFFSET 20
++
++/* Maximum number of config buffers possible */
++#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME
++
++/*
++ * We want to support 2 independent instances allowing 2 simultaneous users
++ * of the ISP-BE (of course they share hardware, platform resources and mutex).
++ * Each such instance comprises a group of device nodes representing input
++ * and output queues, and a media controller device node to describe them.
++ */
++#define PISPBE_NUM_NODE_GROUPS 2
++
++#define PISPBE_NAME "pispbe"
++
++/* Some ISP-BE registers */
++#define PISP_BE_VERSION_OFFSET (0x0)
++#define PISP_BE_CONTROL_OFFSET (0x4)
++#define PISP_BE_TILE_ADDR_LO_OFFSET (0x8)
++#define PISP_BE_TILE_ADDR_HI_OFFSET (0xc)
++#define PISP_BE_STATUS_OFFSET (0x10)
++#define PISP_BE_BATCH_STATUS_OFFSET (0x14)
++#define PISP_BE_INTERRUPT_EN_OFFSET (0x18)
++#define PISP_BE_INTERRUPT_STATUS_OFFSET (0x1c)
++#define PISP_BE_AXI_OFFSET (0x20)
++#define PISP_BE_CONFIG_BASE_OFFSET (0x40)
++#define PISP_BE_IO_INPUT_ADDR0_LO_OFFSET (PISP_BE_CONFIG_BASE_OFFSET)
++#define PISP_BE_GLOBAL_BAYER_ENABLE_OFFSET (PISP_BE_CONFIG_BASE_OFFSET + 0x70)
++#define PISP_BE_GLOBAL_RGB_ENABLE_OFFSET (PISP_BE_CONFIG_BASE_OFFSET + 0x74)
++#define N_HW_ADDRESSES 14
++#define N_HW_ENABLES 2
++
++#define PISP_BE_VERSION_2712C1 0x02252700
++#define PISP_BE_VERSION_MINOR_BITS 0xF
++
++/*
++ * This maps our nodes onto the inputs/outputs of the actual PiSP Back End.
++ * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2
++ * context it means an input to the hardware (source image or metadata).
++ * Elsewhere it means an output from the hardware.
++ */
++enum node_ids {
++	MAIN_INPUT_NODE,
++	TDN_INPUT_NODE,
++	STITCH_INPUT_NODE,
++	HOG_OUTPUT_NODE,
++	OUTPUT0_NODE,
++	OUTPUT1_NODE,
++	TDN_OUTPUT_NODE,
++	STITCH_OUTPUT_NODE,
++	CONFIG_NODE,
++	PISPBE_NUM_NODES
++};
++
++struct node_description {
++	const char *ent_name;
++	enum v4l2_buf_type buf_type;
++	unsigned int caps;
++};
++
++static const struct node_description node_desc[PISPBE_NUM_NODES] = {
++	/* MAIN_INPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-input",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++		.caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++	},
++	/* TDN_INPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-tdn_input",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++		.caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++	},
++	/* STITCH_INPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-stitch_input",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++		.caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++	},
++	/* HOG_OUTPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-hog_output",
++		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++		.caps = V4L2_CAP_META_CAPTURE,
++	},
++	/* OUTPUT0_NODE */
++	{
++		.ent_name = PISPBE_NAME "-output0",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++	},
++	/* OUTPUT1_NODE */
++	{
++		.ent_name = PISPBE_NAME "-output1",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++	},
++	/* TDN_OUTPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-tdn_output",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++	},
++	/* STITCH_OUTPUT_NODE */
++	{
++		.ent_name = PISPBE_NAME "-stitch_output",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++	},
++	/* CONFIG_NODE */
++	{
++		.ent_name = PISPBE_NAME "-config",
++		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
++		.caps = V4L2_CAP_META_OUTPUT,
++	}
++};
++
++#define NODE_DESC_IS_OUTPUT(desc) ( \
++	((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++	((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
++	((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
++
++#define NODE_IS_META(node) ( \
++	((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++	((node)->buf_type == V4L2_BUF_TYPE_META_CAPTURE))
++#define NODE_IS_OUTPUT(node) ( \
++	((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
++#define NODE_IS_CAPTURE(node) ( \
++	((node)->buf_type == V4L2_BUF_TYPE_META_CAPTURE) || \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
++#define NODE_IS_MPLANE(node) ( \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \
++	((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
++
++/*
++ * Structure to describe a single node /dev/video<N> which represents a single
++ * input or output queue to the PiSP Back End device.
++ */
++struct pispbe_node {
++	unsigned int id;
++	int vfl_dir;
++	enum v4l2_buf_type buf_type;
++	struct video_device vfd;
++	struct media_pad pad;
++	struct media_intf_devnode *intf_devnode;
++	struct media_link *intf_link;
++	struct pispbe_node_group *node_group;
++	struct mutex node_lock;
++	struct mutex queue_lock;
++	spinlock_t ready_lock;
++	struct list_head ready_queue;
++	struct vb2_queue queue;
++	struct v4l2_format format;
++	const struct pisp_be_format *pisp_format;
++};
++
++/* For logging only, use the entity name with "pispbe" and separator removed */
++#define NODE_NAME(node) \
++		(node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME))
++#define NODE_GET_V4L2(node) ((node)->node_group->v4l2_dev)
++
++/*
++ * Node group structure, which comprises all the input and output nodes that a
++ * single PiSP client will need, along with its own v4l2 and media devices.
++ */
++struct pispbe_node_group {
++	unsigned int id;
++	struct v4l2_device v4l2_dev;
++	struct v4l2_subdev sd;
++	struct pispbe_dev *pispbe;
++	struct media_device mdev;
++	struct pispbe_node node[PISPBE_NUM_NODES];
++	u32 streaming_map; /* bitmap of which nodes are streaming */
++	struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */
++	struct pisp_be_tiles_config *config;
++	dma_addr_t config_dma_addr;
++};
++
++/* Records details of the jobs currently running or queued on the h/w. */
++struct pispbe_job {
++	struct pispbe_node_group *node_group;
++	/*
++	 * An array of buffer pointers - remember it's source buffers first,
++	 * then captures, then metadata last.
++	 */
++	struct pispbe_buffer *buf[PISPBE_NUM_NODES];
++};
++
++/*
++ * Structure representing the entire PiSP Back End device, comprising several
++ * node groups which share platform resources and a mutex for the actual HW.
++ */
++struct pispbe_dev {
++	struct device *dev;
++	struct pispbe_node_group node_group[PISPBE_NUM_NODE_GROUPS];
++	int hw_busy; /* non-zero if a job is queued or is being started */
++	struct pispbe_job queued_job, running_job;
++	void __iomem *be_reg_base;
++	struct clk *clk;
++	int irq;
++	u32 hw_version;
++	u8 done, started;
++	spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
++};
++
++static inline u32 read_reg(struct pispbe_dev *pispbe, unsigned int offset)
++{
++	return readl(pispbe->be_reg_base + offset);
++}
++
++static inline void write_reg(struct pispbe_dev *pispbe, unsigned int offset,
++			     u32 val)
++{
++	writel(val, pispbe->be_reg_base + offset);
++}
++
++/* Check and initialize hardware. */
++static int hw_init(struct pispbe_dev *pispbe)
++{
++	u32 u;
++
++	/* Check the HW is present and has a known version */
++	u = read_reg(pispbe, PISP_BE_VERSION_OFFSET);
++	dev_info(pispbe->dev, "pispbe_probe: HW version:  0x%08x", u);
++	pispbe->hw_version = u;
++	if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712C1)
++		return -ENODEV;
++
++	/* Clear leftover interrupts */
++	write_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET, 0xFFFFFFFFu);
++	u = read_reg(pispbe, PISP_BE_BATCH_STATUS_OFFSET);
++	dev_info(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u);
++	pispbe->done = (uint8_t)u;
++	pispbe->started = (uint8_t)(u >> 8);
++	u = read_reg(pispbe, PISP_BE_STATUS_OFFSET);
++	dev_info(pispbe->dev, "pispbe_probe: Status:      0x%08x", u);
++	if (u != 0 || pispbe->done != pispbe->started) {
++		dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n");
++		return -EBUSY;
++	}
++	/*
++	 * AXI QOS=0, CACHE=4'b0010, PROT=3'b011
++	 * Also set "chicken bits" 22:20 which enable sub-64-byte bursts
++	 * and AXI AWID/BID variability (on versions which support this).
++	 */
++	write_reg(pispbe, PISP_BE_AXI_OFFSET, 0x32703200u);
++
++	/* Enable both interrupt flags */
++	write_reg(pispbe, PISP_BE_INTERRUPT_EN_OFFSET, 0x00000003u);
++	return 0;
++}
++
++/*
++ * Queue a job to the h/w. If the h/w is idle it will begin immediately.
++ * Caller must ensure it is "safe to queue", i.e. we don't already have a
++ * queued, unstarted job.
++ */
++static void hw_queue_job(struct pispbe_dev *pispbe,
++			 dma_addr_t hw_dma_addrs[N_HW_ADDRESSES],
++			 u32 hw_enables[N_HW_ENABLES],
++			 struct pisp_be_config *config, dma_addr_t tiles,
++			 unsigned int num_tiles)
++{
++	unsigned int begin, end;
++	unsigned int u;
++
++	if (read_reg(pispbe, PISP_BE_STATUS_OFFSET) & 1)
++		dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n");
++
++	/*
++	 * Write configuration to hardware. DMA addresses and enable flags
++	 * are passed separately, because the driver needs to sanitize them,
++	 * and we don't want to modify (or be vulnerable to modifications of)
++	 * the mmap'd buffer.
++	 */
++	for (u = 0; u < N_HW_ADDRESSES; ++u) {
++		write_reg(pispbe, PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u,
++			  (u32)(hw_dma_addrs[u]));
++		write_reg(pispbe, PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u + 4,
++			  (u32)(hw_dma_addrs[u] >> 32));
++	}
++	write_reg(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE_OFFSET, hw_enables[0]);
++	write_reg(pispbe, PISP_BE_GLOBAL_RGB_ENABLE_OFFSET, hw_enables[1]);
++
++	/*
++	 * Everything else is as supplied by the user. XXX Buffer sizes not
++	 * checked!
++	 */
++	begin =	offsetof(struct pisp_be_config, global.bayer_order) /
++								sizeof(u32);
++	end = offsetof(struct pisp_be_config, axi) / sizeof(u32);
++	for (u = begin; u < end; u++) {
++		unsigned int val = ((u32 *)config)[u];
++
++		write_reg(pispbe, PISP_BE_CONFIG_BASE_OFFSET + 4 * u, val);
++	}
++
++	/* Read back the addresses -- an error here could be fatal */
++	for (u = 0; u < N_HW_ADDRESSES; ++u) {
++		unsigned int offset = PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u;
++		u64 along = read_reg(pispbe, offset);
++
++		along += ((u64)read_reg(pispbe, offset + 4)) << 32;
++		if (along != (u64)(hw_dma_addrs[u])) {
++			dev_err(pispbe->dev,
++				"ISP BE config error: check if ISP RAMs enabled?\n");
++			return;
++		}
++	}
++
++	/*
++	 * Write tile pointer to hardware. XXX Tile offsets and sizes not
++	 * checked (and even if checked, the user could subsequently modify
++	 * them)!
++	 */
++	write_reg(pispbe, PISP_BE_TILE_ADDR_LO_OFFSET, (u32)tiles);
++	write_reg(pispbe, PISP_BE_TILE_ADDR_HI_OFFSET, (u32)(tiles >> 32));
++
++	/* Enqueue the job */
++	write_reg(pispbe, PISP_BE_CONTROL_OFFSET, 3 + 65536 * num_tiles);
++}
++
++struct pispbe_buffer {
++	struct vb2_v4l2_buffer vb;
++	struct list_head ready_list;
++	unsigned int config_index;
++};
++
++static int get_addr_3(dma_addr_t addr[3], struct pispbe_buffer *buf,
++		      struct pispbe_node *node)
++{
++	unsigned int num_planes = node->format.fmt.pix_mp.num_planes;
++	unsigned int plane_factor = 0;
++	unsigned int size;
++	unsigned int p;
++
++	if (!buf || !node->pisp_format)
++		return 0;
++
++	WARN_ON(!NODE_IS_MPLANE(node));
++
++	/*
++	 * Determine the base plane size. This will not be the same
++	 * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single
++	 * plane buffer in an mplane format.
++	 */
++	size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline *
++					node->format.fmt.pix_mp.height;
++
++	for (p = 0; p < num_planes && p < 3; p++) {
++		addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p);
++		plane_factor += node->pisp_format->plane_factor[p];
++	}
++
++	for (; p < MAX_PLANES && node->pisp_format->plane_factor[p]; p++) {
++		/*
++		 * Calculate the address offset of this plane as needed
++		 * by the hardware. This is specifically for non-mplane
++		 * buffer formats, where there are 3 image planes, e.g.
++		 * for the V4L2_PIX_FMT_YUV420 format.
++		 */
++		addr[p] = addr[0] + ((size * plane_factor) >> 3);
++		plane_factor += node->pisp_format->plane_factor[p];
++	}
++
++	return num_planes;
++}
++
++static dma_addr_t get_addr(struct pispbe_buffer *buf)
++{
++	if (buf)
++		return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++	return 0;
++}
++
++static void
++fixup_addrs_enables(dma_addr_t addrs[N_HW_ADDRESSES],
++		    u32 hw_enables[N_HW_ENABLES],
++		    struct pisp_be_tiles_config *config,
++		    struct pispbe_buffer *buf[PISPBE_NUM_NODES],
++		    struct pispbe_node_group *node_group)
++{
++	int ret, i;
++
++	/* Take a copy of the "enable" bitmaps so we can modify them. */
++	hw_enables[0] = config->config.global.bayer_enables;
++	hw_enables[1] = config->config.global.rgb_enables;
++
++	/*
++	 * Main input first. There are 3 address pointers, corresponding to up
++	 * to 3 planes.
++	 */
++	ret = get_addr_3(addrs, buf[MAIN_INPUT_NODE],
++			 &node_group->node[MAIN_INPUT_NODE]);
++	if (ret <= 0) {
++		/*
++		 * This shouldn't happen; pispbe_schedule_internal should insist
++		 * on an input.
++		 */
++		dev_warn(node_group->pispbe->dev,
++			"ISP-BE missing input\n");
++		hw_enables[0] = 0;
++		hw_enables[1] = 0;
++		return;
++	}
++
++	/*
++	 * Now TDN/Stitch inputs and outputs. These are single-plane and only
++	 * used with Bayer input. Input enables must match the requirements
++	 * of the processing stages, otherwise the hardware can lock up!
++	 */
++	if (hw_enables[0] & PISP_BE_BAYER_ENABLE_INPUT) {
++		addrs[3] = get_addr(buf[TDN_INPUT_NODE]);
++		if (addrs[3] == 0 ||
++		    !(hw_enables[0] & PISP_BE_BAYER_ENABLE_TDN_INPUT) ||
++		    !(hw_enables[0] & PISP_BE_BAYER_ENABLE_TDN) ||
++		    (config->config.tdn.reset & 1)) {
++			hw_enables[0] &= ~(PISP_BE_BAYER_ENABLE_TDN_INPUT |
++					   PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS);
++			if (!(config->config.tdn.reset & 1))
++				hw_enables[0] &= ~PISP_BE_BAYER_ENABLE_TDN;
++		}
++
++		addrs[4] = get_addr(buf[STITCH_INPUT_NODE]);
++		if (addrs[4] == 0 ||
++		    !(hw_enables[0] & PISP_BE_BAYER_ENABLE_STITCH_INPUT) ||
++		    !(hw_enables[0] & PISP_BE_BAYER_ENABLE_STITCH)) {
++			hw_enables[0] &=
++				~(PISP_BE_BAYER_ENABLE_STITCH_INPUT |
++				  PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS |
++				  PISP_BE_BAYER_ENABLE_STITCH);
++		}
++
++		addrs[5] = get_addr(buf[TDN_OUTPUT_NODE]);
++		if (addrs[5] == 0)
++			hw_enables[0] &= ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS |
++					   PISP_BE_BAYER_ENABLE_TDN_OUTPUT);
++
++		addrs[6] = get_addr(buf[STITCH_OUTPUT_NODE]);
++		if (addrs[6] == 0)
++			hw_enables[0] &=
++				~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS |
++				  PISP_BE_BAYER_ENABLE_STITCH_OUTPUT);
++	} else {
++		/* No Bayer input? Disable entire Bayer pipe (else lockup) */
++		hw_enables[0] = 0;
++	}
++
++	/* Main image output channels. */
++	for (i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) {
++		ret = get_addr_3(addrs + 7 + 3 * i, buf[OUTPUT0_NODE + i],
++				 &node_group->node[OUTPUT0_NODE + i]);
++		if (ret <= 0)
++			hw_enables[1] &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i);
++	}
++
++	/* HoG output (always single plane). */
++	addrs[13] = get_addr(buf[HOG_OUTPUT_NODE]);
++	if (addrs[13] == 0)
++		hw_enables[1] &= ~PISP_BE_RGB_ENABLE_HOG;
++}
++
++/*
++ * Internal function. Called from pispbe_schedule_one/all. Returns non-zero if
++ * we started a job.
++ *
++ * Warning: needs to be called with hw_lock taken, and releases it if it
++ * schedules a job.
++ */
++static int pispbe_schedule_internal(struct pispbe_node_group *node_group,
++				    unsigned long flags)
++{
++	struct pisp_be_tiles_config *config_tiles_buffer;
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	struct pispbe_buffer *buf[PISPBE_NUM_NODES];
++	dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
++	dma_addr_t tiles;
++	u32 hw_enables[N_HW_ENABLES];
++	struct pispbe_node *node;
++	unsigned long flags1;
++	unsigned int config_index;
++	int i;
++
++	/*
++	 * To schedule a job, we need all streaming nodes (apart from Output0,
++	 * Output1, Tdn and Stitch) to have a buffer ready, which must
++	 * include at least a config buffer and a main input image.
++	 *
++	 * For Output0, Output1, Tdn and Stitch, a buffer only needs to be
++	 * available if the blocks are enabled in the config.
++	 *
++	 * (Note that streaming_map is protected by hw_lock, which is held.)
++	 */
++	if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
++		node_group->streaming_map) !=
++			(BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) {
++		dev_dbg(pispbe->dev, "Nothing to do\n");
++		return 0;
++	}
++
++	node = &node_group->node[CONFIG_NODE];
++	spin_lock_irqsave(&node->ready_lock, flags1);
++	buf[CONFIG_NODE] =
++	   list_first_entry_or_null(&node->ready_queue, struct pispbe_buffer,
++				    ready_list);
++	spin_unlock_irqrestore(&node->ready_lock, flags1);
++
++	/* Exit early if no config buffer has been queued. */
++	if (!buf[CONFIG_NODE])
++		return 0;
++
++	config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
++	config_tiles_buffer = &node_group->config[config_index];
++	tiles = (dma_addr_t)node_group->config_dma_addr +
++			config_index * sizeof(struct pisp_be_tiles_config) +
++			offsetof(struct pisp_be_tiles_config, tiles);
++
++	/* remember: srcimages, captures then metadata */
++	for (i = 0; i < PISPBE_NUM_NODES; i++) {
++		unsigned int bayer_en =
++			config_tiles_buffer->config.global.bayer_enables;
++		unsigned int rgb_en =
++			config_tiles_buffer->config.global.rgb_enables;
++		bool ignore_buffers = false;
++
++		/* Config node is handled outside the loop above. */
++		if (i == CONFIG_NODE)
++			continue;
++
++		buf[i] = NULL;
++		if (!(node_group->streaming_map & BIT(i)))
++			continue;
++
++		if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
++		     i == OUTPUT0_NODE) ||
++		    (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) &&
++		     i == OUTPUT1_NODE) ||
++		    (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) &&
++		     i == TDN_INPUT_NODE) ||
++		    (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) &&
++		     i == TDN_OUTPUT_NODE) ||
++		    (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) &&
++		     i == STITCH_INPUT_NODE) ||
++		    (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) &&
++		     i == STITCH_OUTPUT_NODE)) {
++			/*
++			 * Ignore Output0/Output1/Tdn/Stitch buffer check if the
++			 * global enables aren't set for these blocks. If a
++			 * buffer has been provided, we dequeue it back to the
++			 * user with the other in-use buffers.
++			 *
++			 */
++			ignore_buffers = true;
++		}
++
++		node = &node_group->node[i];
++
++		spin_lock_irqsave(&node->ready_lock, flags1);
++		buf[i] = list_first_entry_or_null(&node->ready_queue,
++						  struct pispbe_buffer,
++						  ready_list);
++		spin_unlock_irqrestore(&node->ready_lock, flags1);
++		if (!buf[i] && !ignore_buffers) {
++			dev_dbg(pispbe->dev, "Nothing to do\n");
++			return 0;
++		}
++	}
++
++	/* Pull a buffer from each V4L2 queue to form the queued job */
++	for (i = 0; i < PISPBE_NUM_NODES; i++) {
++		if (buf[i]) {
++			node = &node_group->node[i];
++
++			spin_lock_irqsave(&node->ready_lock, flags1);
++			list_del(&buf[i]->ready_list);
++			spin_unlock_irqrestore(&node->ready_lock,
++					       flags1);
++		}
++		pispbe->queued_job.buf[i] = buf[i];
++	}
++
++	pispbe->queued_job.node_group = node_group;
++	pispbe->hw_busy = 1;
++	spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++	/*
++	 * We can kick the job off without the hw_lock, as this can
++	 * never run again until hw_busy is cleared, which will happen
++	 * only when the following job has been queued.
++	 */
++	dev_dbg(pispbe->dev, "Have buffers - starting hardware\n");
++
++	/* Convert buffers to DMA addresses for the hardware */
++	fixup_addrs_enables(hw_dma_addrs, hw_enables,
++			    config_tiles_buffer, buf, node_group);
++	/*
++	 * This could be a spot to fill in the
++	 * buf[i]->vb.vb2_buf.planes[j].bytesused fields?
++	 */
++	i = config_tiles_buffer->num_tiles;
++	if (i <= 0 || i > PISP_BACK_END_NUM_TILES ||
++	    !((hw_enables[0] | hw_enables[1]) &
++	      PISP_BE_BAYER_ENABLE_INPUT)) {
++		/*
++		 * Bad job. We can't let it proceed as it could lock up
++		 * the hardware, or worse!
++		 *
++		 * XXX How to deal with this most cleanly? For now, just
++		 * force num_tiles to 0, which causes the H/W to do
++		 * something bizarre but survivable. It increments
++		 * (started,done) counters by more than 1, but we seem
++		 * to survive...
++		 */
++		dev_err(pispbe->dev, "PROBLEM: Bad job");
++		i = 0;
++	}
++	hw_queue_job(pispbe, hw_dma_addrs, hw_enables,
++		     &config_tiles_buffer->config, tiles, i);
++
++	return 1;
++}
++
++/* Try and schedule a job for just a single node group. */
++static void pispbe_schedule_one(struct pispbe_node_group *node_group)
++{
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	unsigned long flags;
++	int ret;
++
++	spin_lock_irqsave(&pispbe->hw_lock, flags);
++	if (pispbe->hw_busy) {
++		spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++		return;
++	}
++
++	/* A non-zero return means the lock was released. */
++	ret = pispbe_schedule_internal(node_group, flags);
++	if (!ret)
++		spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++}
++
++/* Try and schedule a job for any of the node groups. */
++static void pispbe_schedule_any(struct pispbe_dev *pispbe, int clear_hw_busy)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&pispbe->hw_lock, flags);
++
++	if (clear_hw_busy)
++		pispbe->hw_busy = 0;
++	if (pispbe->hw_busy == 0) {
++		unsigned int i;
++
++		for (i = 0; i < PISPBE_NUM_NODE_GROUPS; i++) {
++			/*
++			 * A non-zero return from pispbe_schedule_internal means
++			 * the lock was released.
++			 */
++			if (pispbe_schedule_internal(&pispbe->node_group[i],
++						     flags))
++				return;
++		}
++	}
++	spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++}
++
++static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
++			       struct pispbe_job *job)
++{
++	struct pispbe_buffer **buf = job->buf;
++	u64 ts = ktime_get_ns();
++	int i;
++
++	for (i = 0; i < PISPBE_NUM_NODES; i++) {
++		if (buf[i]) {
++			buf[i]->vb.vb2_buf.timestamp = ts;
++			vb2_buffer_done(&buf[i]->vb.vb2_buf,
++					VB2_BUF_STATE_DONE);
++		}
++	}
++}
++
++static irqreturn_t pispbe_isr(int irq, void *dev)
++{
++	struct pispbe_dev *pispbe = (struct pispbe_dev *)dev;
++	u8 started, done;
++	int can_queue_another = 0;
++	u32 u;
++
++	u = read_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET);
++	if (u == 0)
++		return IRQ_NONE;
++
++	write_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET, u);
++	dev_dbg(pispbe->dev, "Hardware interrupt\n");
++	u = read_reg(pispbe, PISP_BE_BATCH_STATUS_OFFSET);
++	done = (uint8_t)u;
++	started = (uint8_t)(u >> 8);
++	dev_dbg(pispbe->dev,
++		"H/W started %d done %d, previously started %d done %d\n",
++		(int)started, (int)done, (int)pispbe->started,
++		(int)pispbe->done);
++
++	/*
++	 * Be aware that done can go up by 2 and started by 1 when: a job that
++	 * we previously saw "start" now finishes, and we then queued a new job
++	 * which we see both start and finish "simultaneously".
++	 */
++	if (pispbe->running_job.node_group && pispbe->done != done) {
++		pispbe_isr_jobdone(pispbe, &pispbe->running_job);
++		memset(&pispbe->running_job, 0, sizeof(pispbe->running_job));
++		pispbe->done++;
++		dev_dbg(pispbe->dev, "Job done (1)\n");
++	}
++
++	if (pispbe->started != started) {
++		pispbe->started++;
++		can_queue_another = 1;
++		dev_dbg(pispbe->dev, "Job started\n");
++
++		if (pispbe->done != done && pispbe->queued_job.node_group) {
++			pispbe_isr_jobdone(pispbe, &pispbe->queued_job);
++			pispbe->done++;
++			dev_dbg(pispbe->dev, "Job done (2)\n");
++		} else {
++			pispbe->running_job = pispbe->queued_job;
++		}
++
++		memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
++	}
++
++	if (pispbe->done != done || pispbe->started != started) {
++		dev_err(pispbe->dev, "PROBLEM: counters not matching!\n");
++		pispbe->started = started;
++		pispbe->done = done;
++	}
++
++	/* check if there's more to do before going to sleep */
++	pispbe_schedule_any(pispbe, can_queue_another);
++
++	return IRQ_HANDLED;
++}
++
++static int pisp_be_validate_config(struct pispbe_node_group *node_group,
++				   struct pisp_be_tiles_config *config)
++{
++	u32 bayer_enables = config->config.global.bayer_enables;
++	u32 rgb_enables = config->config.global.rgb_enables;
++	struct device *dev = node_group->pispbe->dev;
++	struct v4l2_format *fmt;
++	unsigned int bpl, size, i, j;
++
++	if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) ==
++	    !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) {
++		dev_err(dev, "%s: Not one input enabled\n", __func__);
++		return -EIO;
++	}
++
++	/* Ensure output config strides and buffer sizes match the V4L2 formats. */
++	fmt = &node_group->node[TDN_OUTPUT_NODE].format;
++	if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) {
++		bpl = config->config.tdn_output_format.stride;
++		size = bpl * config->config.tdn_output_format.height;
++		if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
++			dev_err(dev, "%s: bpl mismatch on tdn_output\n",
++				__func__);
++			return -EINVAL;
++		}
++		if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
++			dev_err(dev, "%s: size mismatch on tdn_output\n",
++				__func__);
++			return -EINVAL;
++		}
++	}
++
++	fmt = &node_group->node[STITCH_OUTPUT_NODE].format;
++	if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) {
++		bpl = config->config.stitch_output_format.stride;
++		size = bpl * config->config.stitch_output_format.height;
++		if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
++			dev_err(dev, "%s: bpl mismatch on stitch_output\n",
++				__func__);
++			return -EINVAL;
++		}
++		if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
++			dev_err(dev, "%s: size mismatch on stitch_output\n",
++				__func__);
++			return -EINVAL;
++		}
++	}
++
++	for (j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) {
++		if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j)))
++			continue;
++		if (config->config.output_format[j].image.format &
++		    PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++			continue; /* TODO: Size checks for wallpaper formats */
++
++		fmt = &node_group->node[OUTPUT0_NODE + j].format;
++		for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++) {
++			bpl = !i ? config->config.output_format[j].image.stride
++			    : config->config.output_format[j].image.stride2;
++			size = bpl * config->config.output_format[j].image.height;
++
++			if (config->config.output_format[j].image.format &
++						PISP_IMAGE_FORMAT_SAMPLING_420)
++				size >>= 1;
++			if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) {
++				dev_err(dev, "%s: bpl mismatch on output %d\n",
++					__func__, j);
++				return -EINVAL;
++			}
++			if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) {
++				dev_err(dev, "%s: size mismatch on output\n",
++					__func__);
++				return -EINVAL;
++			}
++		}
++	}
++
++	return 0;
++}
++
++static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
++				   unsigned int *nplanes, unsigned int sizes[],
++				   struct device *alloc_devs[])
++{
++	struct pispbe_node *node = vb2_get_drv_priv(q);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	*nplanes = 1;
++	if (NODE_IS_MPLANE(node)) {
++		unsigned int i;
++
++		*nplanes = node->format.fmt.pix_mp.num_planes;
++		for (i = 0; i < *nplanes; i++) {
++			unsigned int size =
++				node->format.fmt.pix_mp.plane_fmt[i].sizeimage;
++			if (sizes[i] && sizes[i] < size) {
++				dev_err(pispbe->dev, "%s: size %u < %u\n",
++					__func__, sizes[i], size);
++				return -EINVAL;
++			}
++			sizes[i] = size;
++		}
++	} else if (NODE_IS_META(node)) {
++		sizes[0] = node->format.fmt.meta.buffersize;
++		/*
++		 * Limit the config node buffer count to the number of internal
++		 * buffers allocated.
++		 */
++		if (node->id == CONFIG_NODE)
++			*nbuffers = min_t(unsigned int, *nbuffers,
++					  PISP_BE_NUM_CONFIG_BUFFERS);
++	}
++
++	dev_dbg(pispbe->dev,
++		"Image (or metadata) size %u, nbuffers %u for node %s\n",
++		sizes[0], *nbuffers, NODE_NAME(node));
++
++	return 0;
++}
++
++static int pispbe_node_buffer_prepare(struct vb2_buffer *vb)
++{
++	struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	unsigned long size = 0;
++	unsigned int num_planes = NODE_IS_MPLANE(node) ?
++					node->format.fmt.pix_mp.num_planes : 1;
++	unsigned int i;
++
++	for (i = 0; i < num_planes; i++) {
++		size = NODE_IS_MPLANE(node)
++			? node->format.fmt.pix_mp.plane_fmt[i].sizeimage
++			: node->format.fmt.meta.buffersize;
++
++		if (vb2_plane_size(vb, i) < size) {
++			dev_err(pispbe->dev,
++				"data will not fit into plane %d (%lu < %lu)\n",
++				i, vb2_plane_size(vb, i), size);
++			return -EINVAL;
++		}
++
++		vb2_set_plane_payload(vb, i, size);
++	}
++
++	if (node->id == CONFIG_NODE) {
++		void *dst = &node->node_group->config[vb->index];
++		void *src = vb2_plane_vaddr(vb, 0);
++
++		memcpy(dst, src, sizeof(struct pisp_be_tiles_config));
++		return pisp_be_validate_config(node->node_group, dst);
++	}
++
++	return 0;
++}
++
++static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
++{
++	struct vb2_v4l2_buffer *vbuf =
++		container_of(buf, struct vb2_v4l2_buffer, vb2_buf);
++	struct pispbe_buffer *buffer =
++		container_of(vbuf, struct pispbe_buffer, vb);
++	struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
++	struct pispbe_node_group *node_group = node->node_group;
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	unsigned long flags;
++
++	dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
++	spin_lock_irqsave(&node->ready_lock, flags);
++	list_add_tail(&buffer->ready_list, &node->ready_queue);
++	spin_unlock_irqrestore(&node->ready_lock, flags);
++
++	/*
++	 * Every time we add a buffer, check if there's now some work for the hw
++	 * to do, but only for this client.
++	 */
++	pispbe_schedule_one(node_group);
++}
++
++static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
++{
++	unsigned long flags;
++	struct pispbe_node *node = vb2_get_drv_priv(q);
++	struct pispbe_node_group *node_group = node->node_group;
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(pispbe->dev);
++	if (ret < 0)
++		return ret;
++
++	spin_lock_irqsave(&pispbe->hw_lock, flags);
++	node->node_group->streaming_map |=  BIT(node->id);
++	spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++	dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
++		__func__, NODE_NAME(node), count);
++	dev_dbg(pispbe->dev, "Nodes streaming for this group now 0x%x\n",
++		node->node_group->streaming_map);
++
++	/* Maybe we're ready to run. */
++	pispbe_schedule_one(node_group);
++
++	return 0;
++}
++
++static void pispbe_node_stop_streaming(struct vb2_queue *q)
++{
++	struct pispbe_node *node = vb2_get_drv_priv(q);
++	struct pispbe_node_group *node_group = node->node_group;
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	struct pispbe_buffer *buf;
++	unsigned long flags;
++
++	/*
++	 * Now this is a bit awkward. In a simple M2M device we could just wait
++	 * for all queued jobs to complete, but here there's a risk that a
++	 * partial set of buffers was queued and cannot be run. For now, just
++	 * cancel all buffers stuck in the "ready queue", then wait for any
++	 * running job.
++	 * XXX This may return buffers out of order.
++	 */
++	dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
++	spin_lock_irqsave(&pispbe->hw_lock, flags);
++	do {
++		unsigned long flags1;
++
++		spin_lock_irqsave(&node->ready_lock, flags1);
++		buf = list_first_entry_or_null(&node->ready_queue,
++					       struct pispbe_buffer,
++					       ready_list);
++		if (buf) {
++			list_del(&buf->ready_list);
++			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++		}
++		spin_unlock_irqrestore(&node->ready_lock, flags1);
++	} while (buf);
++	spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++	vb2_wait_for_all_buffers(&node->queue);
++
++	spin_lock_irqsave(&pispbe->hw_lock, flags);
++	node_group->streaming_map &= ~BIT(node->id);
++	spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++	pm_runtime_mark_last_busy(pispbe->dev);
++	pm_runtime_put_autosuspend(pispbe->dev);
++
++	dev_dbg(pispbe->dev, "Nodes streaming for this group now 0x%x\n",
++		node_group->streaming_map);
++}
++
++static const struct vb2_ops pispbe_node_queue_ops = {
++	.queue_setup = pispbe_node_queue_setup,
++	.buf_prepare = pispbe_node_buffer_prepare,
++	.buf_queue = pispbe_node_buffer_queue,
++	.start_streaming = pispbe_node_start_streaming,
++	.stop_streaming = pispbe_node_stop_streaming,
++};
++
++static const struct v4l2_file_operations pispbe_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 pispbe_node_querycap(struct file *file, void *priv,
++				struct v4l2_capability *cap)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver));
++	strscpy(cap->card, PISPBE_NAME, sizeof(cap->card));
++	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++		 dev_name(pispbe->dev));
++
++	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
++			    V4L2_CAP_VIDEO_OUTPUT_MPLANE |
++			    V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS |
++			    V4L2_CAP_META_OUTPUT | V4L2_CAP_META_CAPTURE;
++	cap->device_caps = node->vfd.device_caps;
++
++	dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n",
++		NODE_NAME(node), cap->capabilities, cap->device_caps,
++		node->vfd.device_caps);
++	return 0;
++}
++
++static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv,
++				     struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++		dev_err(pispbe->dev,
++			"Cannot get capture fmt for output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++	*f = node->format;
++	dev_dbg(pispbe->dev, "Get capture format for node %s\n",
++		NODE_NAME(node));
++	return 0;
++}
++
++static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv,
++				     struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++		dev_err(pispbe->dev,
++			"Cannot get capture fmt for output node %s\n",
++			 NODE_NAME(node));
++		return -EINVAL;
++	}
++	*f = node->format;
++	dev_dbg(pispbe->dev, "Get output format for node %s\n",
++		NODE_NAME(node));
++	return 0;
++}
++
++static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv,
++				      struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
++		dev_err(pispbe->dev,
++			"Cannot get capture fmt for meta output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++	*f = node->format;
++	dev_dbg(pispbe->dev, "Get output format for meta node %s\n",
++		NODE_NAME(node));
++	return 0;
++}
++
++static int pispbe_node_g_fmt_meta_cap(struct file *file, void *priv,
++				      struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (!NODE_IS_META(node) || NODE_IS_OUTPUT(node)) {
++		dev_err(pispbe->dev,
++			"Cannot get capture fmt for meta output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++	*f = node->format;
++	dev_dbg(pispbe->dev, "Get output format for meta node %s\n",
++		NODE_NAME(node));
++	return 0;
++}
++
++static int verify_be_pix_format(const struct v4l2_format *f,
++				struct pispbe_node *node)
++{
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	unsigned int nplanes = f->fmt.pix_mp.num_planes;
++	unsigned int i;
++
++	if (f->fmt.pix_mp.width == 0 || f->fmt.pix_mp.height == 0) {
++		dev_err(pispbe->dev, "Details incorrect for output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++
++	if (nplanes == 0 || nplanes > MAX_PLANES) {
++		dev_err(pispbe->dev,
++			"Bad number of planes for output node %s, req =%d\n",
++			NODE_NAME(node), nplanes);
++		return -EINVAL;
++	}
++
++	for (i = 0; i < nplanes; i++) {
++		const struct v4l2_plane_pix_format *p;
++
++		p = &f->fmt.pix_mp.plane_fmt[i];
++		if (p->bytesperline == 0 || p->sizeimage == 0) {
++			dev_err(pispbe->dev,
++				"Invalid plane %d for output node %s\n",
++				i, NODE_NAME(node));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static const struct pisp_be_format *find_format(unsigned int fourcc)
++{
++	const struct pisp_be_format *fmt;
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
++		fmt = &supported_formats[i];
++		if (fmt->fourcc == fourcc)
++			return fmt;
++	}
++
++	return NULL;
++}
++
++static void set_plane_params(struct v4l2_format *f,
++			     const struct pisp_be_format *fmt)
++{
++	unsigned int nplanes = f->fmt.pix_mp.num_planes;
++	unsigned int total_plane_factor = 0;
++	unsigned int i;
++
++	for (i = 0; i < MAX_PLANES; i++)
++		total_plane_factor += fmt->plane_factor[i];
++
++	for (i = 0; i < nplanes; i++) {
++		struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i];
++		unsigned int bpl, plane_size;
++
++		bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3;
++		bpl = ALIGN(max(p->bytesperline, bpl), fmt->align);
++
++		plane_size = bpl * f->fmt.pix_mp.height *
++		      (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor);
++		/*
++		 * The shift is to divide out the plane_factor fixed point
++		 * scaling of 8.
++		 */
++		plane_size = max(p->sizeimage, plane_size >> 3);
++
++		p->bytesperline = bpl;
++		p->sizeimage = plane_size;
++	}
++}
++
++static int try_format(struct v4l2_format *f, struct pispbe_node *node)
++{
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	const struct pisp_be_format *fmt;
++	unsigned int i;
++	bool is_rgb;
++	u32 pixfmt = f->fmt.pix_mp.pixelformat;
++
++	dev_dbg(pispbe->dev,
++		"%s: [%s] req %ux%u " V4L2_FOURCC_CONV ", planes %d\n",
++		__func__, NODE_NAME(node), f->fmt.pix_mp.width,
++		f->fmt.pix_mp.height, V4L2_FOURCC_CONV_ARGS(pixfmt),
++		f->fmt.pix_mp.num_planes);
++
++	if (pixfmt == V4L2_PIX_FMT_RPI_BE)
++		return verify_be_pix_format(f, node);
++
++	fmt = find_format(pixfmt);
++	if (!fmt)
++		fmt = find_format(V4L2_PIX_FMT_YUV420M);
++
++	f->fmt.pix_mp.pixelformat = fmt->fourcc;
++	f->fmt.pix_mp.num_planes = fmt->num_planes;
++	f->fmt.pix_mp.field = V4L2_FIELD_NONE;
++	f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u),
++				  PISP_BACK_END_MIN_TILE_WIDTH);
++	f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u),
++				   PISP_BACK_END_MIN_TILE_HEIGHT);
++
++	/*
++	 * Fill in the actual colour space when the requested one was
++	 * not supported. This also catches the case when the "default"
++	 * colour space was requested (as that's never in the mask).
++	 */
++	if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) & fmt->colorspace_mask))
++		f->fmt.pix_mp.colorspace = fmt->colorspace_default;
++
++	/* In all cases, we only support the defaults for these: */
++	f->fmt.pix_mp.ycbcr_enc =
++		V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace);
++	f->fmt.pix_mp.xfer_func =
++		V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace);
++
++	is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB;
++	f->fmt.pix_mp.quantization =
++		V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace,
++					      f->fmt.pix_mp.ycbcr_enc);
++
++	/* Set plane size and bytes/line for each plane. */
++	set_plane_params(f, fmt);
++
++	for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
++		dev_dbg(pispbe->dev,
++			"%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n",
++			__func__, NODE_NAME(node), i, f->fmt.pix_mp.width,
++			f->fmt.pix_mp.height, fmt->bit_depth,
++			f->fmt.pix_mp.plane_fmt[i].bytesperline,
++			f->fmt.pix_mp.plane_fmt[i].sizeimage);
++	}
++
++	return 0;
++}
++
++static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv,
++				       struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret;
++
++	if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++		dev_err(pispbe->dev,
++			"Cannot set capture fmt for output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++
++	ret = try_format(f, node);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv,
++				       struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret;
++
++	if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) {
++		dev_err(pispbe->dev,
++			"Cannot set capture fmt for output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++
++	ret = try_format(f, node);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv,
++					struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
++		dev_err(pispbe->dev,
++			"Cannot set capture fmt for meta output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++
++	f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
++	f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
++
++	return 0;
++}
++
++static int pispbe_node_try_fmt_meta_cap(struct file *file, void *priv,
++					struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (!NODE_IS_META(node) || NODE_IS_OUTPUT(node)) {
++		dev_err(pispbe->dev,
++			"Cannot set capture fmt for meta output node %s\n",
++			NODE_NAME(node));
++		return -EINVAL;
++	}
++
++	f->fmt.meta.dataformat = V4L2_PIX_FMT_RPI_BE;
++	if (!f->fmt.meta.buffersize)
++		f->fmt.meta.buffersize = BIT(20);
++
++	return 0;
++}
++
++static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv,
++				     struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret = pispbe_node_try_fmt_vid_cap(file, priv, f);
++
++	if (ret < 0)
++		return ret;
++
++	node->format = *f;
++	node->pisp_format = find_format(f->fmt.pix_mp.pixelformat);
++
++	dev_dbg(pispbe->dev,
++		"Set capture format for node %s to " V4L2_FOURCC_CONV "\n",
++		NODE_NAME(node),
++		V4L2_FOURCC_CONV_ARGS(f->fmt.pix_mp.pixelformat));
++	return 0;
++}
++
++static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv,
++				     struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret = pispbe_node_try_fmt_vid_out(file, priv, f);
++
++	if (ret < 0)
++		return ret;
++
++	node->format = *f;
++	node->pisp_format = find_format(f->fmt.pix_mp.pixelformat);
++
++	dev_dbg(pispbe->dev,
++		"Set output format for node %s to " V4L2_FOURCC_CONV "\n",
++		NODE_NAME(node),
++		V4L2_FOURCC_CONV_ARGS(f->fmt.pix_mp.pixelformat));
++	return 0;
++}
++
++static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv,
++				      struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret = pispbe_node_try_fmt_meta_out(file, priv, f);
++
++	if (ret < 0)
++		return ret;
++
++	node->format = *f;
++	node->pisp_format = &meta_out_supported_formats[0];
++
++	dev_dbg(pispbe->dev,
++		"Set output format for meta node %s to " V4L2_FOURCC_CONV "\n",
++		NODE_NAME(node),
++		V4L2_FOURCC_CONV_ARGS(f->fmt.meta.dataformat));
++	return 0;
++}
++
++static int pispbe_node_s_fmt_meta_cap(struct file *file, void *priv,
++				      struct v4l2_format *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++	int ret = pispbe_node_try_fmt_meta_cap(file, priv, f);
++
++	if (ret < 0)
++		return ret;
++
++	node->format = *f;
++	node->pisp_format = find_format(f->fmt.meta.dataformat);
++
++	dev_dbg(pispbe->dev,
++		"Set capture format for meta node %s to " V4L2_FOURCC_CONV "\n",
++		NODE_NAME(node),
++		V4L2_FOURCC_CONV_ARGS(f->fmt.meta.dataformat));
++	return 0;
++}
++
++static int pispbe_node_enum_fmt(struct file *file, void  *priv,
++				struct v4l2_fmtdesc *f)
++{
++	struct pispbe_node *node = video_drvdata(file);
++
++	if (f->type != node->queue.type)
++		return -EINVAL;
++
++	if (NODE_IS_META(node)) {
++		if (f->index)
++			return -EINVAL;
++
++		if (NODE_IS_OUTPUT(node))
++			f->pixelformat = V4L2_META_FMT_RPI_BE_CFG;
++		else
++			f->pixelformat = V4L2_PIX_FMT_RPI_BE;
++		f->flags = 0;
++		return 0;
++	}
++
++	if (f->index >= ARRAY_SIZE(supported_formats))
++		return -EINVAL;
++
++	f->pixelformat = supported_formats[f->index].fourcc;
++	f->flags = 0;
++
++	return 0;
++}
++
++static int pispbe_enum_framesizes(struct file *file, void *priv,
++				  struct v4l2_frmsizeenum *fsize)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	if (NODE_IS_META(node) || fsize->index)
++		return -EINVAL;
++
++	if (!find_format(fsize->pixel_format)) {
++		dev_err(pispbe->dev, "Invalid pixel code: %x\n",
++			fsize->pixel_format);
++		return -EINVAL;
++	}
++
++	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++	fsize->stepwise.min_width = 32;
++	fsize->stepwise.max_width = 65535;
++	fsize->stepwise.step_width = 2;
++
++	fsize->stepwise.min_height = 32;
++	fsize->stepwise.max_height = 65535;
++	fsize->stepwise.step_height = 2;
++
++	return 0;
++}
++
++static int pispbe_node_streamon(struct file *file, void *priv,
++				enum v4l2_buf_type type)
++{
++	struct pispbe_node *node = video_drvdata(file);
++	struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++	/* Do we need a node->stream_lock mutex? */
++
++	dev_dbg(pispbe->dev, "Stream on for node %s\n", NODE_NAME(node));
++
++	/* Do we care about the type? Each node has only one queue. */
++
++	INIT_LIST_HEAD(&node->ready_queue);
++
++	/* locking should be handled by the queue->lock? */
++	return vb2_streamon(&node->queue, type);
++}
++
++static int pispbe_node_streamoff(struct file *file, void *priv,
++				 enum v4l2_buf_type type)
++{
++	struct pispbe_node *node = video_drvdata(file);
++
++	return vb2_streamoff(&node->queue, type);
++}
++
++static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = {
++	.vidioc_querycap = pispbe_node_querycap,
++	.vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap,
++	.vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out,
++	.vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out,
++	.vidioc_g_fmt_meta_cap = pispbe_node_g_fmt_meta_cap,
++	.vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap,
++	.vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out,
++	.vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out,
++	.vidioc_try_fmt_meta_cap = pispbe_node_try_fmt_meta_cap,
++	.vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap,
++	.vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out,
++	.vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out,
++	.vidioc_s_fmt_meta_cap = pispbe_node_s_fmt_meta_cap,
++	.vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt,
++	.vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt,
++	.vidioc_enum_fmt_meta_cap = pispbe_node_enum_fmt,
++	.vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt,
++	.vidioc_enum_framesizes = pispbe_enum_framesizes,
++	.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_reqbufs = vb2_ioctl_reqbufs,
++	.vidioc_streamon = pispbe_node_streamon,
++	.vidioc_streamoff = pispbe_node_streamoff,
++};
++
++static const struct video_device pispbe_videodev = {
++	.name = PISPBE_NAME,
++	.vfl_dir = VFL_DIR_M2M, /* gets overwritten */
++	.fops = &pispbe_fops,
++	.ioctl_ops = &pispbe_node_ioctl_ops,
++	.minor = -1,
++	.release = video_device_release_empty,
++};
++
++static void node_set_default_format(struct pispbe_node *node)
++{
++	if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) {
++		/* Config node */
++		struct v4l2_format *f = &node->format;
++
++		f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
++		f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
++		f->type = node->buf_type;
++	} else if (NODE_IS_META(node) && NODE_IS_CAPTURE(node)) {
++		/* HOG output node */
++		struct v4l2_format *f = &node->format;
++
++		f->fmt.meta.dataformat = V4L2_PIX_FMT_RPI_BE;
++		f->fmt.meta.buffersize = BIT(20);
++		f->type = node->buf_type;
++	} else {
++		struct v4l2_format f = {0};
++
++		f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
++		f.fmt.pix_mp.width = 1920;
++		f.fmt.pix_mp.height = 1080;
++		f.type = node->buf_type;
++		try_format(&f, node);
++		node->format = f;
++	}
++
++	node->pisp_format = find_format(node->format.fmt.pix_mp.pixelformat);
++}
++
++/*
++ * Initialise a struct pispbe_node and register it as /dev/video<N>
++ * to represent one of the PiSP Back End's input or output streams.
++ */
++static int
++pispbe_init_node(struct pispbe_node_group *node_group, unsigned int id)
++{
++	bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]);
++	struct pispbe_node *node = &node_group->node[id];
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	struct media_entity *entity = &node->vfd.entity;
++	struct video_device *vdev = &node->vfd;
++	struct vb2_queue *q = &node->queue;
++	int ret;
++
++	node->id = id;
++	node->node_group = node_group;
++	node->buf_type = node_desc[id].buf_type;
++
++	mutex_init(&node->node_lock);
++	mutex_init(&node->queue_lock);
++	INIT_LIST_HEAD(&node->ready_queue);
++	spin_lock_init(&node->ready_lock);
++
++	node->format.type = node->buf_type;
++	node_set_default_format(node);
++
++	q->type = node->buf_type;
++	q->io_modes = VB2_MMAP | VB2_DMABUF;
++	q->mem_ops = &vb2_dma_contig_memops;
++	q->drv_priv = node;
++	q->ops = &pispbe_node_queue_ops;
++	q->buf_struct_size = sizeof(struct pispbe_buffer);
++	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++	q->dev = node->node_group->pispbe->dev;
++	/* get V4L2 to handle node->queue locking */
++	q->lock = &node->queue_lock;
++
++	ret = vb2_queue_init(q);
++	if (ret < 0) {
++		dev_err(pispbe->dev, "vb2_queue_init failed\n");
++		return ret;
++	}
++
++	*vdev = pispbe_videodev; /* default initialization */
++	strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name));
++	vdev->v4l2_dev = &node_group->v4l2_dev;
++	vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
++	/* get V4L2 to serialise our ioctls */
++	vdev->lock = &node->node_lock;
++	vdev->queue = &node->queue;
++	vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps;
++
++	node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
++	ret = media_entity_pads_init(entity, 1, &node->pad);
++	if (ret) {
++		dev_err(pispbe->dev,
++			"Failed to register media pads for %s device node\n",
++			NODE_NAME(node));
++		goto err_unregister_queue;
++	}
++
++	ret = video_register_device(vdev, VFL_TYPE_VIDEO,
++				    PISPBE_VIDEO_NODE_OFFSET);
++	if (ret) {
++		dev_err(pispbe->dev,
++			"Failed to register video %s device node\n",
++			NODE_NAME(node));
++		goto err_unregister_queue;
++	}
++	video_set_drvdata(vdev, node);
++
++	if (output)
++		ret = media_create_pad_link(entity, 0, &node_group->sd.entity,
++					    id, MEDIA_LNK_FL_IMMUTABLE |
++					    MEDIA_LNK_FL_ENABLED);
++	else
++		ret = media_create_pad_link(&node_group->sd.entity, id, entity,
++					    0, MEDIA_LNK_FL_IMMUTABLE |
++					    MEDIA_LNK_FL_ENABLED);
++	if (ret)
++		goto err_unregister_video_dev;
++
++	dev_info(pispbe->dev,
++		 "%s device node registered as /dev/video%d\n",
++		 NODE_NAME(node), node->vfd.num);
++	return 0;
++
++err_unregister_video_dev:
++	video_unregister_device(&node->vfd);
++err_unregister_queue:
++	vb2_queue_release(&node->queue);
++	return ret;
++}
++
++static const struct v4l2_subdev_pad_ops pispbe_pad_ops = {
++	.link_validate = v4l2_subdev_link_validate_default,
++};
++
++static const struct v4l2_subdev_ops pispbe_sd_ops = {
++	.pad = &pispbe_pad_ops,
++};
++
++static int pispbe_init_subdev(struct pispbe_node_group *node_group)
++{
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	struct v4l2_subdev *sd = &node_group->sd;
++	unsigned int i;
++	int ret;
++
++	v4l2_subdev_init(sd, &pispbe_sd_ops);
++	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++	sd->owner = THIS_MODULE;
++	sd->dev = pispbe->dev;
++	strscpy(sd->name, PISPBE_NAME, sizeof(sd->name));
++
++	for (i = 0; i < PISPBE_NUM_NODES; i++)
++		node_group->pad[i].flags =
++			NODE_DESC_IS_OUTPUT(&node_desc[i]) ?
++			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
++
++	ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES,
++				     node_group->pad);
++	if (ret)
++		goto error;
++
++	ret = v4l2_device_register_subdev(&node_group->v4l2_dev, sd);
++	if (ret)
++		goto error;
++
++	return 0;
++
++error:
++	media_entity_cleanup(&sd->entity);
++	return ret;
++}
++
++static int pispbe_init_group(struct pispbe_dev *pispbe, unsigned int id)
++{
++	struct pispbe_node_group *node_group = &pispbe->node_group[id];
++	struct v4l2_device *v4l2_dev;
++	struct media_device *mdev;
++	unsigned int num_registered = 0;
++	int ret;
++
++	node_group->id = id;
++	node_group->pispbe = pispbe;
++	node_group->streaming_map = 0;
++
++	dev_info(pispbe->dev, "Register nodes for group %u\n", id);
++
++	/* Register v4l2_device and media_device */
++	mdev = &node_group->mdev;
++	mdev->hw_revision = node_group->pispbe->hw_version;
++	mdev->dev = node_group->pispbe->dev;
++	strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model));
++	snprintf(mdev->bus_info, sizeof(mdev->bus_info),
++		 "platform:%s", dev_name(node_group->pispbe->dev));
++	media_device_init(mdev);
++
++	v4l2_dev = &node_group->v4l2_dev;
++	v4l2_dev->mdev = &node_group->mdev;
++	strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name));
++
++	ret = v4l2_device_register(pispbe->dev, &node_group->v4l2_dev);
++	if (ret)
++		goto err_media_dev_cleanup;
++
++	/* Register the PISPBE subdevice. */
++	ret = pispbe_init_subdev(node_group);
++	if (ret)
++		goto err_unregister_v4l2;
++
++	/* Create device video nodes */
++	for (; num_registered < PISPBE_NUM_NODES; num_registered++) {
++		ret = pispbe_init_node(node_group, num_registered);
++		if (ret)
++			goto err_unregister_nodes;
++	}
++
++	ret = media_device_register(mdev);
++	if (ret)
++		goto err_unregister_nodes;
++
++	node_group->config =
++		dma_alloc_coherent(pispbe->dev,
++				   sizeof(struct pisp_be_tiles_config) *
++					PISP_BE_NUM_CONFIG_BUFFERS,
++				   &node_group->config_dma_addr, GFP_KERNEL);
++	if (!node_group->config) {
++		dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n");
++		ret = -ENOMEM;
++		goto err_unregister_mdev;
++	}
++
++	return 0;
++
++err_unregister_mdev:
++	media_device_unregister(mdev);
++err_unregister_nodes:
++	while (num_registered-- > 0) {
++		video_unregister_device(&node_group->node[num_registered].vfd);
++		vb2_queue_release(&node_group->node[num_registered].queue);
++	}
++	v4l2_device_unregister_subdev(&node_group->sd);
++	media_entity_cleanup(&node_group->sd.entity);
++err_unregister_v4l2:
++	v4l2_device_unregister(v4l2_dev);
++err_media_dev_cleanup:
++	media_device_cleanup(mdev);
++	return ret;
++}
++
++static void pispbe_destroy_node_group(struct pispbe_node_group *node_group)
++{
++	struct pispbe_dev *pispbe = node_group->pispbe;
++	int i;
++
++	if (node_group->config) {
++		dma_free_coherent(node_group->pispbe->dev,
++				  sizeof(struct pisp_be_tiles_config) *
++					PISP_BE_NUM_CONFIG_BUFFERS,
++				  node_group->config,
++				  node_group->config_dma_addr);
++	}
++
++	dev_info(pispbe->dev, "Unregister from media controller\n");
++
++	v4l2_device_unregister_subdev(&node_group->sd);
++	media_entity_cleanup(&node_group->sd.entity);
++	media_device_unregister(&node_group->mdev);
++
++	for (i = PISPBE_NUM_NODES - 1; i >= 0; i--) {
++		video_unregister_device(&node_group->node[i].vfd);
++		vb2_queue_release(&node_group->node[i].queue);
++	}
++
++	media_device_cleanup(&node_group->mdev);
++	v4l2_device_unregister(&node_group->v4l2_dev);
++}
++
++static int pispbe_runtime_suspend(struct device *dev)
++{
++	struct pispbe_dev *pispbe = dev_get_drvdata(dev);
++
++	clk_disable_unprepare(pispbe->clk);
++
++	return 0;
++}
++
++static int pispbe_runtime_resume(struct device *dev)
++{
++	struct pispbe_dev *pispbe = dev_get_drvdata(dev);
++	int ret;
++
++	ret = clk_prepare_enable(pispbe->clk);
++	if (ret) {
++		dev_err(dev, "Unable to enable clock\n");
++		return ret;
++	}
++
++	dev_dbg(dev, "%s: Enabled clock, rate=%lu\n",
++		__func__, clk_get_rate(pispbe->clk));
++
++	return 0;
++}
++
++/*
++ * Probe the ISP-BE hardware block, as a single platform device.
++ * This will instantiate multiple "node groups" each with many device nodes.
++ */
++static int pispbe_probe(struct platform_device *pdev)
++{
++	unsigned int num_groups = 0;
++	struct pispbe_dev *pispbe;
++	int ret;
++
++	pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL);
++	if (!pispbe)
++		return -ENOMEM;
++
++	dev_set_drvdata(&pdev->dev, pispbe);
++	pispbe->dev = &pdev->dev;
++	platform_set_drvdata(pdev, pispbe);
++
++	pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(pispbe->be_reg_base)) {
++		dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n");
++		return PTR_ERR(pispbe->be_reg_base);
++	}
++
++	pispbe->irq = platform_get_irq(pdev, 0);
++	if (pispbe->irq <= 0) {
++		dev_err(&pdev->dev, "No IRQ resource\n");
++		return -EINVAL;
++	}
++
++	ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0,
++			       PISPBE_NAME, pispbe);
++	if (ret) {
++		dev_err(&pdev->dev, "Unable to request interrupt\n");
++		return ret;
++	}
++
++	ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36));
++	if (ret)
++		return ret;
++
++	pispbe->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(pispbe->clk))
++		return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk),
++				     "Failed to get clock");
++
++	/* Hardware initialisation */
++	pm_runtime_set_autosuspend_delay(pispbe->dev, 200);
++	pm_runtime_use_autosuspend(pispbe->dev);
++	pm_runtime_enable(pispbe->dev);
++
++	ret = pm_runtime_resume_and_get(pispbe->dev);
++	if (ret)
++		goto pm_runtime_disable_err;
++
++	pispbe->hw_busy = 0;
++	spin_lock_init(&pispbe->hw_lock);
++	ret = hw_init(pispbe);
++	if (ret)
++		goto pm_runtime_put_err;
++
++	/*
++	 * Initialise and register devices for each node_group, including media
++	 * device
++	 */
++	for (num_groups = 0;
++	     num_groups < PISPBE_NUM_NODE_GROUPS;
++	     num_groups++) {
++		ret = pispbe_init_group(pispbe, num_groups);
++		if (ret)
++			goto disable_nodes_err;
++	}
++
++	pm_runtime_mark_last_busy(pispbe->dev);
++	pm_runtime_put_autosuspend(pispbe->dev);
++
++	return 0;
++
++disable_nodes_err:
++	while (num_groups-- > 0)
++		pispbe_destroy_node_group(&pispbe->node_group[num_groups]);
++pm_runtime_put_err:
++	pm_runtime_put(pispbe->dev);
++pm_runtime_disable_err:
++	pm_runtime_dont_use_autosuspend(pispbe->dev);
++	pm_runtime_disable(pispbe->dev);
++
++	dev_err(&pdev->dev, "%s: returning %d", __func__, ret);
++
++	return ret;
++}
++
++static int pispbe_remove(struct platform_device *pdev)
++{
++	struct pispbe_dev *pispbe = platform_get_drvdata(pdev);
++	int i;
++
++	for (i = PISPBE_NUM_NODE_GROUPS - 1; i >= 0; i--)
++		pispbe_destroy_node_group(&pispbe->node_group[i]);
++
++	pm_runtime_dont_use_autosuspend(pispbe->dev);
++	pm_runtime_disable(pispbe->dev);
++
++	return 0;
++}
++
++static const struct dev_pm_ops pispbe_pm_ops = {
++	SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL)
++};
++
++static const struct of_device_id pispbe_of_match[] = {
++	{
++		.compatible = "raspberrypi,pispbe",
++	},
++	{ /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, pispbe_of_match);
++
++static struct platform_driver pispbe_pdrv = {
++	.probe		= pispbe_probe,
++	.remove		= pispbe_remove,
++	.driver		= {
++		.name	= PISPBE_NAME,
++		.of_match_table = pispbe_of_match,
++		.pm = &pispbe_pm_ops,
++	},
++};
++
++module_platform_driver(pispbe_pdrv);
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+@@ -0,0 +1,533 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * PiSP Back End configuration definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd
++ *
++ */
++#ifndef _PISP_BE_CONFIG_H_
++#define _PISP_BE_CONFIG_H_
++
++#include <linux/types.h>
++
++#include <media/raspberrypi/pisp_common.h>
++
++/* byte alignment for inputs */
++#define PISP_BACK_END_INPUT_ALIGN 4u
++/* alignment for compressed inputs */
++#define PISP_BACK_END_COMPRESSED_ALIGN 8u
++/* minimum required byte alignment for outputs */
++#define PISP_BACK_END_OUTPUT_MIN_ALIGN 16u
++/* preferred byte alignment for outputs */
++#define PISP_BACK_END_OUTPUT_MAX_ALIGN 64u
++
++/* minimum allowed tile width anywhere in the pipeline */
++#define PISP_BACK_END_MIN_TILE_WIDTH 16u
++/* minimum allowed tile width anywhere in the pipeline */
++#define PISP_BACK_END_MIN_TILE_HEIGHT 16u
++
++#define PISP_BACK_END_NUM_OUTPUTS 2
++#define PISP_BACK_END_HOG_OUTPUT 1
++
++#define PISP_BACK_END_NUM_TILES 64
++
++enum pisp_be_bayer_enable {
++	PISP_BE_BAYER_ENABLE_INPUT = 0x000001,
++	PISP_BE_BAYER_ENABLE_DECOMPRESS = 0x000002,
++	PISP_BE_BAYER_ENABLE_DPC = 0x000004,
++	PISP_BE_BAYER_ENABLE_GEQ = 0x000008,
++	PISP_BE_BAYER_ENABLE_TDN_INPUT = 0x000010,
++	PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS = 0x000020,
++	PISP_BE_BAYER_ENABLE_TDN = 0x000040,
++	PISP_BE_BAYER_ENABLE_TDN_COMPRESS = 0x000080,
++	PISP_BE_BAYER_ENABLE_TDN_OUTPUT = 0x000100,
++	PISP_BE_BAYER_ENABLE_SDN = 0x000200,
++	PISP_BE_BAYER_ENABLE_BLC = 0x000400,
++	PISP_BE_BAYER_ENABLE_STITCH_INPUT = 0x000800,
++	PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS = 0x001000,
++	PISP_BE_BAYER_ENABLE_STITCH = 0x002000,
++	PISP_BE_BAYER_ENABLE_STITCH_COMPRESS = 0x004000,
++	PISP_BE_BAYER_ENABLE_STITCH_OUTPUT = 0x008000,
++	PISP_BE_BAYER_ENABLE_WBG = 0x010000,
++	PISP_BE_BAYER_ENABLE_CDN = 0x020000,
++	PISP_BE_BAYER_ENABLE_LSC = 0x040000,
++	PISP_BE_BAYER_ENABLE_TONEMAP = 0x080000,
++	PISP_BE_BAYER_ENABLE_CAC = 0x100000,
++	PISP_BE_BAYER_ENABLE_DEBIN = 0x200000,
++	PISP_BE_BAYER_ENABLE_DEMOSAIC = 0x400000,
++};
++
++enum pisp_be_rgb_enable {
++	PISP_BE_RGB_ENABLE_INPUT = 0x000001,
++	PISP_BE_RGB_ENABLE_CCM = 0x000002,
++	PISP_BE_RGB_ENABLE_SAT_CONTROL = 0x000004,
++	PISP_BE_RGB_ENABLE_YCBCR = 0x000008,
++	PISP_BE_RGB_ENABLE_FALSE_COLOUR = 0x000010,
++	PISP_BE_RGB_ENABLE_SHARPEN = 0x000020,
++	/* Preferred colours would occupy 0x000040 */
++	PISP_BE_RGB_ENABLE_YCBCR_INVERSE = 0x000080,
++	PISP_BE_RGB_ENABLE_GAMMA = 0x000100,
++	PISP_BE_RGB_ENABLE_CSC0 = 0x000200,
++	PISP_BE_RGB_ENABLE_CSC1 = 0x000400,
++	PISP_BE_RGB_ENABLE_DOWNSCALE0 = 0x001000,
++	PISP_BE_RGB_ENABLE_DOWNSCALE1 = 0x002000,
++	PISP_BE_RGB_ENABLE_RESAMPLE0 = 0x008000,
++	PISP_BE_RGB_ENABLE_RESAMPLE1 = 0x010000,
++	PISP_BE_RGB_ENABLE_OUTPUT0 = 0x040000,
++	PISP_BE_RGB_ENABLE_OUTPUT1 = 0x080000,
++	PISP_BE_RGB_ENABLE_HOG = 0x200000
++};
++
++#define PISP_BE_RGB_ENABLE_CSC(i) (PISP_BE_RGB_ENABLE_CSC0 << (i))
++#define PISP_BE_RGB_ENABLE_DOWNSCALE(i) (PISP_BE_RGB_ENABLE_DOWNSCALE0 << (i))
++#define PISP_BE_RGB_ENABLE_RESAMPLE(i) (PISP_BE_RGB_ENABLE_RESAMPLE0 << (i))
++#define PISP_BE_RGB_ENABLE_OUTPUT(i) (PISP_BE_RGB_ENABLE_OUTPUT0 << (i))
++
++/*
++ * We use the enable flags to show when blocks are "dirty", but we need some
++ * extra ones too.
++ */
++enum pisp_be_dirty {
++	PISP_BE_DIRTY_GLOBAL = 0x0001,
++	PISP_BE_DIRTY_SH_FC_COMBINE = 0x0002,
++	PISP_BE_DIRTY_CROP = 0x0004
++};
++
++struct pisp_be_global_config {
++	u32 bayer_enables;
++	u32 rgb_enables;
++	u8 bayer_order;
++	u8 pad[3];
++};
++
++struct pisp_be_input_buffer_config {
++	/* low 32 bits followed by high 32 bits (for each of up to 3 planes) */
++	u32 addr[3][2];
++};
++
++struct pisp_be_dpc_config {
++	u8 coeff_level;
++	u8 coeff_range;
++	u8 pad;
++#define PISP_BE_DPC_FLAG_FOLDBACK 1
++	u8 flags;
++};
++
++struct pisp_be_geq_config {
++	u16 offset;
++#define PISP_BE_GEQ_SHARPER BIT(15)
++#define PISP_BE_GEQ_SLOPE ((1 << 10) - 1)
++	/* top bit is the "sharper" flag, slope value is bottom 10 bits */
++	u16 slope_sharper;
++	u16 min;
++	u16 max;
++};
++
++struct pisp_be_tdn_input_buffer_config {
++	/* low 32 bits followed by high 32 bits */
++	u32 addr[2];
++};
++
++struct pisp_be_tdn_config {
++	u16 black_level;
++	u16 ratio;
++	u16 noise_constant;
++	u16 noise_slope;
++	u16 threshold;
++	u8 reset;
++	u8 pad;
++};
++
++struct pisp_be_tdn_output_buffer_config {
++	/* low 32 bits followed by high 32 bits */
++	u32 addr[2];
++};
++
++struct pisp_be_sdn_config {
++	u16 black_level;
++	u8 leakage;
++	u8 pad;
++	u16 noise_constant;
++	u16 noise_slope;
++	u16 noise_constant2;
++	u16 noise_slope2;
++};
++
++struct pisp_be_stitch_input_buffer_config {
++	/* low 32 bits followed by high 32 bits */
++	u32 addr[2];
++};
++
++#define PISP_BE_STITCH_STREAMING_LONG 0x8000
++#define PISP_BE_STITCH_EXPOSURE_RATIO_MASK 0x7fff
++
++struct pisp_be_stitch_config {
++	u16 threshold_lo;
++	u8 threshold_diff_power;
++	u8 pad;
++
++	/* top bit indicates whether streaming input is the long exposure */
++	u16 exposure_ratio;
++
++	u8 motion_threshold_256;
++	u8 motion_threshold_recip;
++};
++
++struct pisp_be_stitch_output_buffer_config {
++	/* low 32 bits followed by high 32 bits */
++	u32 addr[2];
++};
++
++struct pisp_be_cdn_config {
++	u16 thresh;
++	u8 iir_strength;
++	u8 g_adjust;
++};
++
++#define PISP_BE_LSC_LOG_GRID_SIZE 5
++#define PISP_BE_LSC_GRID_SIZE (1 << PISP_BE_LSC_LOG_GRID_SIZE)
++#define PISP_BE_LSC_STEP_PRECISION 18
++
++struct pisp_be_lsc_config {
++	/* (1<<18) / grid_cell_width */
++	u16 grid_step_x;
++	/* (1<<18) / grid_cell_height */
++	u16 grid_step_y;
++	/* RGB gains jointly encoded in 32 bits */
++	u32 lut_packed[PISP_BE_LSC_GRID_SIZE + 1]
++			   [PISP_BE_LSC_GRID_SIZE + 1];
++};
++
++struct pisp_be_lsc_extra {
++	u16 offset_x;
++	u16 offset_y;
++};
++
++#define PISP_BE_CAC_LOG_GRID_SIZE 3
++#define PISP_BE_CAC_GRID_SIZE (1 << PISP_BE_CAC_LOG_GRID_SIZE)
++#define PISP_BE_CAC_STEP_PRECISION 20
++
++struct pisp_be_cac_config {
++	/* (1<<20) / grid_cell_width */
++	u16 grid_step_x;
++	/* (1<<20) / grid_cell_height */
++	u16 grid_step_y;
++	/* [gridy][gridx][rb][xy] */
++	s8 lut[PISP_BE_CAC_GRID_SIZE + 1][PISP_BE_CAC_GRID_SIZE + 1][2][2];
++};
++
++struct pisp_be_cac_extra {
++	u16 offset_x;
++	u16 offset_y;
++};
++
++#define PISP_BE_DEBIN_NUM_COEFFS 4
++
++struct pisp_be_debin_config {
++	s8 coeffs[PISP_BE_DEBIN_NUM_COEFFS];
++	s8 h_enable;
++	s8 v_enable;
++	s8 pad[2];
++};
++
++#define PISP_BE_TONEMAP_LUT_SIZE 64
++
++struct pisp_be_tonemap_config {
++	u16 detail_constant;
++	u16 detail_slope;
++	u16 iir_strength;
++	u16 strength;
++	u32 lut[PISP_BE_TONEMAP_LUT_SIZE];
++};
++
++struct pisp_be_demosaic_config {
++	u8 sharper;
++	u8 fc_mode;
++	u8 pad[2];
++};
++
++struct pisp_be_ccm_config {
++	s16 coeffs[9];
++	u8 pad[2];
++	s32 offsets[3];
++};
++
++struct pisp_be_sat_control_config {
++	u8 shift_r;
++	u8 shift_g;
++	u8 shift_b;
++	u8 pad;
++};
++
++struct pisp_be_false_colour_config {
++	u8 distance;
++	u8 pad[3];
++};
++
++#define PISP_BE_SHARPEN_SIZE 5
++#define PISP_BE_SHARPEN_FUNC_NUM_POINTS 9
++
++struct pisp_be_sharpen_config {
++	s8 kernel0[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++	s8 pad0[3];
++	s8 kernel1[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++	s8 pad1[3];
++	s8 kernel2[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++	s8 pad2[3];
++	s8 kernel3[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++	s8 pad3[3];
++	s8 kernel4[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++	s8 pad4[3];
++	u16 threshold_offset0;
++	u16 threshold_slope0;
++	u16 scale0;
++	u16 pad5;
++	u16 threshold_offset1;
++	u16 threshold_slope1;
++	u16 scale1;
++	u16 pad6;
++	u16 threshold_offset2;
++	u16 threshold_slope2;
++	u16 scale2;
++	u16 pad7;
++	u16 threshold_offset3;
++	u16 threshold_slope3;
++	u16 scale3;
++	u16 pad8;
++	u16 threshold_offset4;
++	u16 threshold_slope4;
++	u16 scale4;
++	u16 pad9;
++	u16 positive_strength;
++	u16 positive_pre_limit;
++	u16 positive_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS];
++	u16 positive_limit;
++	u16 negative_strength;
++	u16 negative_pre_limit;
++	u16 negative_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS];
++	u16 negative_limit;
++	u8 enables;
++	u8 white;
++	u8 black;
++	u8 grey;
++};
++
++struct pisp_be_sh_fc_combine_config {
++	u8 y_factor;
++	u8 c1_factor;
++	u8 c2_factor;
++	u8 pad;
++};
++
++#define PISP_BE_GAMMA_LUT_SIZE 64
++
++struct pisp_be_gamma_config {
++	u32 lut[PISP_BE_GAMMA_LUT_SIZE];
++};
++
++struct pisp_be_crop_config {
++	u16 offset_x, offset_y;
++	u16 width, height;
++};
++
++#define PISP_BE_RESAMPLE_FILTER_SIZE 96
++
++struct pisp_be_resample_config {
++	u16 scale_factor_h, scale_factor_v;
++	s16 coef[PISP_BE_RESAMPLE_FILTER_SIZE];
++};
++
++struct pisp_be_resample_extra {
++	u16 scaled_width;
++	u16 scaled_height;
++	s16 initial_phase_h[3];
++	s16 initial_phase_v[3];
++};
++
++struct pisp_be_downscale_config {
++	u16 scale_factor_h;
++	u16 scale_factor_v;
++	u16 scale_recip_h;
++	u16 scale_recip_v;
++};
++
++struct pisp_be_downscale_extra {
++	u16 scaled_width;
++	u16 scaled_height;
++};
++
++struct pisp_be_hog_config {
++	u8 compute_signed;
++	u8 channel_mix[3];
++	u32 stride;
++};
++
++struct pisp_be_axi_config {
++	u8 r_qos; /* Read QoS */
++	u8 r_cache_prot; /* Read { prot[2:0], cache[3:0] } */
++	u8 w_qos; /* Write QoS */
++	u8 w_cache_prot; /* Write { prot[2:0], cache[3:0] } */
++};
++
++enum pisp_be_transform {
++	PISP_BE_TRANSFORM_NONE = 0x0,
++	PISP_BE_TRANSFORM_HFLIP = 0x1,
++	PISP_BE_TRANSFORM_VFLIP = 0x2,
++	PISP_BE_TRANSFORM_ROT180 =
++		(PISP_BE_TRANSFORM_HFLIP | PISP_BE_TRANSFORM_VFLIP)
++};
++
++struct pisp_be_output_format_config {
++	struct pisp_image_format_config image;
++	u8 transform;
++	u8 pad[3];
++	u16 lo;
++	u16 hi;
++	u16 lo2;
++	u16 hi2;
++};
++
++struct pisp_be_output_buffer_config {
++	/* low 32 bits followed by high 32 bits (for each of 3 planes) */
++	u32 addr[3][2];
++};
++
++struct pisp_be_hog_buffer_config {
++	/* low 32 bits followed by high 32 bits */
++	u32 addr[2];
++};
++
++struct pisp_be_config {
++	/* I/O configuration: */
++	struct pisp_be_input_buffer_config input_buffer;
++	struct pisp_be_tdn_input_buffer_config tdn_input_buffer;
++	struct pisp_be_stitch_input_buffer_config stitch_input_buffer;
++	struct pisp_be_tdn_output_buffer_config tdn_output_buffer;
++	struct pisp_be_stitch_output_buffer_config stitch_output_buffer;
++	struct pisp_be_output_buffer_config
++				output_buffer[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_hog_buffer_config hog_buffer;
++	/* Processing configuration: */
++	struct pisp_be_global_config global;
++	struct pisp_image_format_config input_format;
++	struct pisp_decompress_config decompress;
++	struct pisp_be_dpc_config dpc;
++	struct pisp_be_geq_config geq;
++	struct pisp_image_format_config tdn_input_format;
++	struct pisp_decompress_config tdn_decompress;
++	struct pisp_be_tdn_config tdn;
++	struct pisp_compress_config tdn_compress;
++	struct pisp_image_format_config tdn_output_format;
++	struct pisp_be_sdn_config sdn;
++	struct pisp_bla_config blc;
++	struct pisp_compress_config stitch_compress;
++	struct pisp_image_format_config stitch_output_format;
++	struct pisp_image_format_config stitch_input_format;
++	struct pisp_decompress_config stitch_decompress;
++	struct pisp_be_stitch_config stitch;
++	struct pisp_be_lsc_config lsc;
++	struct pisp_wbg_config wbg;
++	struct pisp_be_cdn_config cdn;
++	struct pisp_be_cac_config cac;
++	struct pisp_be_debin_config debin;
++	struct pisp_be_tonemap_config tonemap;
++	struct pisp_be_demosaic_config demosaic;
++	struct pisp_be_ccm_config ccm;
++	struct pisp_be_sat_control_config sat_control;
++	struct pisp_be_ccm_config ycbcr;
++	struct pisp_be_sharpen_config sharpen;
++	struct pisp_be_false_colour_config false_colour;
++	struct pisp_be_sh_fc_combine_config sh_fc_combine;
++	struct pisp_be_ccm_config ycbcr_inverse;
++	struct pisp_be_gamma_config gamma;
++	struct pisp_be_ccm_config csc[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_downscale_config downscale[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_resample_config resample[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_output_format_config
++				output_format[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_hog_config hog;
++	struct pisp_be_axi_config axi;
++	/* Non-register fields: */
++	struct pisp_be_lsc_extra lsc_extra;
++	struct pisp_be_cac_extra cac_extra;
++	struct pisp_be_downscale_extra
++				downscale_extra[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_resample_extra resample_extra[PISP_BACK_END_NUM_OUTPUTS];
++	struct pisp_be_crop_config crop;
++	struct pisp_image_format_config hog_format;
++	u32 dirty_flags_bayer; /* these use pisp_be_bayer_enable */
++	u32 dirty_flags_rgb; /* use pisp_be_rgb_enable */
++	u32 dirty_flags_extra; /* these use pisp_be_dirty_t */
++};
++
++/*
++ * We also need a tile structure to describe the size of the tiles going
++ * through the pipeline.
++ */
++
++enum pisp_tile_edge {
++	PISP_LEFT_EDGE = (1 << 0),
++	PISP_RIGHT_EDGE = (1 << 1),
++	PISP_TOP_EDGE = (1 << 2),
++	PISP_BOTTOM_EDGE = (1 << 3)
++};
++
++struct pisp_tile {
++	u8 edge; // enum pisp_tile_edge
++	u8 pad0[3];
++	// 4 bytes
++	u32 input_addr_offset;
++	u32 input_addr_offset2;
++	u16 input_offset_x;
++	u16 input_offset_y;
++	u16 input_width;
++	u16 input_height;
++	// 20 bytes
++	u32 tdn_input_addr_offset;
++	u32 tdn_output_addr_offset;
++	u32 stitch_input_addr_offset;
++	u32 stitch_output_addr_offset;
++	// 36 bytes
++	u32 lsc_grid_offset_x;
++	u32 lsc_grid_offset_y;
++	// 44 bytes
++	u32 cac_grid_offset_x;
++	u32 cac_grid_offset_y;
++	// 52 bytes
++	u16 crop_x_start[PISP_BACK_END_NUM_OUTPUTS];
++	u16 crop_x_end[PISP_BACK_END_NUM_OUTPUTS];
++	u16 crop_y_start[PISP_BACK_END_NUM_OUTPUTS];
++	u16 crop_y_end[PISP_BACK_END_NUM_OUTPUTS];
++	// 68 bytes
++	/* Ordering is planes then branches */
++	u16 downscale_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS];
++	u16 downscale_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS];
++	// 92 bytes
++	u16 resample_in_width[PISP_BACK_END_NUM_OUTPUTS];
++	u16 resample_in_height[PISP_BACK_END_NUM_OUTPUTS];
++	// 100 bytes
++	/* Ordering is planes then branches */
++	u16 resample_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS];
++	u16 resample_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS];
++	// 124 bytes
++	u16 output_offset_x[PISP_BACK_END_NUM_OUTPUTS];
++	u16 output_offset_y[PISP_BACK_END_NUM_OUTPUTS];
++	u16 output_width[PISP_BACK_END_NUM_OUTPUTS];
++	u16 output_height[PISP_BACK_END_NUM_OUTPUTS];
++	// 140 bytes
++	u32 output_addr_offset[PISP_BACK_END_NUM_OUTPUTS];
++	u32 output_addr_offset2[PISP_BACK_END_NUM_OUTPUTS];
++	// 156 bytes
++	u32 output_hog_addr_offset;
++	// 160 bytes
++};
++
++static_assert(sizeof(struct pisp_tile) == 160);
++
++struct pisp_be_tiles_config {
++	struct pisp_be_config config;
++	struct pisp_tile tiles[PISP_BACK_END_NUM_TILES];
++	int num_tiles;
++};
++
++#endif /* _PISP_BE_CONFIG_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -0,0 +1,469 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PiSP Back End driver image format definitions.
++ *
++ * Copyright (c) 2021 Raspberry Pi Ltd
++ */
++
++#ifndef _PISP_BE_FORMATS_
++#define _PISP_BE_FORMATS_
++
++#include <linux/bits.h>
++#include <linux/videodev2.h>
++
++#define MAX_PLANES 3
++#define P3(x) ((x) * 8)
++
++struct pisp_be_format {
++	unsigned int fourcc;
++	unsigned int align;
++	unsigned int bit_depth;
++	/* 0P3 factor for plane sizing */
++	unsigned int plane_factor[MAX_PLANES];
++	unsigned int num_planes;
++	unsigned int colorspace_mask;
++	enum v4l2_colorspace colorspace_default;
++};
++
++#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace)
++
++#define V4L2_COLORSPACE_MASK_JPEG V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG)
++#define V4L2_COLORSPACE_MASK_SMPTE170M V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M)
++#define V4L2_COLORSPACE_MASK_REC709 V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709)
++#define V4L2_COLORSPACE_MASK_SRGB V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB)
++#define V4L2_COLORSPACE_MASK_RAW V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW)
++
++/*
++ * All three colour spaces JPEG, SMPTE170M and REC709 are fundamentally sRGB
++ * underneath (as near as makes no difference to us), just with different YCbCr
++ * encodings. Therefore the ISP can generate sRGB on its main output and any of
++ * the others on its low resolution output. Applications should, when using both
++ * outputs, program the colour spaces on them to be the same, matching whatever
++ * is requested for the low resolution output, even if the main output is
++ * producing an RGB format. In turn this requires us to allow all these colour
++ * spaces for every YUV/RGB output format.
++ */
++#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG |	\
++				       V4L2_COLORSPACE_MASK_SRGB |	\
++				       V4L2_COLORSPACE_MASK_SMPTE170M |	\
++				       V4L2_COLORSPACE_MASK_REC709)
++
++static const struct pisp_be_format supported_formats[] = {
++	/* Single plane YUV formats */
++	{
++		.fourcc		    = V4L2_PIX_FMT_YUV420,
++		/* 128 alignment to ensure U/V planes are 64 byte aligned. */
++		.align		    = 128,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.25), P3(0.25) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_JPEG,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YVU420,
++		/* 128 alignment to ensure U/V planes are 64 byte aligned. */
++		.align		    = 128,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.25), P3(0.25) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_NV12,
++		.align		    = 32,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_NV21,
++		.align		    = 32,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YUYV,
++		.align		    = 64,
++		.bit_depth	    = 16,
++		.plane_factor	    = { P3(1) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_UYVY,
++		.align		    = 64,
++		.bit_depth	    = 16,
++		.plane_factor	    = { P3(1) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YVYU,
++		.align		    = 64,
++		.bit_depth	    = 16,
++		.plane_factor	    = { P3(1) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_VYUY,
++		.align		    = 64,
++		.bit_depth	    = 16,
++		.plane_factor	    = { P3(1) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	/* Multiplane YUV formats */
++	{
++		.fourcc		    = V4L2_PIX_FMT_YUV420M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.25), P3(0.25) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_JPEG,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_NV12M,
++		.align		    = 32,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5) },
++		.num_planes	    = 2,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_NV21M,
++		.align		    = 32,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5) },
++		.num_planes	    = 2,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YVU420M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.25), P3(0.25) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YUV422M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5), P3(0.5) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_JPEG,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YVU422M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(0.5), P3(0.5) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YUV444M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(1), P3(1) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_JPEG,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_YVU444M,
++		.align		    = 64,
++		.bit_depth	    = 8,
++		.plane_factor	    = { P3(1), P3(1), P3(1) },
++		.num_planes	    = 3,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++	},
++	/* RGB formats */
++	{
++		.fourcc		    = V4L2_PIX_FMT_RGB24,
++		.align		    = 32,
++		.bit_depth	    = 24,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SRGB,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_BGR24,
++		.align		    = 32,
++		.bit_depth	    = 24,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SRGB,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_XBGR32,
++		.align		    = 64,
++		.bit_depth	    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SRGB,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_RGBX32,
++		.align		    = 64,
++		.bit_depth	    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++		.colorspace_default = V4L2_COLORSPACE_SRGB,
++	},
++	/* Bayer formats - 8-bit */
++	{
++		.fourcc		    = V4L2_PIX_FMT_SRGGB8,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SBGGR8,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGRBG8,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGBRG8,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	/* Bayer formats - 16-bit */
++	{
++		.fourcc		    = V4L2_PIX_FMT_SRGGB16,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SBGGR16,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGRBG16,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGBRG16,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		/* Bayer formats unpacked to 16bpp */
++		/* 10 bit */
++		.fourcc		    = V4L2_PIX_FMT_SRGGB10,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SBGGR10,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGRBG10,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGBRG10,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		/* 12 bit */
++		.fourcc		    = V4L2_PIX_FMT_SRGGB12,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SBGGR12,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGRBG12,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGBRG12,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		/* 14 bit */
++		.fourcc		    = V4L2_PIX_FMT_SRGGB14,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SBGGR14,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGRBG14,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_SGBRG14,
++		.bit_depth	    = 16,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	/* Bayer formats - 16-bit PiSP Compressed */
++	{
++		.fourcc		    = V4L2_PIX_FMT_PISP_COMP1_BGGR,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_PISP_COMP1_RGGB,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_PISP_COMP1_GRBG,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++	{
++		.fourcc		    = V4L2_PIX_FMT_PISP_COMP1_GBRG,
++		.bit_depth	    = 8,
++		.align		    = 32,
++		.plane_factor	    = { P3(1.0) },
++		.num_planes	    = 1,
++		.colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++		.colorspace_default = V4L2_COLORSPACE_RAW,
++	},
++};
++
++static const struct pisp_be_format meta_out_supported_formats[] = {
++	/* Configuration buffer format. */
++	{
++		.fourcc		    = V4L2_META_FMT_RPI_BE_CFG,
++	},
++};
++
++#endif /* _PISP_BE_FORMATS_ */
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,6 +1452,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ 	case V4L2_PIX_FMT_NV12M_10BE_8L128:	descr = "10-bit NV12M (8x128 Linear, BE)"; 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;
++	case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP Config format"; break;
+ 
+ 	default:
+ 		/* Compressed formats */
+@@ -1503,6 +1504,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ 		case V4L2_PIX_FMT_MT21C:	descr = "Mediatek Compressed Format"; break;
+ 		case V4L2_PIX_FMT_QC08C:	descr = "QCOM Compressed 8-bit Format"; break;
+ 		case V4L2_PIX_FMT_QC10C:	descr = "QCOM Compressed 10-bit Format"; break;
++		case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
+ 		default:
+ 			if (fmt->description[0])
+ 				return;
+--- /dev/null
++++ b/include/media/raspberrypi/pisp_common.h
+@@ -0,0 +1,65 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Raspberry Pi PiSP common configuration definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi (Trading) Ltd.
++ *
++ */
++#ifndef _PISP_COMMON_H_
++#define _PISP_COMMON_H_
++
++#include <linux/types.h>
++
++#include "pisp_types.h"
++
++struct pisp_bla_config {
++	uint16_t black_level_r;
++	uint16_t black_level_gr;
++	uint16_t black_level_gb;
++	uint16_t black_level_b;
++	uint16_t output_black_level;
++	uint8_t pad[2];
++};
++
++struct pisp_wbg_config {
++	uint16_t gain_r;
++	uint16_t gain_g;
++	uint16_t gain_b;
++	uint8_t pad[2];
++};
++
++struct pisp_compress_config {
++	/* value subtracted from incoming data */
++	uint16_t offset;
++	uint8_t pad;
++	/* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++	uint8_t mode;
++};
++
++struct pisp_decompress_config {
++	/* value added to reconstructed data */
++	uint16_t offset;
++	uint8_t pad;
++	/* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++	uint8_t mode;
++};
++
++enum pisp_axi_flags {
++	/* round down bursts to end at a 32-byte boundary, to align following bursts */
++	PISP_AXI_FLAG_ALIGN = 128,
++	 /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */
++	PISP_AXI_FLAG_PAD = 64,
++	/* for FE writer: Use Output FIFO level to trigger "panic" */
++	PISP_AXI_FLAG_PANIC = 32
++};
++
++struct pisp_axi_config {
++	/* burst length minus one, which must be in the range 0:15; OR'd with flags */
++	uint8_t maxlen_flags;
++	/* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */
++	uint8_t cache_prot;
++	/* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */
++	uint16_t qos;
++};
++
++#endif /* _PISP_COMMON_H_ */
+--- /dev/null
++++ b/include/media/raspberrypi/pisp_types.h
+@@ -0,0 +1,144 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Raspberry Pi PiSP common types.
++ *
++ * Copyright (C) 2021 - Raspberry Pi (Trading) Ltd.
++ *
++ */
++#ifndef _PISP_TYPES_H_
++#define _PISP_TYPES_H_
++
++/* This definition must match the format description in the hardware exactly! */
++struct pisp_image_format_config {
++	/* size in pixels */
++	uint16_t width, height;
++	/* must match struct pisp_image_format below */
++	uint32_t format;
++	int32_t stride;
++	/* some planar image formats will need a second stride */
++	int32_t stride2;
++};
++
++static_assert(sizeof(struct pisp_image_format_config) == 16);
++
++enum pisp_bayer_order {
++	/*
++	 * Note how bayer_order&1 tells you if G is on the even pixels of the
++	 * checkerboard or not, and bayer_order&2 tells you if R is on the even
++	 * rows or is swapped with B. Note that if the top (of the 8) bits is
++	 * set, this denotes a monochrome or greyscale image, and the lower bits
++	 * should all be ignored.
++	 */
++	PISP_BAYER_ORDER_RGGB = 0,
++	PISP_BAYER_ORDER_GBRG = 1,
++	PISP_BAYER_ORDER_BGGR = 2,
++	PISP_BAYER_ORDER_GRBG = 3,
++	PISP_BAYER_ORDER_GREYSCALE = 128
++};
++
++enum pisp_image_format {
++	/*
++	 * Precise values are mostly tbd. Generally these will be portmanteau
++	 * values comprising bit fields and flags. This format must be shared
++	 * throughout the PiSP.
++	 */
++	PISP_IMAGE_FORMAT_BPS_8 = 0x00000000,
++	PISP_IMAGE_FORMAT_BPS_10 = 0x00000001,
++	PISP_IMAGE_FORMAT_BPS_12 = 0x00000002,
++	PISP_IMAGE_FORMAT_BPS_16 = 0x00000003,
++	PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003,
++
++	PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000,
++	PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010,
++	PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020,
++	PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030,
++
++	PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000,
++	PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100,
++	PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200,
++	PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300,
++
++	PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000,
++	PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000,
++
++	PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000,
++	PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000,
++	PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000,
++	PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000,
++	PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000,
++	PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000,
++	PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000,
++	PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000,
++	PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000,
++	PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000,
++
++	PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000,
++
++	PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000,
++	PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000,
++	PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000,
++	PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000,
++	PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000,
++
++	/* Lastly a few specific instantiations of the above. */
++	PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16,
++	PISP_IMAGE_FORMAT_THREE_16 =
++		PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL
++};
++
++#define PISP_IMAGE_FORMAT_bps_8(fmt)                                           \
++	(((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8)
++#define PISP_IMAGE_FORMAT_bps_10(fmt)                                          \
++	(((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10)
++#define PISP_IMAGE_FORMAT_bps_12(fmt)                                          \
++	(((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12)
++#define PISP_IMAGE_FORMAT_bps_16(fmt)                                          \
++	(((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16)
++#define PISP_IMAGE_FORMAT_bps(fmt)                                             \
++	(((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) ?                                  \
++		       8 + (2 << (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) - 1)) :   \
++		       8)
++#define PISP_IMAGE_FORMAT_shift(fmt)                                           \
++	(((fmt)&PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1)
++#define PISP_IMAGE_FORMAT_three_channel(fmt)                                   \
++	((fmt)&PISP_IMAGE_FORMAT_THREE_CHANNEL)
++#define PISP_IMAGE_FORMAT_single_channel(fmt)                                  \
++	(!((fmt)&PISP_IMAGE_FORMAT_THREE_CHANNEL))
++#define PISP_IMAGE_FORMAT_compressed(fmt)                                      \
++	(((fmt)&PISP_IMAGE_FORMAT_COMPRESSION_MASK) !=                         \
++	 PISP_IMAGE_FORMAT_UNCOMPRESSED)
++#define PISP_IMAGE_FORMAT_sampling_444(fmt)                                    \
++	(((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                            \
++	 PISP_IMAGE_FORMAT_SAMPLING_444)
++#define PISP_IMAGE_FORMAT_sampling_422(fmt)                                    \
++	(((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                            \
++	 PISP_IMAGE_FORMAT_SAMPLING_422)
++#define PISP_IMAGE_FORMAT_sampling_420(fmt)                                    \
++	(((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                            \
++	 PISP_IMAGE_FORMAT_SAMPLING_420)
++#define PISP_IMAGE_FORMAT_order_normal(fmt)                                    \
++	(!((fmt)&PISP_IMAGE_FORMAT_ORDER_SWAPPED))
++#define PISP_IMAGE_FORMAT_order_swapped(fmt)                                   \
++	((fmt)&PISP_IMAGE_FORMAT_ORDER_SWAPPED)
++#define PISP_IMAGE_FORMAT_interleaved(fmt)                                     \
++	(((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                           \
++	 PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED)
++#define PISP_IMAGE_FORMAT_semiplanar(fmt)                                      \
++	(((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                           \
++	 PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR)
++#define PISP_IMAGE_FORMAT_planar(fmt)                                          \
++	(((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                           \
++	 PISP_IMAGE_FORMAT_PLANARITY_PLANAR)
++#define PISP_IMAGE_FORMAT_wallpaper(fmt)                                       \
++	((fmt)&PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++#define PISP_IMAGE_FORMAT_HOG(fmt)                                             \
++	((fmt) &                                                               \
++	 (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED))
++
++#define PISP_WALLPAPER_WIDTH 128 // in bytes
++
++#endif /* _PISP_TYPES_H_ */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -793,6 +793,9 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_IPU3_SGRBG10	v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
+ #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
+ 
++/* The pixel format for all our buffers (the precise format is found in the config buffer). */
++#define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
++
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
+ #define V4L2_SDR_FMT_CU16LE       v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
+@@ -822,6 +825,9 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_PARAMS	v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A	v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+ 
++/* The metadata format identifier for our configuration buffers. */
++#define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C')
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
+ 

+ 386 - 0
target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch

@@ -0,0 +1,386 @@
+From 89b748416358e4e04765b9a4f20e1c3d256b9d9e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 28 Jul 2021 11:13:39 +0100
+Subject: [PATCH] irqchip: irq-bcm2712-mip: Support for 2712's MIP
+
+irqchip: irq-bcm2712-mip: specify bitmap search size as ilog2(N) not N
+
+Freeing also has the same interface.
+
+irqchip: irq-bcm2712-mip: Fix build warnings
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+irqchip: bcm2712-mip: add a quick hack to optionally shift MSI vectors
+
+There are two MIP peripherals in bcm2712, the first gets a first-class
+treatment where 64 consecutive GIC SPIs are assigned to all 64 output
+vectors. The second gets an agglomeration of 17 GIC SPIs, but only 8 of
+these are consecutive starting at the 8th output vector.
+
+For now, allow the use of this smaller contiguous range within a larger
+whole.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/irqchip/Kconfig           |   8 +
+ drivers/irqchip/Makefile          |   1 +
+ drivers/irqchip/irq-bcm2712-mip.c | 325 ++++++++++++++++++++++++++++++
+ 3 files changed, 334 insertions(+)
+ create mode 100644 drivers/irqchip/irq-bcm2712-mip.c
+
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -109,6 +109,14 @@ config I8259
+ 	bool
+ 	select IRQ_DOMAIN
+ 
++config BCM2712_MIP
++	bool "Broadcom 2712 MSI-X Interrupt Peripheral support"
++	depends on ARM_GIC
++	select GENERIC_IRQ_CHIP
++	select IRQ_DOMAIN
++	help
++	  Enable support for the Broadcom BCM2712 MSI-X target peripheral.
++
+ config BCM6345_L1_IRQ
+ 	bool
+ 	select GENERIC_IRQ_CHIP
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-
+ obj-$(CONFIG_XILINX_INTC)		+= irq-xilinx-intc.o
+ obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
+ obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
++obj-$(CONFIG_BCM2712_MIP)		+= irq-bcm2712-mip.o
+ obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+ obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
+ obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
+--- /dev/null
++++ b/drivers/irqchip/irq-bcm2712-mip.c
+@@ -0,0 +1,325 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2021 Raspberry Pi Ltd., All Rights Reserved.
++ */
++
++#include <linux/pci.h>
++#include <linux/msi.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++
++#include <linux/irqchip.h>
++
++#define MIP_INT_RAISED		0x00
++#define MIP_INT_CLEARED		0x10
++#define MIP_INT_CFGL_HOST	0x20
++#define MIP_INT_CFGH_HOST	0x30
++#define MIP_INT_MASKL_HOST	0x40
++#define MIP_INT_MASKH_HOST	0x50
++#define MIP_INT_MASKL_VPU	0x60
++#define MIP_INT_MASKH_VPU	0x70
++#define MIP_INT_STATUSL_HOST	0x80
++#define MIP_INT_STATUSH_HOST	0x90
++#define MIP_INT_STATUSL_VPU	0xa0
++#define MIP_INT_STATUSH_VPU	0xb0
++
++struct mip_priv {
++	spinlock_t msi_map_lock;
++	spinlock_t hw_lock;
++	void * __iomem base;
++	phys_addr_t msg_addr;
++	u32 msi_base;		/* The SGI number that MSIs start */
++	u32 num_msis;		/* The number of SGIs for MSIs */
++	u32 msi_offset;		/* Shift the allocated msi up by N */
++	unsigned long *msi_map;
++};
++
++static void mip_mask_msi_irq(struct irq_data *d)
++{
++	pci_msi_mask_irq(d);
++	irq_chip_mask_parent(d);
++}
++
++static void mip_unmask_msi_irq(struct irq_data *d)
++{
++	pci_msi_unmask_irq(d);
++	irq_chip_unmask_parent(d);
++}
++
++static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
++{
++	struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++
++	msg->address_hi = upper_32_bits(priv->msg_addr);
++	msg->address_lo = lower_32_bits(priv->msg_addr);
++	msg->data = d->hwirq;
++}
++
++// The "bus-specific" irq_chip (the MIP doesn't _have_ to be used with PCIe)
++
++static struct irq_chip mip_msi_irq_chip = {
++	.name			= "MIP-MSI",
++	.irq_unmask		= mip_unmask_msi_irq,
++	.irq_mask		= mip_mask_msi_irq,
++	.irq_eoi		= irq_chip_eoi_parent,
++	.irq_set_affinity	= irq_chip_set_affinity_parent,
++};
++
++static struct msi_domain_info mip_msi_domain_info = {
++	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++		   MSI_FLAG_PCI_MSIX),
++	.chip	= &mip_msi_irq_chip,
++};
++
++// The "middle" irq_chip (the hardware control part)
++
++static struct irq_chip mip_irq_chip = {
++	.name			= "MIP",
++	.irq_mask		= irq_chip_mask_parent,
++	.irq_unmask		= irq_chip_unmask_parent,
++	.irq_eoi		= irq_chip_eoi_parent,
++	.irq_set_affinity	= irq_chip_set_affinity_parent,
++	.irq_set_type		= irq_chip_set_type_parent,
++	.irq_compose_msi_msg	= mip_compose_msi_msg,
++};
++
++
++// And a domain to connect it to its parent (the GIC)
++
++static int mip_irq_domain_alloc(struct irq_domain *domain,
++				unsigned int virq, unsigned int nr_irqs,
++				void *args)
++{
++	struct mip_priv *priv = domain->host_data;
++	struct irq_fwspec fwspec;
++	struct irq_data *irqd;
++	int hwirq, ret, i;
++
++	spin_lock(&priv->msi_map_lock);
++
++	hwirq = bitmap_find_free_region(priv->msi_map, priv->num_msis, ilog2(nr_irqs));
++
++	spin_unlock(&priv->msi_map_lock);
++
++	if (hwirq < 0)
++		return -ENOSPC;
++
++	hwirq += priv->msi_offset;
++	fwspec.fwnode = domain->parent->fwnode;
++	fwspec.param_count = 3;
++	fwspec.param[0] = 0;
++	fwspec.param[1] = hwirq + priv->msi_base;
++	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
++
++	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
++	if (ret)
++	    return ret;
++
++	for (i = 0; i < nr_irqs; i++) {
++		irqd = irq_domain_get_irq_data(domain->parent, virq + i);
++		irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING);
++
++		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
++					      &mip_irq_chip, priv);
++		irqd = irq_get_irq_data(virq + i);
++		irqd_set_single_target(irqd);
++		irqd_set_affinity_on_activate(irqd);
++	}
++
++	return 0;
++}
++
++static void mip_irq_domain_free(struct irq_domain *domain,
++				unsigned int virq, unsigned int nr_irqs)
++{
++	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++	struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++
++	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
++	d->hwirq -= priv->msi_offset;
++
++	spin_lock(&priv->msi_map_lock);
++
++	bitmap_release_region(priv->msi_map, d->hwirq, ilog2(nr_irqs));
++
++	spin_unlock(&priv->msi_map_lock);
++}
++
++#if 0
++static int mip_irq_domain_activate(struct irq_domain *domain,
++				   struct irq_data *d, bool reserve)
++{
++	struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++	unsigned long flags;
++	unsigned int irq = d->hwirq;
++	void *__iomem reg = priv->base +
++		((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST);
++	u32 val;
++
++	spin_lock_irqsave(&priv->hw_lock, flags);
++	val = readl(reg);
++	val &= ~(1 << (irq % 32)); // Clear the mask
++	writel(val, reg);
++	spin_unlock_irqrestore(&priv->hw_lock, flags);
++	return 0;
++}
++
++static void mip_irq_domain_deactivate(struct irq_domain *domain,
++				      struct irq_data *d)
++{
++	struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++	unsigned long flags;
++	unsigned int irq = d->hwirq - priv->msi_base;
++	void *__iomem reg = priv->base +
++		((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST);
++	u32 val;
++
++	spin_lock_irqsave(&priv->hw_lock, flags);
++	val = readl(reg);
++	val |= (1 << (irq % 32)); // Mask it out
++	writel(val, reg);
++	spin_unlock_irqrestore(&priv->hw_lock, flags);
++}
++#endif
++
++static const struct irq_domain_ops mip_irq_domain_ops = {
++	.alloc		= mip_irq_domain_alloc,
++	.free		= mip_irq_domain_free,
++	//.activate	= mip_irq_domain_activate,
++	//.deactivate	= mip_irq_domain_deactivate,
++};
++
++static int mip_init_domains(struct mip_priv *priv,
++			    struct device_node *node)
++{
++	struct irq_domain *middle_domain, *msi_domain, *gic_domain;
++	struct device_node *gic_node;
++
++	gic_node = of_irq_find_parent(node);
++	if (!gic_node) {
++		pr_err("Failed to find the GIC node\n");
++		return -ENODEV;
++	}
++
++	gic_domain = irq_find_host(gic_node);
++	if (!gic_domain) {
++		pr_err("Failed to find the GIC domain\n");
++		return -ENXIO;
++	}
++
++	middle_domain = irq_domain_add_tree(NULL,
++					    &mip_irq_domain_ops,
++					    priv);
++	if (!middle_domain) {
++		pr_err("Failed to create the MIP middle domain\n");
++		return -ENOMEM;
++	}
++
++	middle_domain->parent = gic_domain;
++
++	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
++					       &mip_msi_domain_info,
++					       middle_domain);
++	if (!msi_domain) {
++		pr_err("Failed to create MSI domain\n");
++		irq_domain_remove(middle_domain);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++static int __init mip_of_msi_init(struct device_node *node,
++				  struct device_node *parent)
++{
++	struct mip_priv *priv;
++	struct resource res;
++	int ret;
++
++	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	spin_lock_init(&priv->msi_map_lock);
++	spin_lock_init(&priv->hw_lock);
++
++	ret = of_address_to_resource(node, 0, &res);
++	if (ret) {
++		pr_err("Failed to allocate resource\n");
++		goto err_priv;
++	}
++
++	if (of_property_read_u32(node, "brcm,msi-base-spi", &priv->msi_base)) {
++		pr_err("Unable to parse MSI base\n");
++		ret = -EINVAL;
++		goto err_priv;
++	}
++
++	if (of_property_read_u32(node, "brcm,msi-num-spis", &priv->num_msis)) {
++		pr_err("Unable to parse MSI numbers\n");
++		ret = -EINVAL;
++		goto err_priv;
++	}
++
++	if (of_property_read_u32(node, "brcm,msi-offset", &priv->msi_offset))
++		priv->msi_offset = 0;
++
++	if (of_property_read_u64(node, "brcm,msi-pci-addr", &priv->msg_addr)) {
++		pr_err("Unable to parse MSI address\n");
++		ret = -EINVAL;
++		goto err_priv;
++	}
++
++	priv->base = ioremap(res.start, resource_size(&res));
++	if (!priv->base) {
++		pr_err("Failed to ioremap regs\n");
++		ret = -ENOMEM;
++		goto err_priv;
++	}
++
++	priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_msis),
++				sizeof(*priv->msi_map),
++				GFP_KERNEL);
++	if (!priv->msi_map) {
++		ret = -ENOMEM;
++		goto err_base;
++	}
++
++	pr_debug("Registering %d msixs, starting at %d\n",
++		 priv->num_msis, priv->msi_base);
++
++	/*
++	 * Begin with all MSI-Xs masked in for the host, masked out for the
++	 * VPU, and edge-triggered.
++	 */
++	writel(0, priv->base + MIP_INT_MASKL_HOST);
++	writel(0, priv->base + MIP_INT_MASKH_HOST);
++	writel(~0, priv->base + MIP_INT_MASKL_VPU);
++	writel(~0, priv->base + MIP_INT_MASKH_VPU);
++	writel(~0, priv->base + MIP_INT_CFGL_HOST);
++	writel(~0, priv->base + MIP_INT_CFGH_HOST);
++
++	ret = mip_init_domains(priv, node);
++	if (ret) {
++		pr_err("Failed to allocate msi_map\n");
++		goto err_map;
++	}
++
++	return 0;
++
++err_map:
++	kfree(priv->msi_map);
++
++err_base:
++	iounmap(priv->base);
++
++err_priv:
++	kfree(priv);
++
++	pr_err("%s: failed - err %d\n", __func__, ret);
++
++	return ret;
++}
++IRQCHIP_DECLARE(bcm_mip, "brcm,bcm2712-mip-intc", mip_of_msi_init);

+ 47 - 0
target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch

@@ -0,0 +1,47 @@
+From 87b1126181f79fb2558652af0d7fafd9deaab5f3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 7 Sep 2021 14:49:00 +0100
+Subject: [PATCH] reset: reset-brcmstb-rescal: Support shared use
+
+reset_control_reset should not be used with shared reset controllers.
+Add support for reset_control_assert and _deassert to get the desired
+behaviour and avoid ugly warnings in the kernel log.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/reset/reset-brcmstb-rescal.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/reset/reset-brcmstb-rescal.c
++++ b/drivers/reset/reset-brcmstb-rescal.c
+@@ -20,6 +20,7 @@ struct brcm_rescal_reset {
+ 	struct reset_controller_dev rcdev;
+ };
+ 
++/* Also doubles a deassert */
+ static int brcm_rescal_reset_set(struct reset_controller_dev *rcdev,
+ 				 unsigned long id)
+ {
+@@ -52,6 +53,13 @@ static int brcm_rescal_reset_set(struct
+ 	return 0;
+ }
+ 
++/* A dummy function - deassert/reset does all the work */
++static int brcm_rescal_reset_assert(struct reset_controller_dev *rcdev,
++				    unsigned long id)
++{
++	return 0;
++}
++
+ static int brcm_rescal_reset_xlate(struct reset_controller_dev *rcdev,
+ 				   const struct of_phandle_args *reset_spec)
+ {
+@@ -61,6 +69,8 @@ static int brcm_rescal_reset_xlate(struc
+ 
+ static const struct reset_control_ops brcm_rescal_reset_ops = {
+ 	.reset = brcm_rescal_reset_set,
++	.deassert = brcm_rescal_reset_set,
++	.assert = brcm_rescal_reset_assert,
+ };
+ 
+ static int brcm_rescal_reset_probe(struct platform_device *pdev)

+ 418 - 0
target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch

@@ -0,0 +1,418 @@
+From bd36586dd9e05bde8e23dc3d99771269b48b65f8 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 10 Sep 2021 17:20:45 +0100
+Subject: [PATCH] net: macb: Also set DMA coherent mask
+
+macb: Add device tree properties that allow configuration of the AXI max pipeline register
+
+net: macb: add support for ethtool interrupt moderation configuration
+
+Only global throttling of rx or tx by time quanta is supported.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+macb: add platform device shutdown function. Prevents AXI master over PCIE from hanging when the host is rebooted.
+
+net: macb: increase polling interval for MDIO completion
+
+MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
+is a bit aggressive, so increase to 100us as the transaction
+usually takes 100-200us to complete.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+net: macb: Several patches for RP1
+
+64-bit RX fix
+
+Also set DMA coherent mask
+
+Add device tree properties that allow configuration of the AXI max
+pipeline register
+
+Add support for ethtool interrupt moderation configuration
+
+Only global throttling of rx or tx by time quanta is supported.
+
+Add platform device shutdown function. Prevents AXI master over PCIE
+from hanging when the host is rebooted.
+
+Increase polling interval for MDIO completion
+
+MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
+is a bit aggressive, so increase to 100us as the transaction
+usually takes 100-200us to complete.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+net: macb: Support the phy-reset-gpios property
+
+Allow a PHY to be reset with an optional GPIO. The reset duration can
+be specified in milliseconds - the default is 10ms.
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+drivers: net: macb: close device on driver shutdown
+
+Fix some suspicious locking and instead call into macb_close, which
+deregisters and frees all resources the corresponding macb_open
+claimed.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+net: macb: add hack to prevent TX stalls in a quiet system
+
+See https://github.com/raspberrypi/linux-2712/issues/89
+
+There is some critical window during TX where a further write to the
+TSTART bit while TX is active does not cause newly queued TX descriptors
+to be consumed.
+
+For now "wait a bit, then try anyway" seems to work.
+
+Requires further investigation, but this unsticks NFS reliably.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+net: macb: set default interrupt moderation for GEM hardware
+
+Defaulting to intmod = 0 is antisocial, as the MAC can generate over
+130,000 interrupts per second. 50us is a sensible default.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/net/ethernet/cadence/macb.h      |  25 ++++
+ drivers/net/ethernet/cadence/macb_main.c | 151 ++++++++++++++++++++++-
+ 2 files changed, 174 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/cadence/macb.h
++++ b/drivers/net/ethernet/cadence/macb.h
+@@ -84,6 +84,8 @@
+ #define GEM_DMACFG		0x0010 /* DMA Configuration */
+ #define GEM_JML			0x0048 /* Jumbo Max Length */
+ #define GEM_HS_MAC_CONFIG	0x0050 /* GEM high speed config */
++#define GEM_AMP			0x0054 /* AXI Max Pipeline */
++#define GEM_INTMOD		0x005c /* Interrupt moderation */
+ #define GEM_HRB			0x0080 /* Hash Bottom */
+ #define GEM_HRT			0x0084 /* Hash Top */
+ #define GEM_SA1B		0x0088 /* Specific1 Bottom */
+@@ -346,6 +348,21 @@
+ #define GEM_ADDR64_OFFSET	30 /* Address bus width - 64b or 32b */
+ #define GEM_ADDR64_SIZE		1
+ 
++/* Bitfields in AMP */
++#define GEM_AR2R_MAX_PIPE_OFFSET	0  /* Maximum number of outstanding AXI read requests */
++#define GEM_AR2R_MAX_PIPE_SIZE		8
++#define GEM_AW2W_MAX_PIPE_OFFSET	8  /* Maximum number of outstanding AXI write requests */
++#define GEM_AW2W_MAX_PIPE_SIZE		8
++#define GEM_AW2B_FILL_OFFSET		16 /* Select wether the max AW2W transactions operates between: */
++#define GEM_AW2B_FILL_AW2W		0  /*   0: the AW to W AXI channel */
++#define GEM_AW2B_FILL_AW2B		1  /*   1: AW to B channel */
++#define GEM_AW2B_FILL_SIZE              1
++
++/* Bitfields in INTMOD */
++#define GEM_RX_MODERATION_OFFSET	0  /* RX interrupt moderation */
++#define GEM_RX_MODERATION_SIZE		8
++#define GEM_TX_MODERATION_OFFSET	16 /* TX interrupt moderation */
++#define GEM_TX_MODERATION_SIZE		8
+ 
+ /* Bitfields in NSR */
+ #define MACB_NSR_LINK_OFFSET	0 /* pcs_link_state */
+@@ -798,6 +815,7 @@
+ 	})
+ 
+ #define MACB_READ_NSR(bp)	macb_readl(bp, NSR)
++#define MACB_READ_TSR(bp)	macb_readl(bp, TSR)
+ 
+ /* struct macb_dma_desc - Hardware DMA descriptor
+  * @addr: DMA address of data buffer
+@@ -1217,6 +1235,7 @@ struct macb_queue {
+ 	dma_addr_t		tx_ring_dma;
+ 	struct work_struct	tx_error_task;
+ 	bool			txubr_pending;
++	bool			tx_pending;
+ 	struct napi_struct	napi_tx;
+ 
+ 	dma_addr_t		rx_ring_dma;
+@@ -1286,9 +1305,15 @@ struct macb {
+ 
+ 	u32			caps;
+ 	unsigned int		dma_burst_length;
++	u8			aw2w_max_pipe;
++	u8			ar2r_max_pipe;
++	bool			use_aw2b_fill;
+ 
+ 	phy_interface_t		phy_interface;
+ 
++	struct gpio_desc	*phy_reset_gpio;
++	int			phy_reset_ms;
++
+ 	/* AT91RM9200 transmit queue (1 on wire + 1 queued) */
+ 	struct macb_tx_skb	rm9200_txq[2];
+ 	unsigned int		max_tx_length;
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -41,6 +41,9 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+ #include "macb.h"
+ 
++static unsigned int txdelay = 35;
++module_param(txdelay, uint, 0644);
++
+ /* This structure is only used for MACB on SiFive FU540 devices */
+ struct sifive_fu540_macb_mgmt {
+ 	void __iomem *reg;
+@@ -336,7 +339,7 @@ static int macb_mdio_wait_for_idle(struc
+ 	u32 val;
+ 
+ 	return readx_poll_timeout(MACB_READ_NSR, bp, val, val & MACB_BIT(IDLE),
+-				  1, MACB_MDIO_TIMEOUT);
++				  100, MACB_MDIO_TIMEOUT);
+ }
+ 
+ static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+@@ -442,6 +445,19 @@ mdio_pm_exit:
+ 	return status;
+ }
+ 
++static int macb_mdio_reset(struct mii_bus *bus)
++{
++	struct macb *bp = bus->priv;
++
++	if (bp->phy_reset_gpio) {
++		gpiod_set_value_cansleep(bp->phy_reset_gpio, 1);
++		msleep(bp->phy_reset_ms);
++		gpiod_set_value_cansleep(bp->phy_reset_gpio, 0);
++	}
++
++	return 0;
++}
++
+ static void macb_init_buffers(struct macb *bp)
+ {
+ 	struct macb_queue *queue;
+@@ -915,6 +931,7 @@ static int macb_mii_init(struct macb *bp
+ 	bp->mii_bus->name = "MACB_mii_bus";
+ 	bp->mii_bus->read = &macb_mdio_read;
+ 	bp->mii_bus->write = &macb_mdio_write;
++	bp->mii_bus->reset = &macb_mdio_reset;
+ 	snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ 		 bp->pdev->name, bp->pdev->id);
+ 	bp->mii_bus->priv = bp;
+@@ -1584,6 +1601,11 @@ static int macb_rx(struct macb_queue *qu
+ 
+ 		macb_init_rx_ring(queue);
+ 		queue_writel(queue, RBQP, queue->rx_ring_dma);
++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
++		if (bp->hw_dma_cap & HW_DMA_CAP_64B)
++			macb_writel(bp, RBQPH,
++				    upper_32_bits(queue->rx_ring_dma));
++#endif
+ 
+ 		macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
+ 
+@@ -1884,8 +1906,9 @@ static irqreturn_t macb_interrupt(int ir
+ 				queue_writel(queue, ISR, MACB_BIT(TCOMP) |
+ 							 MACB_BIT(TXUBR));
+ 
+-			if (status & MACB_BIT(TXUBR)) {
++			if (status & MACB_BIT(TXUBR) || queue->tx_pending) {
+ 				queue->txubr_pending = true;
++				queue->tx_pending = 0;
+ 				wmb(); // ensure softirq can see update
+ 			}
+ 
+@@ -2332,6 +2355,11 @@ static netdev_tx_t macb_start_xmit(struc
+ 	skb_tx_timestamp(skb);
+ 
+ 	spin_lock_irq(&bp->lock);
++
++	/* TSTART write might get dropped, so make the IRQ retrigger a buffer read */
++	if (macb_readl(bp, TSR) & MACB_BIT(TGO))
++		queue->tx_pending = 1;
++
+ 	macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
+ 	spin_unlock_irq(&bp->lock);
+ 
+@@ -2699,6 +2727,37 @@ static void macb_configure_dma(struct ma
+ 	}
+ }
+ 
++static void gem_init_axi(struct macb *bp)
++{
++	u32 amp;
++
++	/* AXI pipeline setup - don't touch values unless specified in device
++	 * tree. Some hardware could have reset values > 1.
++	 */
++	amp = gem_readl(bp, AMP);
++
++	if (bp->use_aw2b_fill)
++		amp = GEM_BFINS(AW2B_FILL, bp->use_aw2b_fill, amp);
++	if (bp->aw2w_max_pipe)
++		amp = GEM_BFINS(AW2W_MAX_PIPE, bp->aw2w_max_pipe, amp);
++	if (bp->ar2r_max_pipe)
++		amp = GEM_BFINS(AR2R_MAX_PIPE, bp->ar2r_max_pipe, amp);
++
++	gem_writel(bp, AMP, amp);
++}
++
++static void gem_init_intmod(struct macb *bp)
++{
++	unsigned int throttle;
++	u32 intmod = 0;
++
++	/* Use sensible interrupt moderation thresholds (50us rx and tx) */
++	throttle = (1000 * 50) / 800;
++	intmod = GEM_BFINS(TX_MODERATION, throttle, intmod);
++	intmod = GEM_BFINS(RX_MODERATION, throttle, intmod);
++	gem_writel(bp, INTMOD, intmod);
++}
++
+ static void macb_init_hw(struct macb *bp)
+ {
+ 	u32 config;
+@@ -2727,6 +2786,11 @@ static void macb_init_hw(struct macb *bp
+ 	if (bp->caps & MACB_CAPS_JUMBO)
+ 		bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK;
+ 
++	if (macb_is_gem(bp)) {
++		gem_init_axi(bp);
++		gem_init_intmod(bp);
++	}
++
+ 	macb_configure_dma(bp);
+ }
+ 
+@@ -3072,6 +3136,52 @@ static void gem_get_ethtool_strings(stru
+ 	}
+ }
+ 
++static int gem_set_coalesce(struct net_device *dev,
++			    struct ethtool_coalesce *ec,
++			    struct kernel_ethtool_coalesce *kernel_coal,
++			    struct netlink_ext_ack *extack)
++{
++	struct macb *bp = netdev_priv(dev);
++	unsigned int tx_throttle;
++	unsigned int rx_throttle;
++	u32 intmod = 0;
++
++	/* GEM has simple IRQ throttling support. RX and TX interrupts
++	 * are separately moderated on 800ns quantums, with no support
++	 * for frame coalescing.
++	 */
++
++	/* Max is 255 * 0.8us = 204us. Zero implies no moderation. */
++	if (ec->rx_coalesce_usecs > 204 || ec->tx_coalesce_usecs > 204)
++		return -EINVAL;
++
++	tx_throttle = (1000 * ec->tx_coalesce_usecs) / 800;
++	rx_throttle = (1000 * ec->rx_coalesce_usecs) / 800;
++
++	intmod = GEM_BFINS(TX_MODERATION, tx_throttle, intmod);
++	intmod = GEM_BFINS(RX_MODERATION, rx_throttle, intmod);
++
++	gem_writel(bp, INTMOD, intmod);
++
++	return 0;
++}
++
++static int gem_get_coalesce(struct net_device *dev,
++			    struct ethtool_coalesce *ec,
++			    struct kernel_ethtool_coalesce *kernel_coal,
++			    struct netlink_ext_ack *extack)
++{
++	struct macb *bp = netdev_priv(dev);
++	u32 intmod;
++
++	intmod = gem_readl(bp, INTMOD);
++
++	ec->tx_coalesce_usecs = (GEM_BFEXT(TX_MODERATION, intmod) * 800) / 1000;
++	ec->rx_coalesce_usecs = (GEM_BFEXT(RX_MODERATION, intmod) * 800) / 1000;
++
++	return 0;
++}
++
+ static struct net_device_stats *macb_get_stats(struct net_device *dev)
+ {
+ 	struct macb *bp = netdev_priv(dev);
+@@ -3664,6 +3774,8 @@ static const struct ethtool_ops macb_eth
+ };
+ 
+ static const struct ethtool_ops gem_ethtool_ops = {
++	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
++				     ETHTOOL_COALESCE_TX_USECS,
+ 	.get_regs_len		= macb_get_regs_len,
+ 	.get_regs		= macb_get_regs,
+ 	.get_wol		= macb_get_wol,
+@@ -3673,6 +3785,8 @@ static const struct ethtool_ops gem_etht
+ 	.get_ethtool_stats	= gem_get_ethtool_stats,
+ 	.get_strings		= gem_get_ethtool_strings,
+ 	.get_sset_count		= gem_get_sset_count,
++	.get_coalesce		= gem_get_coalesce,
++	.set_coalesce		= gem_set_coalesce,
+ 	.get_link_ksettings     = macb_get_link_ksettings,
+ 	.set_link_ksettings     = macb_set_link_ksettings,
+ 	.get_ringparam		= macb_get_ringparam,
+@@ -4940,6 +5054,10 @@ static int macb_probe(struct platform_de
+ 
+ 	bp->usrio = macb_config->usrio;
+ 
++	device_property_read_u8(&pdev->dev, "cdns,aw2w-max-pipe", &bp->aw2w_max_pipe);
++	device_property_read_u8(&pdev->dev, "cdns,ar2r-max-pipe", &bp->ar2r_max_pipe);
++	bp->use_aw2b_fill = device_property_read_bool(&pdev->dev, "cdns,use-aw2b-fill");
++
+ 	spin_lock_init(&bp->lock);
+ 
+ 	/* setup capabilities */
+@@ -4995,6 +5113,21 @@ static int macb_probe(struct platform_de
+ 	else
+ 		bp->phy_interface = interface;
+ 
++	/* optional PHY reset-related properties */
++	bp->phy_reset_gpio = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
++						     GPIOD_OUT_LOW);
++	if (IS_ERR(bp->phy_reset_gpio)) {
++		dev_err(&pdev->dev, "Failed to obtain phy-reset gpio\n");
++		err = PTR_ERR(bp->phy_reset_gpio);
++		goto err_out_free_netdev;
++	}
++
++	bp->phy_reset_ms = 10;
++	of_property_read_u32(np, "phy-reset-duration", &bp->phy_reset_ms);
++	/* A sane reset duration should not be longer than 1s */
++	if (bp->phy_reset_ms > 1000)
++		bp->phy_reset_ms = 1000;
++
+ 	/* IP specific init */
+ 	err = init(pdev);
+ 	if (err)
+@@ -5071,6 +5204,19 @@ static int macb_remove(struct platform_d
+ 	return 0;
+ }
+ 
++static void macb_shutdown(struct platform_device *pdev)
++{
++	struct net_device *dev;
++
++	dev = platform_get_drvdata(pdev);
++
++	rtnl_lock();
++	netif_device_detach(dev);
++	if (netif_running(dev))
++		dev_close(dev);
++	rtnl_unlock();
++}
++
+ static int __maybe_unused macb_suspend(struct device *dev)
+ {
+ 	struct net_device *netdev = dev_get_drvdata(dev);
+@@ -5285,6 +5431,7 @@ static const struct dev_pm_ops macb_pm_o
+ static struct platform_driver macb_driver = {
+ 	.probe		= macb_probe,
+ 	.remove		= macb_remove,
++	.shutdown	= macb_shutdown,
+ 	.driver		= {
+ 		.name		= "macb",
+ 		.of_match_table	= of_match_ptr(macb_dt_ids),

+ 384 - 0
target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch

@@ -0,0 +1,384 @@
+From 4ffa5f2c5fc7854683964bb2f2bf23907c18213f Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Mon, 13 Sep 2021 11:14:32 +0100
+Subject: [PATCH] usb: dwc3: Set DMA and coherent masks early
+
+dwc3 allocates scratch and event buffers in the top-level driver. Hack the
+probe function to set the DMA mask before trying to allocate these.
+
+I think the event buffers are only used in device mode, but the scratch
+buffers may be used if core hibernation is enabled.
+
+usb: dwc3: add support for new DT quirks
+
+Apply the optional axi-pipe-limit and dis-in-autoretry-quirk properties
+during driver probe.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+phy: phy-brcm-usb: Add 2712 support
+
+usb: dwc3: if the host controller instance number is present in DT, use it
+
+If two instances of a dwc3 host controller are specified in devicetree,
+then the probe order may be arbitrary which results in the device names
+swapping on a per-boot basis.
+
+If a "usb" alias with the instance number is specified, then use
+that to construct the device name instead of autogenerating one.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+rp1 dwc3 changes
+
+drivers: usb: dwc3: allow setting GTXTHRCFG on dwc_usb3.0 hardware
+
+Equivalent register fields exist in the SuperSpeed Host version of the
+hardware, so allow the use of TX thresholds if specified in devicetree.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+drivers: usb: dwc3: remove downstream quirk dis-in-autoretry
+
+Upstream have unilaterally disabled the feature.
+
+Partially reverts 6e9142a26ee0fdc3a5adc49ed6cedc0b16ec2ed1 (downstream)
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/phy/broadcom/Kconfig                  |  2 +-
+ .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 59 +++++++++++++++++++
+ drivers/phy/broadcom/phy-brcm-usb-init.h      |  2 +
+ drivers/phy/broadcom/phy-brcm-usb.c           | 18 +++++-
+ drivers/usb/dwc3/core.c                       | 52 ++++++++++++++++
+ drivers/usb/dwc3/core.h                       | 10 ++++
+ drivers/usb/dwc3/host.c                       | 17 ++++--
+ 7 files changed, 153 insertions(+), 7 deletions(-)
+
+--- a/drivers/phy/broadcom/Kconfig
++++ b/drivers/phy/broadcom/Kconfig
+@@ -93,7 +93,7 @@ config PHY_BRCM_SATA
+ 
+ config PHY_BRCM_USB
+ 	tristate "Broadcom STB USB PHY driver"
+-	depends on ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST
++	depends on ARCH_BCMBCA || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ 	depends on OF
+ 	select GENERIC_PHY
+ 	select SOC_BRCMSTB if ARCH_BRCMSTB
+--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
++++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
+@@ -318,6 +318,36 @@ static void usb_init_common_7216(struct
+ 	usb_init_common(params);
+ }
+ 
++static void usb_init_common_2712(struct brcm_usb_init_params *params)
++{
++	void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
++	void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC];
++	u32 reg;
++
++	if (params->syscon_piarbctl)
++		syscon_piarbctl_init(params->syscon_piarbctl);
++
++	USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
++
++	usb_wake_enable_7211b0(params, false);
++
++	usb_init_common(params);
++
++	/*
++	 * The BDC controller will get occasional failures with
++	 * the default "Read Transaction Size" of 6 (1024 bytes).
++	 * Set it to 4 (256 bytes).
++	 */
++	if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
++		reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
++		reg &= ~BDC_EC_AXIRDA_RTS_MASK;
++		reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
++		brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA);
++	}
++
++	usb2_eye_fix_7211b0(params);
++}
++
+ static void usb_init_xhci(struct brcm_usb_init_params *params)
+ {
+ 	pr_debug("%s\n", __func__);
+@@ -363,6 +393,18 @@ static void usb_uninit_common_7211b0(str
+ 
+ }
+ 
++static void usb_uninit_common_2712(struct brcm_usb_init_params *params)
++{
++	void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
++
++	if (params->wake_enabled) {
++		USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN);
++		usb_wake_enable_7211b0(params, true);
++	} else {
++		USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
++	}
++}
++
+ static void usb_uninit_xhci(struct brcm_usb_init_params *params)
+ {
+ 
+@@ -417,6 +459,16 @@ static const struct brcm_usb_init_ops bc
+ 	.set_dual_select = usb_set_dual_select,
+ };
+ 
++static const struct brcm_usb_init_ops bcm2712_ops = {
++	.init_ipp = usb_init_ipp,
++	.init_common = usb_init_common_2712,
++	.init_xhci = usb_init_xhci,
++	.uninit_common = usb_uninit_common_2712,
++	.uninit_xhci = usb_uninit_xhci,
++	.get_dual_select = usb_get_dual_select,
++	.set_dual_select = usb_set_dual_select,
++};
++
+ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
+ {
+ 
+@@ -434,3 +486,10 @@ void brcm_usb_dvr_init_7211b0(struct brc
+ 	params->family_name = "7211";
+ 	params->ops = &bcm7211b0_ops;
+ }
++
++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params)
++{
++	params->family_name = "2712";
++	params->ops = &bcm2712_ops;
++	params->suspend_with_clocks = true;
++}
+--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
++++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
+@@ -61,12 +61,14 @@ struct  brcm_usb_init_params {
+ 	const struct brcm_usb_init_ops *ops;
+ 	struct regmap *syscon_piarbctl;
+ 	bool wake_enabled;
++	bool suspend_with_clocks;
+ };
+ 
+ void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params);
+ 
+ static inline u32 brcm_usb_readl(void __iomem *addr)
+ {
+--- a/drivers/phy/broadcom/phy-brcm-usb.c
++++ b/drivers/phy/broadcom/phy-brcm-usb.c
+@@ -76,7 +76,7 @@ struct brcm_usb_phy_data {
+ };
+ 
+ static s8 *node_reg_names[BRCM_REGS_MAX] = {
+-	"crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
++	"ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
+ };
+ 
+ static int brcm_pm_notifier(struct notifier_block *notifier,
+@@ -315,6 +315,18 @@ static const struct match_chip_info chip
+ 	.optional_reg = BRCM_REGS_BDC_EC,
+ };
+ 
++static const struct match_chip_info chip_info_2712 = {
++	.init_func = &brcm_usb_dvr_init_2712,
++	.required_regs = {
++		BRCM_REGS_CTRL,
++		BRCM_REGS_XHCI_EC,
++		BRCM_REGS_XHCI_GBL,
++		BRCM_REGS_USB_MDIO,
++		-1,
++	},
++	.optional_reg = BRCM_REGS_BDC_EC,
++};
++
+ static const struct match_chip_info chip_info_7445 = {
+ 	.init_func = &brcm_usb_dvr_init_7445,
+ 	.required_regs = {
+@@ -338,6 +350,10 @@ static const struct of_device_id brcm_us
+ 		.data = &chip_info_7211b0,
+ 	},
+ 	{
++		.compatible = "brcm,bcm2712-usb-phy",
++		.data = &chip_info_2712,
++	},
++	{
+ 		.compatible = "brcm,brcmstb-usb-phy",
+ 		.data = &chip_info_7445,
+ 	},
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -1216,6 +1216,24 @@ static void dwc3_config_threshold(struct
+ 	}
+ }
+ 
++static void dwc3_set_axi_pipe_limit(struct dwc3 *dwc)
++{
++	struct device *dev = dwc->dev;
++	u32 cfg;
++
++	if (!dwc->axi_pipe_limit)
++		return;
++	if (dwc->axi_pipe_limit > 16) {
++		dev_err(dev, "Invalid axi_pipe_limit property\n");
++		return;
++	}
++	cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
++	cfg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT(15);
++	cfg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(dwc->axi_pipe_limit - 1);
++
++	dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, cfg);
++}
++
+ /**
+  * dwc3_core_init - Low-level initialization of DWC3 Core
+  * @dwc: Pointer to our controller context structure
+@@ -1308,6 +1326,8 @@ static int dwc3_core_init(struct dwc3 *d
+ 
+ 	dwc3_set_incr_burst_type(dwc);
+ 
++	dwc3_set_axi_pipe_limit(dwc);
++
+ 	usb_phy_set_suspend(dwc->usb2_phy, 0);
+ 	usb_phy_set_suspend(dwc->usb3_phy, 0);
+ 	ret = phy_power_on(dwc->usb2_generic_phy);
+@@ -1541,6 +1561,7 @@ static void dwc3_get_properties(struct d
+ 	u8			tx_thr_num_pkt_prd = 0;
+ 	u8			tx_max_burst_prd = 0;
+ 	u8			tx_fifo_resize_max_num;
++	u8			axi_pipe_limit;
+ 	const char		*usb_psy_name;
+ 	int			ret;
+ 
+@@ -1563,6 +1584,9 @@ static void dwc3_get_properties(struct d
+ 	 */
+ 	tx_fifo_resize_max_num = 6;
+ 
++	/* Default to 0 (don't override hardware defaults) */
++	axi_pipe_limit = 0;
++
+ 	dwc->maximum_speed = usb_get_maximum_speed(dev);
+ 	dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
+ 	dwc->dr_mode = usb_get_dr_mode(dev);
+@@ -1678,6 +1702,9 @@ static void dwc3_get_properties(struct d
+ 	dwc->dis_split_quirk = device_property_read_bool(dev,
+ 				"snps,dis-split-quirk");
+ 
++	device_property_read_u8(dev, "snps,axi-pipe-limit",
++				   &axi_pipe_limit);
++
+ 	dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+ 	dwc->tx_de_emphasis = tx_de_emphasis;
+ 
+@@ -1695,6 +1722,8 @@ static void dwc3_get_properties(struct d
+ 	dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ 	dwc->tx_max_burst_prd = tx_max_burst_prd;
+ 
++	dwc->axi_pipe_limit = axi_pipe_limit;
++
+ 	dwc->imod_interval = 0;
+ 
+ 	dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
+@@ -1903,6 +1932,12 @@ static int dwc3_probe(struct platform_de
+ 
+ 	dwc3_get_properties(dwc);
+ 
++	if (!dwc->sysdev_is_parent) {
++		ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
++		if (ret)
++			return ret;
++	}
++
+ 	dwc->reset = devm_reset_control_array_get_optional_shared(dev);
+ 	if (IS_ERR(dwc->reset)) {
+ 		ret = PTR_ERR(dwc->reset);
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -183,6 +183,9 @@
+ #define DWC3_GSBUSCFG0_INCRBRSTENA	(1 << 0) /* undefined length enable */
+ #define DWC3_GSBUSCFG0_INCRBRST_MASK	0xff
+ 
++/* Global SoC Bus Configuration Register 1 */
++#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n)	(((n) & 0xf) << 8)
++
+ /* Global Debug LSP MUX Select */
+ #define DWC3_GDBGLSPMUX_ENDBC		BIT(15)	/* Host only */
+ #define DWC3_GDBGLSPMUX_HOSTSELECT(n)	((n) & 0x3fff)
+@@ -1056,6 +1059,7 @@ struct dwc3_scratchpad_array {
+  * @tx_max_burst_prd: max periodic ESS transmit burst size
+  * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
+  * @clear_stall_protocol: endpoint number that requires a delayed status phase
++ * @axi_max_pipe: set to override the maximum number of pipelined AXI transfers
+  * @hsphy_interface: "utmi" or "ulpi"
+  * @connected: true when we're connected to a host, false otherwise
+  * @softconnect: true when gadget connect is called, false when disconnect runs
+@@ -1287,6 +1291,7 @@ struct dwc3 {
+ 	u8			tx_max_burst_prd;
+ 	u8			tx_fifo_resize_max_num;
+ 	u8			clear_stall_protocol;
++	u8			axi_pipe_limit;
+ 
+ 	const char		*hsphy_interface;
+ 
+--- a/drivers/usb/dwc3/host.c
++++ b/drivers/usb/dwc3/host.c
+@@ -30,10 +30,10 @@ static void dwc3_host_fill_xhci_irq_res(
+ 
+ static int dwc3_host_get_irq(struct dwc3 *dwc)
+ {
+-	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
++	struct platform_device	*pdev = to_platform_device(dwc->dev);
+ 	int irq;
+ 
+-	irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
++	irq = platform_get_irq_byname_optional(pdev, "host");
+ 	if (irq > 0) {
+ 		dwc3_host_fill_xhci_irq_res(dwc, irq, "host");
+ 		goto out;
+@@ -42,7 +42,7 @@ static int dwc3_host_get_irq(struct dwc3
+ 	if (irq == -EPROBE_DEFER)
+ 		goto out;
+ 
+-	irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
++	irq = platform_get_irq_byname_optional(pdev, "dwc_usb3");
+ 	if (irq > 0) {
+ 		dwc3_host_fill_xhci_irq_res(dwc, irq, "dwc_usb3");
+ 		goto out;
+@@ -51,7 +51,7 @@ static int dwc3_host_get_irq(struct dwc3
+ 	if (irq == -EPROBE_DEFER)
+ 		goto out;
+ 
+-	irq = platform_get_irq(dwc3_pdev, 0);
++	irq = platform_get_irq(pdev, 0);
+ 	if (irq > 0) {
+ 		dwc3_host_fill_xhci_irq_res(dwc, irq, NULL);
+ 		goto out;
+@@ -66,16 +66,23 @@ out:
+ 
+ int dwc3_host_init(struct dwc3 *dwc)
+ {
++	struct platform_device	*pdev = to_platform_device(dwc->dev);
+ 	struct property_entry	props[4];
+ 	struct platform_device	*xhci;
+ 	int			ret, irq;
+ 	int			prop_idx = 0;
++	int			id;
+ 
+ 	irq = dwc3_host_get_irq(dwc);
+ 	if (irq < 0)
+ 		return irq;
+ 
+-	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
++	id = of_alias_get_id(pdev->dev.of_node, "usb");
++	if (id >= 0)
++		xhci = platform_device_alloc("xhci-hcd", id);
++	else
++		xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
++
+ 	if (!xhci) {
+ 		dev_err(dwc->dev, "couldn't allocate xHCI device\n");
+ 		return -ENOMEM;

+ 37 - 0
target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch

@@ -0,0 +1,37 @@
+From 480c8e9f48f8a96c457eb3dc0079a73598fb7477 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Wed, 1 Dec 2021 19:43:08 +0000
+Subject: [PATCH] drm/panel/raspberrypi-touchscreen: Insert more delays.
+
+This avoids failures in cases where the panel is enabled
+or re-probed very soon after being disabled or probed.
+These can occur because the Atmel device can mis-behave
+over I2C for a few ms after any write to the POWERON register.
+---
+ drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
++++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+@@ -299,6 +299,13 @@ static int rpi_touchscreen_prepare(struc
+ 	struct rpi_touchscreen *ts = panel_to_ts(panel);
+ 	int i, data;
+ 
++	/*
++	 * Power up the Toshiba bridge. The Atmel device can misbehave
++	 * over I2C for a few ms after writes to REG_POWERON (including the
++	 * write in rpi_touchscreen_disable()), so sleep before and after.
++	 * Also to ensure that the bridge has been off for at least 100ms.
++	 */
++	msleep(100);
+ 	rpi_touchscreen_i2c_write(ts, REG_POWERON, 1);
+ 	usleep_range(20000, 25000);
+ 	/* Wait for nPWRDWN to go low to indicate poweron is done. */
+@@ -431,6 +438,7 @@ static int rpi_touchscreen_probe(struct
+ 
+ 	/* Turn off at boot, so we can cleanly sequence powering on. */
+ 	rpi_touchscreen_i2c_write(ts, REG_POWERON, 0);
++	usleep_range(20000, 25000);
+ 
+ 	/* Look up the DSI host.  It needs to probe before we do. */
+ 	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);

+ 1108 - 0
target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch

@@ -0,0 +1,1108 @@
+From 29702857d1ab71243ea6c247dfe9b5bc43dd422f Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <[email protected]>
+Date: Fri, 23 Jun 2023 10:40:57 -0400
+Subject: [PATCH] PCI: brcmstb: Add BCM2712 support
+
+PCI: brcmstb: differing register offsets on 2712
+
+pcie-brcmstb: Add 2712 bridge reset support
+
+pcie: 2712 PORT_MASK and rescal support
+
+pcie-brcmstb: don't alter the L1SS debug register
+
+For reasons unknown, this disables the reference clock
+
+pcie-brcmstb: fix BAR2 enable and window decode
+
+Set UBUS ACCESS_EN to let inbound DMA work. Also BCM2712 has grown
+an index in the inbound window size decode register.
+
+PCIe: brcmstb: Enable support for 64 MSI-Xs
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pcie-brcmstb: Suppress read error responses
+
+If the link is down or the EP fails to return a read completion, the
+RC's default behaviour is to return an AXI error. This causes fatal
+exceptions on A76, so it's better to respond with all 1s instead.
+
+pcie-brcmstb: increase UBUS timeout to cater for link retrain events
+
+pcie-brcmstb: Handle additional inbound regions
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+pcie-brcmstb: Add support for external MSI controller
+
+pcie-brcmstb: add a reasonable default traffic class to priority map
+
+BCM2712 supports multiple traffic classes (TCs) with independent
+maximally sized transfer queues for each TC. Traffic classes have no
+transaction ordering requirements between them, which facilitates
+out-of-order completions and arbitration between posted writes for
+data streams that have no dependence on each other.
+
+In addition to the above benefits of splitting endpoint traffic into
+individual queues, priorities can be assigned to traffic classes by
+a heuristic or deterministic mechanism. The heuristic elevates AXI
+QOS priority in accordance with the number of pending transfers in
+each TC's queue, but for true priority signalling a forwarding
+mechanism using vendor-defined messages is implemented.
+
+Receipt of a 3 DWORD VDM assigns a priority tag to a TC on-the-fly,
+and this tag corresponds to a configurable AXI QOS value.
+
+As a simple baseline, assign a linear map of AXI QOS to each tag.
+
+pcie: brcmstb: set up the VDM forwarding interface when setting up QoS
+
+pcie-brcmstb: add DT bindings for MPS-size Read Completion Mode
+
+This controller has an optional feature that allows read completion
+TLPs to be sized up to the Maximum Packet Size of a configured link.
+
+This can exceed the Read Completion Boundary of 128B specified in
+the PCIe specification, but depending on endpoint support may increase
+link read bandwidth significantly.
+
+pcie-brcmstb: clean up debug messages
+
+pcie-brcmstb: fix BCM2712A0 PHY PM errata
+
+The power management clock is 54MHz not 50MHz, so adjust the PM clock period
+to suit. Powering off the PHY PLL in L1.2 is unsafe, so force it on.
+
+pcie-brcmstb: set CLKREQ functionality according to link partner support
+
+The RC supports either L1 with clock PM or L1 sub-state control, not both
+at the same time. Examine the link partner's capabilities to determine
+which is the most suitable scheme to use.
+
+pcie: brcmstb: don't reset block bridges in suspend or removal cases
+
+BCM2712 has a single rescal block for all three root complexes, and
+holding PCIE1's bridge in reset will hang the chip if a different
+RC wants to access any of the rescal registers.
+
+pcie: brcmstb: guard 2712-specific setup with a RC type check
+
+BCM2711 doesn't implement the UBUS control registers.
+
+pcie: brcmstb: On 2712 keeping the PLL powered in L1.x is not required
+
+A separate misconfiguration when enabling SSC (the MDIO registers no
+longer do the same thing on BCM2712) had the side-effect of breaking
+PLL powerdown and resume sequencing.
+
+Allow entry into a true L1.2 state where analogue is depowered.
+
+pcie: brcmstb: Fix reset warning on probe failure
+
+Signed-off-by: Phil Elwell <[email protected]>
+
+bcm2712: pcie: adjust PHY PLL setup to use a 54MHz input refclk
+
+Use canned MDIO writes from Broadcom that switch the ref_clk output
+pair to run from the internal fractional PLL, and set the internal PLL
+to expect a 54MHz input reference clock.
+
+Gen3 operation is not guaranteed to be stable in this setup, so default
+to gen2.
+
+This only works if the LCPLL is bypassed (requires latest bootloader).
+
+pcie: brcmstb: add missing register writes
+
+drivers: pcie: brcmstb: cater for BCM2712C0 bug dropping QoS on the floor
+
+The AXI QoS value extracted from the request fifo ends up as zero forever.
+Disabling this means that "panic" signalling doesn't do anything useful,
+but static priorites do work.
+
+Also align the selected TC:QoS map with RP1's expectations of service.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+drivers: pcie: brcmstb: shuffle TC priorities up to 8
+
+Use the range 8-11 which puts the highest below HVS but leaves space
+below for other 2712 masters.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+drivers: pcie: brcmstb: optionally enable QoS features by DT for BCM2712
+
+It's a bad idea to universally enable "realtime" priorities for TCs
+across all the RC instances on the chip. Endpoints other than RP1 may
+make use of these, so you don't want e.g. NVMe descriptor fetches getting
+higher priority than your remote display.
+
+Add two optional DT properties controlling the behaviour - FIFO-based
+backpressure QoS or "message-based". Message-based signalling is
+fundamentally broken due to a chip bug, so it collapses into a set of
+static assignments that RP1 needs.
+
+The default if neither property is specified is to assign everything a
+QoS of 0.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+
+drivers: pcie: brcmstb: adjust completion timeouts for bcm2712
+
+Setting the RC config retry timeout makes CRS auto-polling work, but
+the UBUS timeout will override the config retry. Both need to be large.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 507 +++++++++++++++++++++++---
+ 1 file changed, 458 insertions(+), 49 deletions(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -13,6 +13,7 @@
+ #include <linux/irqchip/chained_irq.h>
+ #include <linux/irqdomain.h>
+ #include <linux/kernel.h>
++#include <linux/kthread.h>
+ #include <linux/list.h>
+ #include <linux/log2.h>
+ #include <linux/module.h>
+@@ -47,11 +48,25 @@
+ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY			0x04dc
+ #define  PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK	0xc00
+ 
++#define PCIE_RC_TL_VDM_CTL0				0x0a20
++#define  PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK		0x10000
++#define  PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK		0x20000
++#define  PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK	0x40000
++
++#define PCIE_RC_TL_VDM_CTL1				0x0a0c
++#define  PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK		0x0000ffff
++#define  PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK		0xffff0000
++
+ #define PCIE_RC_DL_MDIO_ADDR				0x1100
+ #define PCIE_RC_DL_MDIO_WR_DATA				0x1104
+ #define PCIE_RC_DL_MDIO_RD_DATA				0x1108
+ 
++#define PCIE_RC_PL_PHY_CTL_15				0x184c
++#define  PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK		0x400000
++#define  PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK	0xff
++
+ #define PCIE_MISC_MISC_CTRL				0x4008
++#define  PCIE_MISC_MISC_CTRL_RCB_MPS_MODE_MASK		0x400
+ #define  PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK		0x1000
+ #define  PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK	0x2000
+ #define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK	0x300000
+@@ -71,6 +86,7 @@
+ 
+ #define PCIE_MISC_RC_BAR1_CONFIG_LO			0x402c
+ #define  PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK		0x1f
++#define PCIE_MISC_RC_BAR1_CONFIG_HI			0x4030
+ 
+ #define PCIE_MISC_RC_BAR2_CONFIG_LO			0x4034
+ #define  PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK		0x1f
+@@ -78,6 +94,7 @@
+ 
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO			0x403c
+ #define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK		0x1f
++#define PCIE_MISC_RC_BAR3_CONFIG_HI			0x4040
+ 
+ #define PCIE_MISC_MSI_BAR_CONFIG_LO			0x4044
+ #define PCIE_MISC_MSI_BAR_CONFIG_HI			0x4048
+@@ -86,12 +103,15 @@
+ #define  PCIE_MISC_MSI_DATA_CONFIG_VAL_32		0xffe06540
+ #define  PCIE_MISC_MSI_DATA_CONFIG_VAL_8		0xfff86540
+ 
++#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT		0x405c
++
+ #define PCIE_MISC_PCIE_CTRL				0x4064
+ #define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK	0x1
+ #define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK		0x4
+ 
+ #define PCIE_MISC_PCIE_STATUS				0x4068
+ #define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK		0x80
++#define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712	0x40
+ #define  PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK	0x20
+ #define  PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK	0x10
+ #define  PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK	0x40
+@@ -116,14 +136,73 @@
+ #define PCIE_MEM_WIN0_LIMIT_HI(win)	\
+ 		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
+ 
+-#define PCIE_MISC_HARD_PCIE_HARD_DEBUG					0x4204
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG	pcie->reg_offsets[PCIE_HARD_DEBUG]
+ #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	0x2
+ #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x08000000
+ #define  PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x00800000
+ #define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK		0x00200000
+ 
++#define PCIE_MISC_CTRL_1					0x40A0
++#define  PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK			0xf
++#define  PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK		BIT(3)
++#define  PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK			BIT(4)
++#define  PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK		BIT(5)
++
++#define PCIE_MISC_UBUS_CTRL	0x40a4
++#define  PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK	BIT(13)
++#define  PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK	BIT(19)
++
++#define PCIE_MISC_UBUS_TIMEOUT	0x40A8
++
++#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP	0x40ac
++#define  PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK	BIT(0)
++#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI	0x40b0
++
++#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP	0x40b4
++#define  PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK	BIT(0)
++
++/* Additional RC BARs */
++#define  PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK		0x1f
++#define PCIE_MISC_RC_BAR4_CONFIG_LO			0x40d4
++#define PCIE_MISC_RC_BAR4_CONFIG_HI			0x40d8
++/* ... */
++#define PCIE_MISC_RC_BAR10_CONFIG_LO			0x4104
++#define PCIE_MISC_RC_BAR10_CONFIG_HI			0x4108
++
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE		0x1
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK		0xfffff000
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK		0xff
++#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO		0x410c
++#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI		0x4110
++/* ... */
++#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_LO		0x413c
++#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_HI		0x4140
++
++/* AXI priority forwarding - automatic level-based */
++#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x)		(0x4160 - (x) * 4)
++/* Defined in quarter-fullness */
++#define  QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT		12
++#define  QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT		8
++#define  QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT		4
++#define  QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT		0
++#define  QUEUE_THRESHOLD_MASK				0xf
++
++/* VDM messages indexing TCs to AXI priorities */
++/* Indexes 8-15 */
++#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI		0x4164
++/* Indexes 0-7 */
++#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO		0x4168
++#define  VDM_PRIORITY_TO_QOS_MAP_SHIFT(x)		(4 * (x))
++#define  VDM_PRIORITY_TO_QOS_MAP_MASK			0xf
++
++#define PCIE_MISC_AXI_INTF_CTRL 0x416C
++#define  AXI_REQFIFO_EN_QOS_PROPAGATION			BIT(7)
++#define  AXI_BRIDGE_LOW_LATENCY_MODE			BIT(6)
++#define  AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK	0x3f
+ 
+-#define PCIE_INTR2_CPU_BASE		0x4300
++#define PCIE_MISC_AXI_READ_ERROR_DATA	0x4170
++
++#define PCIE_INTR2_CPU_BASE		(pcie->reg_offsets[INTR2_CPU])
+ #define PCIE_MSI_INTR2_BASE		0x4500
+ /* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */
+ #define  MSI_INT_STATUS			0x0
+@@ -197,6 +276,8 @@ enum {
+ 	RGR1_SW_INIT_1,
+ 	EXT_CFG_INDEX,
+ 	EXT_CFG_DATA,
++	PCIE_HARD_DEBUG,
++	INTR2_CPU,
+ };
+ 
+ enum {
+@@ -211,6 +292,7 @@ enum pcie_type {
+ 	BCM4908,
+ 	BCM7278,
+ 	BCM2711,
++	BCM2712,
+ };
+ 
+ struct pcie_cfg_data {
+@@ -218,6 +300,7 @@ struct pcie_cfg_data {
+ 	const enum pcie_type type;
+ 	void (*perst_set)(struct brcm_pcie *pcie, u32 val);
+ 	void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
++	bool (*rc_mode)(struct brcm_pcie *pcie);
+ };
+ 
+ struct subdev_regulators {
+@@ -234,7 +317,7 @@ struct brcm_msi {
+ 	struct mutex		lock; /* guards the alloc/free operations */
+ 	u64			target_addr;
+ 	int			irq;
+-	DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);
++	DECLARE_BITMAP(used, 64);
+ 	bool			legacy;
+ 	/* Some chips have MSIs in bits [31..24] of a shared register. */
+ 	int			legacy_shift;
+@@ -251,6 +334,7 @@ struct brcm_pcie {
+ 	struct device_node	*np;
+ 	bool			ssc;
+ 	bool			l1ss;
++	bool			rcb_mps_mode;
+ 	int			gen;
+ 	u64			msi_target_addr;
+ 	struct brcm_msi		*msi;
+@@ -258,11 +342,14 @@ struct brcm_pcie {
+ 	enum pcie_type		type;
+ 	struct reset_control	*rescal;
+ 	struct reset_control	*perst_reset;
++	struct reset_control	*bridge_reset;
+ 	int			num_memc;
+ 	u64			memc_size[PCIE_BRCM_MAX_MEMC];
+ 	u32			hw_rev;
++	u32			qos_map;
+ 	void			(*perst_set)(struct brcm_pcie *pcie, u32 val);
+ 	void			(*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
++	bool			(*rc_mode)(struct brcm_pcie *pcie);
+ 	struct subdev_regulators *sr;
+ 	bool			ep_wakeup_capable;
+ };
+@@ -283,8 +370,8 @@ static int brcm_pcie_encode_ibar_size(u6
+ 	if (log2_in >= 12 && log2_in <= 15)
+ 		/* Covers 4KB to 32KB (inclusive) */
+ 		return (log2_in - 12) + 0x1c;
+-	else if (log2_in >= 16 && log2_in <= 35)
+-		/* Covers 64KB to 32GB, (inclusive) */
++	else if (log2_in >= 16 && log2_in <= 36)
++		/* Covers 64KB to 64GB, (inclusive) */
+ 		return log2_in - 15;
+ 	/* Something is awry so disable */
+ 	return 0;
+@@ -381,6 +468,35 @@ static int brcm_pcie_set_ssc(struct brcm
+ 	return ssc && pll ? 0 : -EIO;
+ }
+ 
++static void brcm_pcie_munge_pll(struct brcm_pcie *pcie)
++{
++	//print "MDIO block 0x1600 written per Dannys instruction"
++	//tmp = pcie_mdio_write(phyad, &h16&, &h50b9&)
++	//tmp = pcie_mdio_write(phyad, &h17&, &hbd1a&)
++	//tmp = pcie_mdio_write(phyad, &h1b&, &h5030&)
++	//tmp = pcie_mdio_write(phyad, &h1e&, &h0007&)
++
++	u32 tmp;
++	int ret, i;
++	u8 regs[] =  { 0x16,   0x17,   0x18,   0x19,   0x1b,   0x1c,   0x1e };
++	u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
++
++	ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
++				0x1600);
++	for (i = 0; i < ARRAY_SIZE(regs); i++) {
++		brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
++		dev_dbg(pcie->dev, "PCIE MDIO pre_refclk 0x%02x = 0x%04x\n",
++			regs[i], tmp);
++	}
++	for (i = 0; i < ARRAY_SIZE(regs); i++) {
++		brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
++		brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
++		dev_dbg(pcie->dev, "PCIE MDIO post_refclk 0x%02x = 0x%04x\n",
++			regs[i], tmp);
++	}
++	usleep_range(100, 200);
++}
++
+ /* Limits operation to a specific generation (1, 2, or 3) */
+ static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
+ {
+@@ -438,6 +554,97 @@ static void brcm_pcie_set_outbound_win(s
+ 	writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
+ }
+ 
++static void brcm_pcie_set_tc_qos(struct brcm_pcie *pcie)
++{
++	int i;
++	u32 reg;
++
++	if (pcie->type != BCM2712)
++		return;
++
++	/* XXX: BCM2712C0 is broken, disable the forwarding search */
++	reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
++	reg &= ~AXI_REQFIFO_EN_QOS_PROPAGATION;
++	writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
++
++	/* Disable VDM reception by default - QoS map defaults to 0 */
++	reg = readl(pcie->base + PCIE_MISC_CTRL_1);
++	reg &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
++	writel(reg, pcie->base + PCIE_MISC_CTRL_1);
++
++	if (!of_property_read_u32(pcie->np, "brcm,fifo-qos-map", &pcie->qos_map)) {
++		/*
++		 * Backpressure mode - bottom 4 nibbles are QoS for each
++		 * quartile of FIFO level. Each TC gets the same map, because
++		 * this mode is intended for nonrealtime EPs.
++		 */
++
++		pcie->qos_map &= 0x0000ffff;
++		for (i = 0; i < 8; i++)
++			writel(pcie->qos_map, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i));
++
++		return;
++	}
++
++	if (!of_property_read_u32(pcie->np, "brcm,vdm-qos-map", &pcie->qos_map)) {
++
++		reg = readl(pcie->base + PCIE_MISC_CTRL_1);
++		reg |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
++		writel(reg, pcie->base + PCIE_MISC_CTRL_1);
++
++		/* No forwarding means no point separating panic priorities from normal */
++		writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO);
++		writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI);
++
++		/* Match Vendor ID of 0 */
++		writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1);
++		/* Forward VDMs to priority interface - at least the rx counters work */
++		reg = readl(pcie->base + PCIE_RC_TL_VDM_CTL0);
++		reg |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK |
++			PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK |
++			PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK;
++		writel(reg, pcie->base + PCIE_RC_TL_VDM_CTL0);
++	}
++}
++
++static void brcm_pcie_config_clkreq(struct brcm_pcie *pcie)
++{
++	void __iomem *base = pcie->base;
++	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++	int domain = pci_domain_nr(bridge->bus);
++	const struct pci_bus *bus = pci_find_bus(domain, 1);
++	struct pci_dev *pdev = (struct pci_dev *)bus->devices.next;
++	u32 tmp, link_cap = 0;
++	u16 link_ctl = 0;
++	int clkpm = 0;
++	int substates = 0;
++
++	pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
++	if ((link_cap & PCI_EXP_LNKCAP_CLKPM))
++		clkpm = 1;
++
++	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctl);
++	if (!(link_ctl & PCI_EXP_LNKCTL_CLKREQ_EN))
++		clkpm = 0;
++
++	if (pcie->l1ss && pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS))
++		substates = 1;
++
++	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++	tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++	tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
++
++	if (substates)
++		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
++	else if (clkpm)
++		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++
++	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++	if (substates || clkpm)
++		dev_info(pcie->dev, "clkreq control enabled\n");
++}
++
+ static struct irq_chip brcm_msi_irq_chip = {
+ 	.name            = "BRCM STB PCIe MSI",
+ 	.irq_ack         = irq_chip_ack_parent,
+@@ -455,7 +662,7 @@ static struct msi_domain_info brcm_msi_d
+ static void brcm_pcie_msi_isr(struct irq_desc *desc)
+ {
+ 	struct irq_chip *chip = irq_desc_get_chip(desc);
+-	unsigned long status;
++	unsigned long status, virq;
+ 	struct brcm_msi *msi;
+ 	struct device *dev;
+ 	u32 bit;
+@@ -467,10 +674,22 @@ static void brcm_pcie_msi_isr(struct irq
+ 	status = readl(msi->intr_base + MSI_INT_STATUS);
+ 	status >>= msi->legacy_shift;
+ 
+-	for_each_set_bit(bit, &status, msi->nr) {
+-		int ret;
+-		ret = generic_handle_domain_irq(msi->inner_domain, bit);
+-		if (ret)
++	for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR/*msi->nr*/) {
++		bool found = false;
++
++		virq = irq_find_mapping(msi->inner_domain, bit);
++		if (virq) {
++			found = true;
++			dev_dbg(dev, "MSI -> %ld\n", virq);
++			generic_handle_irq(virq);
++		}
++		virq = irq_find_mapping(msi->inner_domain, bit + 32);
++		if (virq) {
++			found = true;
++			dev_dbg(dev, "MSI -> %ld\n", virq);
++			generic_handle_irq(virq);
++		}
++		if (!found)
+ 			dev_dbg(dev, "unexpected MSI\n");
+ 	}
+ 
+@@ -483,7 +702,7 @@ static void brcm_msi_compose_msi_msg(str
+ 
+ 	msg->address_lo = lower_32_bits(msi->target_addr);
+ 	msg->address_hi = upper_32_bits(msi->target_addr);
+-	msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq;
++	msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | (data->hwirq & 0x1f);
+ }
+ 
+ static int brcm_msi_set_affinity(struct irq_data *irq_data,
+@@ -495,7 +714,7 @@ static int brcm_msi_set_affinity(struct
+ static void brcm_msi_ack_irq(struct irq_data *data)
+ {
+ 	struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
+-	const int shift_amt = data->hwirq + msi->legacy_shift;
++	const int shift_amt = (data->hwirq & 0x1f) + msi->legacy_shift;
+ 
+ 	writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR);
+ }
+@@ -653,7 +872,7 @@ static int brcm_pcie_enable_msi(struct b
+ 		msi->legacy_shift = 24;
+ 	} else {
+ 		msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
+-		msi->nr = BRCM_INT_PCI_MSI_NR;
++		msi->nr = 64; //BRCM_INT_PCI_MSI_NR;
+ 		msi->legacy_shift = 0;
+ 	}
+ 
+@@ -670,7 +889,7 @@ static int brcm_pcie_enable_msi(struct b
+ }
+ 
+ /* The controller is capable of serving in both RC and EP roles */
+-static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
++static bool brcm_pcie_rc_mode_generic(struct brcm_pcie *pcie)
+ {
+ 	void __iomem *base = pcie->base;
+ 	u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
+@@ -678,6 +897,14 @@ static bool brcm_pcie_rc_mode(struct brc
+ 	return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
+ }
+ 
++static bool brcm_pcie_rc_mode_2712(struct brcm_pcie *pcie)
++{
++	void __iomem *base = pcie->base;
++	u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
++
++	return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712, val) | 1; //XXX
++}
++
+ static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
+ {
+ 	u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
+@@ -749,6 +976,18 @@ static inline void brcm_pcie_bridge_sw_i
+ 	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
+ }
+ 
++static inline void brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val)
++{
++	if (WARN_ONCE(!pcie->bridge_reset,
++		      "missing bridge reset controller\n"))
++		return;
++
++	if (val)
++		reset_control_assert(pcie->bridge_reset);
++	else
++		reset_control_deassert(pcie->bridge_reset);
++}
++
+ static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
+ {
+ 	if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
+@@ -770,6 +1009,16 @@ static inline void brcm_pcie_perst_set_7
+ 	writel(tmp, pcie->base +  PCIE_MISC_PCIE_CTRL);
+ }
+ 
++static inline void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val)
++{
++	u32 tmp;
++
++	/* Perst bit has moved and assert value is 0 */
++	tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
++	u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK);
++	writel(tmp, pcie->base +  PCIE_MISC_PCIE_CTRL);
++}
++
+ static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
+ {
+ 	u32 tmp;
+@@ -796,6 +1045,8 @@ static inline int brcm_pcie_get_rc_bar2_
+ 		size += entry->res->end - entry->res->start + 1;
+ 		if (pcie_beg < lowest_pcie_addr)
+ 			lowest_pcie_addr = pcie_beg;
++		if (pcie->type == BCM2711 || pcie->type == BCM2712)
++			break; // Only consider the first entry
+ 	}
+ 
+ 	if (lowest_pcie_addr == ~(u64)0) {
+@@ -866,6 +1117,30 @@ static inline int brcm_pcie_get_rc_bar2_
+ 	return 0;
+ }
+ 
++static int brcm_pcie_get_rc_bar_n(struct brcm_pcie *pcie,
++				  int idx,
++				  u64 *rc_bar_cpu,
++				  u64 *rc_bar_size,
++				  u64 *rc_bar_pci)
++{
++	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++	struct resource_entry *entry;
++	int i = 0;
++
++	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
++		if (i == idx) {
++			*rc_bar_cpu  = entry->res->start;
++			*rc_bar_size = entry->res->end - entry->res->start + 1;
++			*rc_bar_pci = entry->res->start - entry->offset;
++			return 0;
++		}
++
++		i++;
++	}
++
++	return -EINVAL;
++}
++
+ static int brcm_pcie_setup(struct brcm_pcie *pcie)
+ {
+ 	u64 rc_bar2_offset, rc_bar2_size;
+@@ -874,11 +1149,14 @@ static int brcm_pcie_setup(struct brcm_p
+ 	struct resource_entry *entry;
+ 	u32 tmp, burst, aspm_support;
+ 	int num_out_wins = 0;
+-	int ret, memc;
++	int ret, memc, count, i;
+ 
+ 	/* Reset the bridge */
+ 	pcie->bridge_sw_init_set(pcie, 1);
+-	pcie->perst_set(pcie, 1);
++
++	/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
++	if (pcie->type == BCM2711)
++		pcie->perst_set(pcie, 1);
+ 
+ 	usleep_range(100, 200);
+ 
+@@ -894,6 +1172,17 @@ static int brcm_pcie_setup(struct brcm_p
+ 	/* Wait for SerDes to be stable */
+ 	usleep_range(100, 200);
+ 
++	if (pcie->type == BCM2712) {
++		/* Allow a 54MHz (xosc) refclk source */
++		brcm_pcie_munge_pll(pcie);
++		/* Fix for L1SS errata */
++		tmp = readl(base + PCIE_RC_PL_PHY_CTL_15);
++		tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
++		/* PM clock period is 18.52ns (round down) */
++		tmp |= 0x12;
++		writel(tmp, base + PCIE_RC_PL_PHY_CTL_15);
++	}
++
+ 	/*
+ 	 * SCB_MAX_BURST_SIZE is a two bit field.  For GENERIC chips it
+ 	 * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
+@@ -903,18 +1192,25 @@ static int brcm_pcie_setup(struct brcm_p
+ 		burst = 0x1; /* 256 bytes */
+ 	else if (pcie->type == BCM2711)
+ 		burst = 0x0; /* 128 bytes */
++	else if (pcie->type == BCM2712)
++		burst = 0x1; /* 128 bytes */
+ 	else if (pcie->type == BCM7278)
+ 		burst = 0x3; /* 512 bytes */
+ 	else
+ 		burst = 0x2; /* 512 bytes */
+ 
+-	/* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
++	/* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN, RCB_MPS_MODE */
+ 	tmp = readl(base + PCIE_MISC_MISC_CTRL);
+ 	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
+ 	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
+ 	u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
++	if (pcie->rcb_mps_mode)
++		u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_RCB_MPS_MODE_MASK);
++	dev_info(pcie->dev, "setting SCB_ACCESS_EN, READ_UR_MODE, MAX_BURST_SIZE\n");
+ 	writel(tmp, base + PCIE_MISC_MISC_CTRL);
+ 
++	brcm_pcie_set_tc_qos(pcie);
++
+ 	ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
+ 						    &rc_bar2_offset);
+ 	if (ret)
+@@ -927,7 +1223,11 @@ static int brcm_pcie_setup(struct brcm_p
+ 	writel(upper_32_bits(rc_bar2_offset),
+ 	       base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+ 
++	tmp = readl(base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
++	u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK);
++	writel(tmp, base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
+ 	tmp = readl(base + PCIE_MISC_MISC_CTRL);
++
+ 	for (memc = 0; memc < pcie->num_memc; memc++) {
+ 		u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15;
+ 
+@@ -938,8 +1238,32 @@ static int brcm_pcie_setup(struct brcm_p
+ 		else if (memc == 2)
+ 			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
+ 	}
++
+ 	writel(tmp, base + PCIE_MISC_MISC_CTRL);
+ 
++	if (pcie->type == BCM2712) {
++		/* Suppress AXI error responses and return 1s for read failures */
++		tmp = readl(base + PCIE_MISC_UBUS_CTRL);
++		u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
++		u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
++		writel(tmp, base + PCIE_MISC_UBUS_CTRL);
++		writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA);
++
++		/*
++		 * Adjust timeouts. The UBUS timeout also affects CRS
++		 * completion retries, as the request will get terminated if
++		 * either timeout expires, so both have to be a large value
++		 * (in clocks of 750MHz).
++		 * Set UBUS timeout to 250ms, then set RC config retry timeout
++		 * to be ~240ms.
++		 *
++		 * Setting CRSVis=1 will stop the core from blocking on a CRS
++		 * response, but does require the device to be well-behaved...
++		 */
++		writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT);
++		writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
++	}
++
+ 	/*
+ 	 * We ideally want the MSI target address to be located in the 32bit
+ 	 * addressable memory area. Some devices might depend on it. This is
+@@ -952,7 +1276,7 @@ static int brcm_pcie_setup(struct brcm_p
+ 	else
+ 		pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
+ 
+-	if (!brcm_pcie_rc_mode(pcie)) {
++	if (!pcie->rc_mode(pcie)) {
+ 		dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n");
+ 		return -EINVAL;
+ 	}
+@@ -976,6 +1300,38 @@ static int brcm_pcie_setup(struct brcm_p
+ 		PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
+ 	writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
+ 
++	/* program additional inbound windows (RC_BAR4..RC_BAR10) */
++	count = (pcie->type == BCM2712) ? 7 : 0;
++	for (i = 0; i < count; i++) {
++		u64 bar_cpu, bar_size, bar_pci;
++
++		ret = brcm_pcie_get_rc_bar_n(pcie, 1 + i, &bar_cpu, &bar_size,
++					     &bar_pci);
++		if (ret)
++			break;
++
++		tmp = lower_32_bits(bar_pci);
++		u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size),
++				  PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK);
++		writel(tmp, base + PCIE_MISC_RC_BAR4_CONFIG_LO + i * 8);
++		writel(upper_32_bits(bar_pci),
++		       base + PCIE_MISC_RC_BAR4_CONFIG_HI + i * 8);
++
++		tmp = upper_32_bits(bar_cpu) &
++			PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK;
++		writel(tmp,
++		       base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI + i * 8);
++		tmp = lower_32_bits(bar_cpu) &
++			PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK;
++		writel(tmp | PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE,
++		       base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + i * 8);
++	}
++
++	if (pcie->gen) {
++		dev_info(pcie->dev, "Forcing gen %d\n", pcie->gen);
++		brcm_pcie_set_gen(pcie, pcie->gen);
++	}
++
+ 	/*
+ 	 * For config space accesses on the RC, show the right class for
+ 	 * a PCIe-PCIe bridge (the default setting is to be EP mode).
+@@ -1031,7 +1387,6 @@ static int brcm_pcie_start_link(struct b
+ 	void __iomem *base = pcie->base;
+ 	u16 nlw, cls, lnksta;
+ 	bool ssc_good = false;
+-	u32 tmp;
+ 	int ret, i;
+ 
+ 	/* Unassert the fundamental reset */
+@@ -1067,6 +1422,7 @@ static int brcm_pcie_start_link(struct b
+ 			dev_err(dev, "failed attempt to enter ssc mode\n");
+ 	}
+ 
++
+ 	lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
+ 	cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
+ 	nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
+@@ -1074,27 +1430,6 @@ static int brcm_pcie_start_link(struct b
+ 		 pci_speed_string(pcie_link_speed[cls]), nlw,
+ 		 ssc_good ? "(SSC)" : "(!SSC)");
+ 
+-	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+-	if (pcie->l1ss) {
+-		/*
+-		 * Enable CLKREQ# signalling include L1 Substate control of
+-		 * the CLKREQ# signal and the external reference clock buffer.
+-		 * meet requirement for Endpoints that require CLKREQ#
+-		 * assertion to clock active within 400ns.
+-		 */
+-		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+-		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+-	} else {
+-		/*
+-		 * Refclk from RC should be gated with CLKREQ# input when
+-		 * ASPM L0s,L1 is enabled => setting the CLKREQ_DEBUG_ENABLE
+-		 * field to 1.
+-		 */
+-		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+-		tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+-	}
+-	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+-
+ 	return 0;
+ }
+ 
+@@ -1202,6 +1537,7 @@ static void brcm_pcie_enter_l23(struct b
+ 
+ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
+ {
++#if 0
+ 	static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
+ 		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT,
+ 		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT,
+@@ -1234,6 +1570,9 @@ static int brcm_phy_cntl(struct brcm_pci
+ 		dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop"));
+ 
+ 	return ret;
++#else
++	return 0;
++#endif
+ }
+ 
+ static inline int brcm_phy_start(struct brcm_pcie *pcie)
+@@ -1266,6 +1605,12 @@ static void brcm_pcie_turn_off(struct br
+ 	u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
+ 	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+ 
++	/*
++	 * Shutting down this bridge on pcie1 means accesses to rescal block
++	 * will hang the chip if another RC wants to assert/deassert rescal.
++	 */
++	if (pcie->type == BCM2712)
++		return;
+ 	/* Shutdown PCIe bridge */
+ 	pcie->bridge_sw_init_set(pcie, 1);
+ }
+@@ -1296,9 +1641,9 @@ static int brcm_pcie_suspend_noirq(struc
+ 	if (brcm_phy_stop(pcie))
+ 		dev_err(dev, "Could not stop phy for suspend\n");
+ 
+-	ret = reset_control_rearm(pcie->rescal);
++	ret = reset_control_assert(pcie->rescal);
+ 	if (ret) {
+-		dev_err(dev, "Could not rearm rescal reset\n");
++		dev_err(dev, "Could not assert rescal reset\n");
+ 		return ret;
+ 	}
+ 
+@@ -1393,7 +1738,7 @@ err_regulator:
+ 	if (pcie->sr)
+ 		regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
+ err_reset:
+-	reset_control_rearm(pcie->rescal);
++	reset_control_assert(pcie->rescal);
+ err_disable_clk:
+ 	clk_disable_unprepare(pcie->clk);
+ 	return ret;
+@@ -1405,8 +1750,8 @@ static void __brcm_pcie_remove(struct br
+ 	brcm_pcie_turn_off(pcie);
+ 	if (brcm_phy_stop(pcie))
+ 		dev_err(pcie->dev, "Could not stop phy\n");
+-	if (reset_control_rearm(pcie->rescal))
+-		dev_err(pcie->dev, "Could not rearm rescal reset\n");
++	if (reset_control_assert(pcie->rescal))
++		dev_err(pcie->dev, "Could not assert rescal reset\n");
+ 	clk_disable_unprepare(pcie->clk);
+ }
+ 
+@@ -1426,12 +1771,16 @@ static const int pcie_offsets[] = {
+ 	[RGR1_SW_INIT_1] = 0x9210,
+ 	[EXT_CFG_INDEX]  = 0x9000,
+ 	[EXT_CFG_DATA]   = 0x9004,
++	[PCIE_HARD_DEBUG] = 0x4204,
++	[INTR2_CPU]      = 0x4300,
+ };
+ 
+ static const int pcie_offsets_bmips_7425[] = {
+ 	[RGR1_SW_INIT_1] = 0x8010,
+ 	[EXT_CFG_INDEX]  = 0x8300,
+ 	[EXT_CFG_DATA]   = 0x8304,
++	[PCIE_HARD_DEBUG] = 0x4204,
++	[INTR2_CPU]      = 0x4300,
+ };
+ 
+ static const struct pcie_cfg_data generic_cfg = {
+@@ -1439,6 +1788,7 @@ static const struct pcie_cfg_data generi
+ 	.type		= GENERIC,
+ 	.perst_set	= brcm_pcie_perst_set_generic,
+ 	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++	.rc_mode	= brcm_pcie_rc_mode_generic,
+ };
+ 
+ static const struct pcie_cfg_data bcm7425_cfg = {
+@@ -1446,6 +1796,7 @@ static const struct pcie_cfg_data bcm742
+ 	.type		= BCM7425,
+ 	.perst_set	= brcm_pcie_perst_set_generic,
+ 	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++	.rc_mode	= brcm_pcie_rc_mode_generic,
+ };
+ 
+ static const struct pcie_cfg_data bcm7435_cfg = {
+@@ -1460,12 +1811,15 @@ static const struct pcie_cfg_data bcm490
+ 	.type		= BCM4908,
+ 	.perst_set	= brcm_pcie_perst_set_4908,
+ 	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++	.rc_mode	= brcm_pcie_rc_mode_generic,
+ };
+ 
+ static const int pcie_offset_bcm7278[] = {
+ 	[RGR1_SW_INIT_1] = 0xc010,
+ 	[EXT_CFG_INDEX] = 0x9000,
+ 	[EXT_CFG_DATA] = 0x9004,
++	[PCIE_HARD_DEBUG] = 0x4204,
++	[INTR2_CPU]      = 0x4300,
+ };
+ 
+ static const struct pcie_cfg_data bcm7278_cfg = {
+@@ -1473,6 +1827,7 @@ static const struct pcie_cfg_data bcm727
+ 	.type		= BCM7278,
+ 	.perst_set	= brcm_pcie_perst_set_7278,
+ 	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
++	.rc_mode	= brcm_pcie_rc_mode_generic,
+ };
+ 
+ static const struct pcie_cfg_data bcm2711_cfg = {
+@@ -1480,10 +1835,27 @@ static const struct pcie_cfg_data bcm271
+ 	.type		= BCM2711,
+ 	.perst_set	= brcm_pcie_perst_set_generic,
+ 	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++	.rc_mode	= brcm_pcie_rc_mode_generic,
++};
++
++static const int pcie_offsets_bcm2712[] = {
++	[EXT_CFG_INDEX] = 0x9000,
++	[EXT_CFG_DATA] = 0x9004,
++	[PCIE_HARD_DEBUG] = 0x4304,
++	[INTR2_CPU] = 0x4400,
++};
++
++static const struct pcie_cfg_data bcm2712_cfg = {
++	.offsets	= pcie_offsets_bcm2712,
++	.type		= BCM2712,
++	.perst_set	= brcm_pcie_perst_set_2712,
++	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712,
++	.rc_mode	= brcm_pcie_rc_mode_2712,
+ };
+ 
+ static const struct of_device_id brcm_pcie_match[] = {
+ 	{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
++	{ .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg },
+ 	{ .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
+ 	{ .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
+ 	{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
+@@ -1524,7 +1896,7 @@ static int brcm_pcie_probe(struct platfo
+ 
+ 	data = of_device_get_match_data(&pdev->dev);
+ 	if (!data) {
+-		pr_err("failed to look up compatible string\n");
++		dev_err(&pdev->dev, "failed to look up compatible string\n");
+ 		return -EINVAL;
+ 	}
+ 
+@@ -1535,6 +1907,7 @@ static int brcm_pcie_probe(struct platfo
+ 	pcie->type = data->type;
+ 	pcie->perst_set = data->perst_set;
+ 	pcie->bridge_sw_init_set = data->bridge_sw_init_set;
++	pcie->rc_mode = data->rc_mode;
+ 
+ 	pcie->base = devm_platform_ioremap_resource(pdev, 0);
+ 	if (IS_ERR(pcie->base))
+@@ -1549,6 +1922,7 @@ static int brcm_pcie_probe(struct platfo
+ 
+ 	pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
+ 	pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
++	pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb");
+ 
+ 	ret = clk_prepare_enable(pcie->clk);
+ 	if (ret) {
+@@ -1565,14 +1939,20 @@ static int brcm_pcie_probe(struct platfo
+ 		clk_disable_unprepare(pcie->clk);
+ 		return PTR_ERR(pcie->perst_reset);
+ 	}
++	pcie->bridge_reset =
++		devm_reset_control_get_optional_exclusive(&pdev->dev, "bridge");
++	if (IS_ERR(pcie->bridge_reset)) {
++		clk_disable_unprepare(pcie->clk);
++		return PTR_ERR(pcie->bridge_reset);
++	}
+ 
+-	ret = reset_control_reset(pcie->rescal);
++	ret = reset_control_deassert(pcie->rescal);
+ 	if (ret)
+ 		dev_err(&pdev->dev, "failed to deassert 'rescal'\n");
+ 
+ 	ret = brcm_phy_start(pcie);
+ 	if (ret) {
+-		reset_control_rearm(pcie->rescal);
++		reset_control_assert(pcie->rescal);
+ 		clk_disable_unprepare(pcie->clk);
+ 		return ret;
+ 	}
+@@ -1595,6 +1975,33 @@ static int brcm_pcie_probe(struct platfo
+ 			dev_err(pcie->dev, "probe of internal MSI failed");
+ 			goto fail;
+ 		}
++	} else if (pci_msi_enabled() && msi_np != pcie->np) {
++		/* Use RC_BAR1 for MIP access */
++		u64 msi_pci_addr;
++		u64 msi_phys_addr;
++
++		if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) {
++			dev_err(pcie->dev, "Unable to find MSI PCI address\n");
++			ret = -EINVAL;
++			goto fail;
++		}
++
++		if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) {
++			dev_err(pcie->dev, "Unable to find MSI physical address\n");
++			ret = -EINVAL;
++			goto fail;
++		}
++
++		writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000),
++		       pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++		writel(upper_32_bits(msi_pci_addr),
++		       pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI);
++
++		writel(lower_32_bits(msi_phys_addr) |
++		       PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK,
++		       pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP);
++		writel(upper_32_bits(msi_phys_addr),
++		       pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI);
+ 	}
+ 
+ 	bridge->ops = pcie->type == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops;
+@@ -1611,6 +2018,8 @@ static int brcm_pcie_probe(struct platfo
+ 		return ret;
+ 	}
+ 
++	brcm_pcie_config_clkreq(pcie);
++
+ 	return 0;
+ 
+ fail:

+ 42 - 0
target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch

@@ -0,0 +1,42 @@
+From 9a11300e46344917226b986a8740e7581d66adf3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Mon, 7 Feb 2022 09:20:49 +0000
+Subject: [PATCH] V4L2: Add PiSP opaque formats to V4L2
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 4 +++-
+ include/uapi/linux/videodev2.h       | 7 +++++++
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,7 +1452,9 @@ static void v4l_fill_fmtdesc(struct v4l2
+ 	case V4L2_PIX_FMT_NV12M_10BE_8L128:	descr = "10-bit NV12M (8x128 Linear, BE)"; 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;
+-	case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP Config format"; break;
++	case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP BE Config format"; break;
++	case V4L2_META_FMT_RPI_FE_CFG: descr = "PiSP FE Config format"; break;
++	case V4L2_META_FMT_RPI_FE_STATS: descr = "PiSP FE Statistics format"; break;
+ 
+ 	default:
+ 		/* Compressed formats */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -826,8 +826,15 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A	v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+ 
+ /* The metadata format identifier for our configuration buffers. */
++/* The metadata format identifier for BE configuration buffers. */
+ #define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C')
+ 
++/* The metadata format identifier for FE configuration buffers. */
++#define V4L2_META_FMT_RPI_FE_CFG v4l2_fourcc('R', 'P', 'F', 'C')
++
++/* The metadata format identifier for FE configuration buffers. */
++#define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S')
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
+ 

+ 39 - 0
target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch

@@ -0,0 +1,39 @@
+From 01f31f4145d49a30eb553c65ea755dde8dba1de0 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Wed, 2 Mar 2022 16:10:50 +0000
+Subject: [PATCH] V4L2: Add PiSP compressed formats to V4L2
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++++
+ include/uapi/linux/videodev2.h       | 6 +++++-
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1507,6 +1507,10 @@ static void v4l_fill_fmtdesc(struct v4l2
+ 		case V4L2_PIX_FMT_QC08C:	descr = "QCOM Compressed 8-bit Format"; break;
+ 		case V4L2_PIX_FMT_QC10C:	descr = "QCOM Compressed 10-bit Format"; break;
+ 		case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
++		case V4L2_PIX_FMT_PISP_COMP_RGGB:
++		case V4L2_PIX_FMT_PISP_COMP_GRBG:
++		case V4L2_PIX_FMT_PISP_COMP_GBRG:
++		case V4L2_PIX_FMT_PISP_COMP_BGGR: descr = "PiSP Bayer Compressed Format"; break;
+ 		default:
+ 			if (fmt->description[0])
+ 				return;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -794,7 +794,11 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_IPU3_SRGGB10	v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
+ 
+ /* The pixel format for all our buffers (the precise format is found in the config buffer). */
+-#define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
++#define V4L2_PIX_FMT_RPI_BE		v4l2_fourcc('R', 'P', 'B', 'P')
++#define V4L2_PIX_FMT_PISP_COMP_RGGB	v4l2_fourcc('P', 'C', 'R', 'G')
++#define V4L2_PIX_FMT_PISP_COMP_GRBG	v4l2_fourcc('P', 'C', 'G', 'R')
++#define V4L2_PIX_FMT_PISP_COMP_GBRG	v4l2_fourcc('P', 'C', 'G', 'B')
++#define V4L2_PIX_FMT_PISP_COMP_BGGR	v4l2_fourcc('P', 'C', 'B', 'G')
+ 
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */

+ 249 - 0
target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch

@@ -0,0 +1,249 @@
+From c93f469dabdbed822e5abeb5283d79fc9faa858c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 28 Oct 2022 14:10:34 +0100
+Subject: [PATCH] dt-binding: mfd: Add binding for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ include/dt-bindings/mfd/rp1.h | 235 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 235 insertions(+)
+ create mode 100644 include/dt-bindings/mfd/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/mfd/rp1.h
+@@ -0,0 +1,235 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * This header provides constants for the PY MFD.
++ */
++
++#ifndef _RP1_H
++#define _RP1_H
++
++/* Address map */
++#define RP1_SYSINFO_BASE 0x000000
++#define RP1_TBMAN_BASE 0x004000
++#define RP1_SYSCFG_BASE 0x008000
++#define RP1_OTP_BASE 0x00c000
++#define RP1_POWER_BASE 0x010000
++#define RP1_RESETS_BASE 0x014000
++#define RP1_CLOCKS_BANK_DEFAULT_BASE 0x018000
++#define RP1_CLOCKS_BANK_VIDEO_BASE 0x01c000
++#define RP1_PLL_SYS_BASE 0x020000
++#define RP1_PLL_AUDIO_BASE 0x024000
++#define RP1_PLL_VIDEO_BASE 0x028000
++#define RP1_UART0_BASE 0x030000
++#define RP1_UART1_BASE 0x034000
++#define RP1_UART2_BASE 0x038000
++#define RP1_UART3_BASE 0x03c000
++#define RP1_UART4_BASE 0x040000
++#define RP1_UART5_BASE 0x044000
++#define RP1_SPI8_BASE 0x04c000
++#define RP1_SPI0_BASE 0x050000
++#define RP1_SPI1_BASE 0x054000
++#define RP1_SPI2_BASE 0x058000
++#define RP1_SPI3_BASE 0x05c000
++#define RP1_SPI4_BASE 0x060000
++#define RP1_SPI5_BASE 0x064000
++#define RP1_SPI6_BASE 0x068000
++#define RP1_SPI7_BASE 0x06c000
++#define RP1_I2C0_BASE 0x070000
++#define RP1_I2C1_BASE 0x074000
++#define RP1_I2C2_BASE 0x078000
++#define RP1_I2C3_BASE 0x07c000
++#define RP1_I2C4_BASE 0x080000
++#define RP1_I2C5_BASE 0x084000
++#define RP1_I2C6_BASE 0x088000
++#define RP1_AUDIO_IN_BASE 0x090000
++#define RP1_AUDIO_OUT_BASE 0x094000
++#define RP1_PWM0_BASE 0x098000
++#define RP1_PWM1_BASE 0x09c000
++#define RP1_I2S0_BASE 0x0a0000
++#define RP1_I2S1_BASE 0x0a4000
++#define RP1_I2S2_BASE 0x0a8000
++#define RP1_TIMER_BASE 0x0ac000
++#define RP1_SDIO0_APBS_BASE 0x0b0000
++#define RP1_SDIO1_APBS_BASE 0x0b4000
++#define RP1_BUSFABRIC_MONITOR_BASE 0x0c0000
++#define RP1_BUSFABRIC_AXISHIM_BASE 0x0c4000
++#define RP1_ADC_BASE 0x0c8000
++#define RP1_IO_BANK0_BASE 0x0d0000
++#define RP1_IO_BANK1_BASE 0x0d4000
++#define RP1_IO_BANK2_BASE 0x0d8000
++#define RP1_SYS_RIO0_BASE 0x0e0000
++#define RP1_SYS_RIO1_BASE 0x0e4000
++#define RP1_SYS_RIO2_BASE 0x0e8000
++#define RP1_PADS_BANK0_BASE 0x0f0000
++#define RP1_PADS_BANK1_BASE 0x0f4000
++#define RP1_PADS_BANK2_BASE 0x0f8000
++#define RP1_PADS_ETH_BASE 0x0fc000
++#define RP1_ETH_IP_BASE 0x100000
++#define RP1_ETH_CFG_BASE 0x104000
++#define RP1_PCIE_APBS_BASE 0x108000
++#define RP1_MIPI0_CSIDMA_BASE 0x110000
++#define RP1_MIPI0_CSIHOST_BASE 0x114000
++#define RP1_MIPI0_DSIDMA_BASE 0x118000
++#define RP1_MIPI0_DSIHOST_BASE 0x11c000
++#define RP1_MIPI0_MIPICFG_BASE 0x120000
++#define RP1_MIPI0_ISP_BASE 0x124000
++#define RP1_MIPI1_CSIDMA_BASE 0x128000
++#define RP1_MIPI1_CSIHOST_BASE 0x12c000
++#define RP1_MIPI1_DSIDMA_BASE 0x130000
++#define RP1_MIPI1_DSIHOST_BASE 0x134000
++#define RP1_MIPI1_MIPICFG_BASE 0x138000
++#define RP1_MIPI1_ISP_BASE 0x13c000
++#define RP1_VIDEO_OUT_CFG_BASE 0x140000
++#define RP1_VIDEO_OUT_VEC_BASE 0x144000
++#define RP1_VIDEO_OUT_DPI_BASE 0x148000
++#define RP1_XOSC_BASE 0x150000
++#define RP1_WATCHDOG_BASE 0x154000
++#define RP1_DMA_TICK_BASE 0x158000
++#define RP1_SDIO_CLOCKS_BASE 0x15c000
++#define RP1_USBHOST0_APBS_BASE 0x160000
++#define RP1_USBHOST1_APBS_BASE 0x164000
++#define RP1_ROSC0_BASE 0x168000
++#define RP1_ROSC1_BASE 0x16c000
++#define RP1_VBUSCTRL_BASE 0x170000
++#define RP1_TICKS_BASE 0x174000
++#define RP1_PIO_APBS_BASE 0x178000
++#define RP1_SDIO0_AHBLS_BASE 0x180000
++#define RP1_SDIO1_AHBLS_BASE 0x184000
++#define RP1_DMA_BASE 0x188000
++#define RP1_RAM_BASE 0x1c0000
++#define RP1_RAM_SIZE 0x020000
++#define RP1_USBHOST0_AXIS_BASE 0x200000
++#define RP1_USBHOST1_AXIS_BASE 0x300000
++#define RP1_EXAC_BASE 0x400000
++
++/* Interrupts */
++
++#define RP1_INT_IO_BANK0 0
++#define RP1_INT_IO_BANK1 1
++#define RP1_INT_IO_BANK2 2
++#define RP1_INT_AUDIO_IN 3
++#define RP1_INT_AUDIO_OUT 4
++#define RP1_INT_PWM0 5
++#define RP1_INT_ETH 6
++#define RP1_INT_I2C0 7
++#define RP1_INT_I2C1 8
++#define RP1_INT_I2C2 9
++#define RP1_INT_I2C3 10
++#define RP1_INT_I2C4 11
++#define RP1_INT_I2C5 12
++#define RP1_INT_I2C6 13
++#define RP1_INT_I2S0 14
++#define RP1_INT_I2S1 15
++#define RP1_INT_I2S2 16
++#define RP1_INT_SDIO0 17
++#define RP1_INT_SDIO1 18
++#define RP1_INT_SPI0 19
++#define RP1_INT_SPI1 20
++#define RP1_INT_SPI2 21
++#define RP1_INT_SPI3 22
++#define RP1_INT_SPI4 23
++#define RP1_INT_SPI5 24
++#define RP1_INT_UART0 25
++#define RP1_INT_TIMER_0 26
++#define RP1_INT_TIMER_1 27
++#define RP1_INT_TIMER_2 28
++#define RP1_INT_TIMER_3 29
++#define RP1_INT_USBHOST0 30
++#define RP1_INT_USBHOST0_0 31
++#define RP1_INT_USBHOST0_1 32
++#define RP1_INT_USBHOST0_2 33
++#define RP1_INT_USBHOST0_3 34
++#define RP1_INT_USBHOST1 35
++#define RP1_INT_USBHOST1_0 36
++#define RP1_INT_USBHOST1_1 37
++#define RP1_INT_USBHOST1_2 38
++#define RP1_INT_USBHOST1_3 39
++#define RP1_INT_DMA 40
++#define RP1_INT_PWM1 41
++#define RP1_INT_UART1 42
++#define RP1_INT_UART2 43
++#define RP1_INT_UART3 44
++#define RP1_INT_UART4 45
++#define RP1_INT_UART5 46
++#define RP1_INT_MIPI0 47
++#define RP1_INT_MIPI1 48
++#define RP1_INT_VIDEO_OUT 49
++#define RP1_INT_PIO_0 50
++#define RP1_INT_PIO_1 51
++#define RP1_INT_ADC_FIFO 52
++#define RP1_INT_PCIE_OUT 53
++#define RP1_INT_SPI6 54
++#define RP1_INT_SPI7 55
++#define RP1_INT_SPI8 56
++#define RP1_INT_SYSCFG 58
++#define RP1_INT_CLOCKS_DEFAULT 59
++#define RP1_INT_VBUSCTRL 60
++#define RP1_INT_PROC_MISC 57
++#define RP1_INT_END 61
++
++/* DMA peripherals (for pacing) */
++#define RP1_DMA_I2C0_RX 0x0
++#define RP1_DMA_I2C0_TX 0x1
++#define RP1_DMA_I2C1_RX 0x2
++#define RP1_DMA_I2C1_TX 0x3
++#define RP1_DMA_I2C2_RX 0x4
++#define RP1_DMA_I2C2_TX 0x5
++#define RP1_DMA_I2C3_RX 0x6
++#define RP1_DMA_I2C3_TX 0x7
++#define RP1_DMA_I2C4_RX 0x8
++#define RP1_DMA_I2C4_TX 0x9
++#define RP1_DMA_I2C5_RX 0xa
++#define RP1_DMA_I2C5_TX 0xb
++#define RP1_DMA_SPI0_RX 0xc
++#define RP1_DMA_SPI0_TX 0xd
++#define RP1_DMA_SPI1_RX 0xe
++#define RP1_DMA_SPI1_TX 0xf
++#define RP1_DMA_SPI2_RX 0x10
++#define RP1_DMA_SPI2_TX 0x11
++#define RP1_DMA_SPI3_RX 0x12
++#define RP1_DMA_SPI3_TX 0x13
++#define RP1_DMA_SPI4_RX 0x14
++#define RP1_DMA_SPI4_TX 0x15
++#define RP1_DMA_SPI5_RX 0x16
++#define RP1_DMA_SPI5_TX 0x17
++#define RP1_DMA_PWM0 0x18
++#define RP1_DMA_UART0_RX 0x19
++#define RP1_DMA_UART0_TX 0x1a
++#define RP1_DMA_AUDIO_IN_CH0 0x1b
++#define RP1_DMA_AUDIO_IN_CH1 0x1c
++#define RP1_DMA_AUDIO_OUT 0x1d
++#define RP1_DMA_PWM1 0x1e
++#define RP1_DMA_I2S0_RX 0x1f
++#define RP1_DMA_I2S0_TX 0x20
++#define RP1_DMA_I2S1_RX 0x21
++#define RP1_DMA_I2S1_TX 0x22
++#define RP1_DMA_I2S2_RX 0x23
++#define RP1_DMA_I2S2_TX 0x24
++#define RP1_DMA_UART1_RX 0x25
++#define RP1_DMA_UART1_TX 0x26
++#define RP1_DMA_UART2_RX 0x27
++#define RP1_DMA_UART2_TX 0x28
++#define RP1_DMA_UART3_RX 0x29
++#define RP1_DMA_UART3_TX 0x2a
++#define RP1_DMA_UART4_RX 0x2b
++#define RP1_DMA_UART4_TX 0x2c
++#define RP1_DMA_UART5_RX 0x2d
++#define RP1_DMA_UART5_TX 0x2e
++#define RP1_DMA_ADC 0x2f
++#define RP1_DMA_DMA_TICK_TICK0 0x30
++#define RP1_DMA_DMA_TICK_TICK1 0x31
++#define RP1_DMA_SPI6_RX 0x32
++#define RP1_DMA_SPI6_TX 0x33
++#define RP1_DMA_SPI7_RX 0x34
++#define RP1_DMA_SPI7_TX 0x35
++#define RP1_DMA_SPI8_RX 0x36
++#define RP1_DMA_SPI8_TX 0x37
++#define RP1_DMA_PIO_CH0_TX 0x38
++#define RP1_DMA_PIO_CH0_RX 0x39
++#define RP1_DMA_PIO_CH1_TX 0x3a
++#define RP1_DMA_PIO_CH1_RX 0x3b
++#define RP1_DMA_PIO_CH2_TX 0x3c
++#define RP1_DMA_PIO_CH2_RX 0x3d
++#define RP1_DMA_PIO_CH3_TX 0x3e
++#define RP1_DMA_PIO_CH3_RX 0x3f
++
++#endif

+ 442 - 0
target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch

@@ -0,0 +1,442 @@
+From 7196a12b94e90225686e6c34cdf65a583214f7a5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 10 Oct 2022 14:21:50 +0100
+Subject: [PATCH] mfd: Add rp1 driver
+
+RP1 is a multifunction PCIe device that exposes a range of
+peripherals.
+Add the parent driver to manage these.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/mfd/Kconfig          |  11 ++
+ drivers/mfd/Makefile         |   1 +
+ drivers/mfd/rp1.c            | 367 +++++++++++++++++++++++++++++++++++
+ include/linux/rp1_platform.h |  20 ++
+ 4 files changed, 399 insertions(+)
+ create mode 100644 drivers/mfd/rp1.c
+ create mode 100644 include/linux/rp1_platform.h
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -2252,6 +2252,17 @@ config MFD_INTEL_M10_BMC
+ 	  additional drivers must be enabled in order to use the functionality
+ 	  of the device.
+ 
++config MFD_RP1
++	tristate "RP1 MFD driver"
++	depends on PCI
++	select MFD_CORE
++	help
++	  Support for the RP1 peripheral chip.
++
++	  This driver provides support for the Raspberry Pi RP1 peripheral chip.
++	  It is responsible for enabling the Device Tree node once the PCIe endpoint
++	  has been configured, and handling interrupts.
++
+ config MFD_RSMU_I2C
+ 	tristate "Renesas Synchronization Management Unit with I2C"
+ 	depends on I2C && OF
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -273,6 +273,7 @@ obj-$(CONFIG_MFD_RPISENSE_CORE)	+= rpise
+ obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
+ obj-$(CONFIG_MFD_SIMPLE_MFD_I2C)	+= simple-mfd-i2c.o
+ obj-$(CONFIG_MFD_INTEL_M10_BMC)   += intel-m10-bmc.o
++obj-$(CONFIG_MFD_RP1)		+= rp1.o
+ 
+ obj-$(CONFIG_MFD_ATC260X)	+= atc260x-core.o
+ obj-$(CONFIG_MFD_ATC260X_I2C)	+= atc260x-i2c.o
+--- /dev/null
++++ b/drivers/mfd/rp1.c
+@@ -0,0 +1,367 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2018-22 Raspberry Pi Ltd.
++ * All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/clkdev.h>
++#include <linux/clk-provider.h>
++#include <linux/completion.h>
++#include <linux/etherdevice.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/mfd/core.h>
++#include <linux/mmc/host.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_platform.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++
++#include <dt-bindings/mfd/rp1.h>
++
++/* TO DO:
++ * 1. Occasional shutdown crash - RP1 being closed before its children?
++ * 2. DT mode interrupt handling.
++ */
++
++#define RP1_DRIVER_NAME "rp1"
++
++#define PCI_VENDOR_ID_RPI 0x1de4
++#define PCI_DEVICE_ID_RP1_C0 0x0001
++#define PCI_DEVICE_REV_RP1_C0 2
++
++#define RP1_ACTUAL_IRQS		RP1_INT_END
++#define RP1_IRQS		RP1_ACTUAL_IRQS
++
++#define RP1_SYSCLK_RATE		200000000
++#define RP1_SYSCLK_FPGA_RATE	60000000
++
++// Don't want to include the whole sysinfo reg header
++#define SYSINFO_CHIP_ID_OFFSET	0x00000000
++#define SYSINFO_PLATFORM_OFFSET	0x00000004
++
++#define REG_RW          0x000
++#define REG_SET         0x800
++#define REG_CLR         0xc00
++
++// MSIX CFG registers start at 0x8
++#define MSIX_CFG(x) (0x8 + (4 * (x)))
++
++#define MSIX_CFG_IACK_EN        BIT(3)
++#define MSIX_CFG_IACK           BIT(2)
++#define MSIX_CFG_TEST           BIT(1)
++#define MSIX_CFG_ENABLE         BIT(0)
++
++#define INTSTATL		0x108
++#define INTSTATH		0x10c
++
++struct rp1_dev {
++	struct pci_dev *pdev;
++	struct device *dev;
++	resource_size_t bar_start;
++	resource_size_t bar_end;
++	struct clk *sys_clk;
++	struct irq_domain *domain;
++	struct irq_data *pcie_irqds[64];
++	void __iomem *msix_cfg_regs;
++};
++
++static bool rp1_level_triggered_irq[RP1_ACTUAL_IRQS] = { 0 };
++
++static struct rp1_dev *g_rp1;
++static u32 g_chip_id, g_platform;
++
++static void dump_bar(struct pci_dev *pdev, unsigned int bar)
++{
++	dev_info(&pdev->dev,
++		 "bar%d len 0x%llx, start 0x%llx, end 0x%llx, flags, 0x%lx\n",
++		 bar,
++		 pci_resource_len(pdev, bar),
++		 pci_resource_start(pdev, bar),
++		 pci_resource_end(pdev, bar),
++		 pci_resource_flags(pdev, bar));
++}
++
++static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
++{
++	writel(value, rp1->msix_cfg_regs + REG_SET + MSIX_CFG(hwirq));
++}
++
++static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
++{
++	writel(value, rp1->msix_cfg_regs + REG_CLR + MSIX_CFG(hwirq));
++}
++
++static void rp1_mask_irq(struct irq_data *irqd)
++{
++	struct rp1_dev *rp1 = irqd->domain->host_data;
++	struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++
++	pci_msi_mask_irq(pcie_irqd);
++}
++
++static void rp1_unmask_irq(struct irq_data *irqd)
++{
++	struct rp1_dev *rp1 = irqd->domain->host_data;
++	struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++
++	pci_msi_unmask_irq(pcie_irqd);
++}
++
++static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type)
++{
++	struct rp1_dev *rp1 = irqd->domain->host_data;
++	unsigned int hwirq = (unsigned int)irqd->hwirq;
++	int ret = 0;
++
++	switch (type) {
++	case IRQ_TYPE_LEVEL_HIGH:
++		dev_dbg(rp1->dev, "MSIX IACK EN for irq %d\n", hwirq);
++		msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN);
++		rp1_level_triggered_irq[hwirq] = true;
++	break;
++	case IRQ_TYPE_EDGE_RISING:
++		msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN);
++		rp1_level_triggered_irq[hwirq] = false;
++		break;
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	return ret;
++}
++
++static struct irq_chip rp1_irq_chip = {
++	.name            = "rp1_irq_chip",
++	.irq_mask        = rp1_mask_irq,
++	.irq_unmask      = rp1_unmask_irq,
++	.irq_set_type    = rp1_irq_set_type,
++};
++
++static void rp1_chained_handle_irq(struct irq_desc *desc)
++{
++	struct irq_chip *chip = irq_desc_get_chip(desc);
++	struct rp1_dev *rp1 = desc->irq_data.chip_data;
++	unsigned int hwirq = desc->irq_data.hwirq & 0x3f;
++	int new_irq;
++
++	rp1 = g_rp1;
++
++	chained_irq_enter(chip, desc);
++
++	new_irq = irq_linear_revmap(rp1->domain, hwirq);
++	generic_handle_irq(new_irq);
++	if (rp1_level_triggered_irq[hwirq])
++		msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK);
++
++	chained_irq_exit(chip, desc);
++}
++
++static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node,
++			 const u32 *intspec, unsigned int intsize,
++			 unsigned long *out_hwirq, unsigned int *out_type)
++{
++	struct rp1_dev *rp1 = d->host_data;
++	struct irq_data *pcie_irqd;
++	unsigned long hwirq;
++	int pcie_irq;
++	int ret;
++
++	ret = irq_domain_xlate_twocell(d, node, intspec, intsize,
++				       &hwirq, out_type);
++	if (!ret) {
++		pcie_irq = pci_irq_vector(rp1->pdev, hwirq);
++		pcie_irqd = irq_get_irq_data(pcie_irq);
++		rp1->pcie_irqds[hwirq] = pcie_irqd;
++		*out_hwirq = hwirq;
++	}
++	return ret;
++}
++
++static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd,
++			    bool reserve)
++{
++	struct rp1_dev *rp1 = d->host_data;
++	struct irq_data *pcie_irqd;
++
++	pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++	msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
++	return irq_domain_activate_irq(pcie_irqd, reserve);
++}
++
++static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd)
++{
++	struct rp1_dev *rp1 = d->host_data;
++	struct irq_data *pcie_irqd;
++
++	pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++	msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
++	return irq_domain_deactivate_irq(pcie_irqd);
++}
++
++static const struct irq_domain_ops rp1_domain_ops = {
++	.xlate      = rp1_irq_xlate,
++	.activate   = rp1_irq_activate,
++	.deactivate = rp1_irq_deactivate,
++};
++
++static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int offset)
++{
++	return rp1->bar_start + offset;
++}
++
++static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 offset)
++{
++	dma_addr_t phys = rp1_io_to_phys(rp1, base_addr);
++	void __iomem *regblock = ioremap(phys, 0x1000);
++	u32 value = readl(regblock + offset);
++
++	iounmap(regblock);
++	return value;
++}
++
++void rp1_get_platform(u32 *chip_id, u32 *platform)
++{
++	if (chip_id)
++		*chip_id = g_chip_id;
++	if (platform)
++		*platform = g_platform;
++}
++EXPORT_SYMBOL_GPL(rp1_get_platform);
++
++static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++	struct reset_control *reset;
++	struct platform_device *pcie_pdev;
++	struct device_node *rp1_node;
++	struct rp1_dev *rp1;
++	int err  = 0;
++	int i;
++
++	reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
++	if (IS_ERR(reset))
++		return PTR_ERR(reset);
++	reset_control_reset(reset);
++
++	dump_bar(pdev, 0);
++	dump_bar(pdev, 1);
++
++	if (pci_resource_len(pdev, 1) <= 0x10000) {
++		dev_err(&pdev->dev,
++			"Not initialised - is the firmware running?\n");
++		return -EINVAL;
++	}
++
++	/* enable pci device */
++	err = pcim_enable_device(pdev);
++	if (err < 0) {
++		dev_err(&pdev->dev, "Enabling PCI device has failed: %d",
++			err);
++		return err;
++	}
++
++	pci_set_master(pdev);
++
++	err = pci_alloc_irq_vectors(pdev, RP1_IRQS, RP1_IRQS,
++				    PCI_IRQ_MSIX);
++	if (err != RP1_IRQS) {
++		dev_err(&pdev->dev, "pci_alloc_irq_vectors failed - %d\n", err);
++		return err;
++	}
++
++	rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL);
++	if (!rp1)
++		return -ENOMEM;
++
++	rp1->pdev = pdev;
++	rp1->dev = &pdev->dev;
++
++	pci_set_drvdata(pdev, rp1);
++
++	rp1->bar_start = pci_resource_start(pdev, 1);
++	rp1->bar_end = pci_resource_end(pdev, 1);
++
++	// Get chip id
++	g_chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET);
++	g_platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET);
++	dev_info(&pdev->dev, "chip_id 0x%x%s\n", g_chip_id,
++		 (g_platform & RP1_PLATFORM_FPGA) ? " FPGA" : "");
++	if (g_chip_id != RP1_C0_CHIP_ID) {
++		dev_err(&pdev->dev, "wrong chip id (%x)\n", g_chip_id);
++		return -EINVAL;
++	}
++
++	rp1_node = of_find_node_by_name(NULL, "rp1");
++	if (!rp1_node) {
++		dev_err(&pdev->dev, "failed to find RP1 DT node\n");
++		return -EINVAL;
++	}
++
++	pcie_pdev = of_find_device_by_node(rp1_node->parent);
++	rp1->domain = irq_domain_add_linear(rp1_node, RP1_IRQS,
++					    &rp1_domain_ops, rp1);
++
++	g_rp1 = rp1;
++
++	/* TODO can this go in the rp1 device tree entry? */
++	rp1->msix_cfg_regs = ioremap(rp1_io_to_phys(rp1, RP1_PCIE_APBS_BASE), 0x1000);
++
++	for (i = 0; i < RP1_IRQS; i++) {
++		int irq = irq_create_mapping(rp1->domain, i);
++
++		if (irq < 0) {
++			dev_err(&pdev->dev, "failed to create irq mapping\n");
++			return irq;
++		}
++
++		irq_set_chip_data(irq, rp1);
++		irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq);
++		irq_set_probe(irq);
++		irq_set_chained_handler(pci_irq_vector(pdev, i),
++					rp1_chained_handle_irq);
++	}
++
++	if (rp1_node)
++		of_platform_populate(rp1_node, NULL, NULL, &pcie_pdev->dev);
++
++	of_node_put(rp1_node);
++
++	return 0;
++}
++
++static void rp1_remove(struct pci_dev *pdev)
++{
++	struct rp1_dev *rp1 = pci_get_drvdata(pdev);
++
++	mfd_remove_devices(&pdev->dev);
++
++	clk_unregister(rp1->sys_clk);
++}
++
++static const struct pci_device_id dev_id_table[] = {
++	{ PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), },
++	{ 0, }
++};
++
++static struct pci_driver rp1_driver = {
++	.name		= RP1_DRIVER_NAME,
++	.id_table	= dev_id_table,
++	.probe		= rp1_probe,
++	.remove		= rp1_remove,
++};
++
++module_pci_driver(rp1_driver);
++
++MODULE_AUTHOR("Phil Elwell <[email protected]>");
++MODULE_DESCRIPTION("RP1 wrapper");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/include/linux/rp1_platform.h
+@@ -0,0 +1,20 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2021-2022 Raspberry Pi Ltd.
++ * All rights reserved.
++ */
++
++#ifndef _RP1_PLATFORM_H
++#define _RP1_PLATFORM_H
++
++#include <vdso/bits.h>
++
++#define RP1_B0_CHIP_ID 0x10001927
++#define RP1_C0_CHIP_ID 0x20001927
++
++#define RP1_PLATFORM_ASIC BIT(1)
++#define RP1_PLATFORM_FPGA BIT(0)
++
++void rp1_get_platform(u32 *chip_id, u32 *platform);
++
++#endif

+ 65 - 0
target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch

@@ -0,0 +1,65 @@
+From 00ff2819eb852b54fe22e7181646e40d560576dc Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 28 Oct 2022 14:12:18 +0100
+Subject: [PATCH] dt-bindings: clock: Add bindings for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ include/dt-bindings/clock/rp1.h | 51 +++++++++++++++++++++++++++++++++
+ 1 file changed, 51 insertions(+)
+ create mode 100644 include/dt-bindings/clock/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/clock/rp1.h
+@@ -0,0 +1,51 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021 Raspberry Pi Ltd.
++ */
++
++#define RP1_PLL_SYS_CORE		0
++#define RP1_PLL_AUDIO_CORE		1
++#define RP1_PLL_VIDEO_CORE		2
++
++#define RP1_PLL_SYS			3
++#define RP1_PLL_AUDIO			4
++#define RP1_PLL_VIDEO			5
++
++#define RP1_PLL_SYS_PRI_PH		6
++#define RP1_PLL_SYS_SEC_PH		7
++
++#define RP1_PLL_SYS_SEC			8
++#define RP1_PLL_AUDIO_SEC		9
++#define RP1_PLL_VIDEO_SEC		10
++
++#define RP1_CLK_SYS			11
++#define RP1_CLK_SLOW_SYS		12
++#define RP1_CLK_DMA			13
++#define RP1_CLK_UART			14
++#define RP1_CLK_ETH			15
++#define RP1_CLK_PWM0			16
++#define RP1_CLK_PWM1			17
++#define RP1_CLK_AUDIO_IN		18
++#define RP1_CLK_AUDIO_OUT		19
++#define RP1_CLK_I2S			20
++#define RP1_CLK_MIPI0_CFG		21
++#define RP1_CLK_MIPI1_CFG		22
++#define RP1_CLK_PCIE_AUX		23
++#define RP1_CLK_USBH0_MICROFRAME	24
++#define RP1_CLK_USBH1_MICROFRAME	25
++#define RP1_CLK_USBH0_SUSPEND		26
++#define RP1_CLK_USBH1_SUSPEND		27
++#define RP1_CLK_ETH_TSU			28
++#define RP1_CLK_ADC			29
++#define RP1_CLK_SDIO_TIMER		30
++#define RP1_CLK_SDIO_ALT_SRC		31
++#define RP1_CLK_GP0			32
++#define RP1_CLK_GP1			33
++#define RP1_CLK_GP2			34
++#define RP1_CLK_GP3			35
++#define RP1_CLK_GP4			36
++#define RP1_CLK_GP5			37
++#define RP1_CLK_VEC			38
++#define RP1_CLK_DPI			39
++#define RP1_CLK_MIPI0_DPI		40
++#define RP1_CLK_MIPI1_DPI		41

+ 2208 - 0
target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch

@@ -0,0 +1,2208 @@
+From 66517cdfea750b89d86f78af55ef773cbd3e005f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 10 Oct 2022 14:25:38 +0100
+Subject: [PATCH] clk: Add rp1 clock driver
+
+RP1 contains various PLLs and clocks for driving the hardware
+blocks, so add a driver to configure these.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/clk/Kconfig             |    7 +
+ drivers/clk/Makefile            |    1 +
+ drivers/clk/clk-rp1.c           | 2085 +++++++++++++++++++++++++++++++
+ include/dt-bindings/clock/rp1.h |   69 +-
+ 4 files changed, 2128 insertions(+), 34 deletions(-)
+ create mode 100644 drivers/clk/clk-rp1.c
+
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -89,6 +89,13 @@ config COMMON_CLK_RK808
+ 	  These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each.
+ 	  Clkout1 is always on, Clkout2 can off by control register.
+ 
++config COMMON_CLK_RP1
++	tristate "Raspberry Pi RP1-based clock support"
++	depends on PCI || COMPILE_TEST
++	depends on COMMON_CLK
++	help
++	  Enable common clock framework support for Raspberry Pi RP1
++
+ config COMMON_CLK_HI655X
+ 	tristate "Clock driver for Hi655x" if EXPERT
+ 	depends on (MFD_HI655X_PMIC || COMPILE_TEST)
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -58,6 +58,7 @@ obj-$(CONFIG_CLK_LS1028A_PLLDIG)	+= clk-
+ obj-$(CONFIG_COMMON_CLK_PWM)		+= clk-pwm.o
+ obj-$(CONFIG_CLK_QORIQ)			+= clk-qoriq.o
+ obj-$(CONFIG_COMMON_CLK_RK808)		+= clk-rk808.o
++obj-$(CONFIG_COMMON_CLK_RP1)		+= clk-rp1.o
+ obj-$(CONFIG_COMMON_CLK_HI655X)		+= clk-hi655x.o
+ obj-$(CONFIG_COMMON_CLK_S2MPS11)	+= clk-s2mps11.o
+ obj-$(CONFIG_COMMON_CLK_SCMI)           += clk-scmi.o
+--- /dev/null
++++ b/drivers/clk/clk-rp1.c
+@@ -0,0 +1,2085 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * Clock driver for RP1 PCIe multifunction chip.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/clk.h>
++#include <linux/debugfs.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include <linux/slab.h>
++
++#include <asm/div64.h>
++
++#include <dt-bindings/clock/rp1.h>
++
++#define PLL_SYS_CS			0x08000
++#define PLL_SYS_PWR			0x08004
++#define PLL_SYS_FBDIV_INT		0x08008
++#define PLL_SYS_FBDIV_FRAC		0x0800c
++#define PLL_SYS_PRIM			0x08010
++#define PLL_SYS_SEC			0x08014
++
++#define PLL_AUDIO_CS			0x0c000
++#define PLL_AUDIO_PWR			0x0c004
++#define PLL_AUDIO_FBDIV_INT		0x0c008
++#define PLL_AUDIO_FBDIV_FRAC		0x0c00c
++#define PLL_AUDIO_PRIM			0x0c010
++#define PLL_AUDIO_SEC			0x0c014
++
++#define PLL_VIDEO_CS			0x10000
++#define PLL_VIDEO_PWR			0x10004
++#define PLL_VIDEO_FBDIV_INT		0x10008
++#define PLL_VIDEO_FBDIV_FRAC		0x1000c
++#define PLL_VIDEO_PRIM			0x10010
++#define PLL_VIDEO_SEC			0x10014
++
++#define CLK_SYS_CTRL			0x00014
++#define CLK_SYS_DIV_INT			0x00018
++#define CLK_SYS_SEL			0x00020
++
++#define CLK_SLOW_SYS_CTRL		0x00024
++#define CLK_SLOW_SYS_DIV_INT		0x00028
++#define CLK_SLOW_SYS_SEL		0x00030
++
++#define CLK_DMA_CTRL			0x00044
++#define CLK_DMA_DIV_INT			0x00048
++#define CLK_DMA_SEL			0x00050
++
++#define CLK_UART_CTRL			0x00054
++#define CLK_UART_DIV_INT		0x00058
++#define CLK_UART_SEL			0x00060
++
++#define CLK_ETH_CTRL			0x00064
++#define CLK_ETH_DIV_INT			0x00068
++#define CLK_ETH_SEL			0x00070
++
++#define CLK_PWM0_CTRL			0x00074
++#define CLK_PWM0_DIV_INT		0x00078
++#define CLK_PWM0_DIV_FRAC		0x0007c
++#define CLK_PWM0_SEL			0x00080
++
++#define CLK_PWM1_CTRL			0x00084
++#define CLK_PWM1_DIV_INT		0x00088
++#define CLK_PWM1_DIV_FRAC		0x0008c
++#define CLK_PWM1_SEL			0x00090
++
++#define CLK_AUDIO_IN_CTRL		0x00094
++#define CLK_AUDIO_IN_DIV_INT		0x00098
++#define CLK_AUDIO_IN_SEL		0x000a0
++
++#define CLK_AUDIO_OUT_CTRL		0x000a4
++#define CLK_AUDIO_OUT_DIV_INT		0x000a8
++#define CLK_AUDIO_OUT_SEL		0x000b0
++
++#define CLK_I2S_CTRL			0x000b4
++#define CLK_I2S_DIV_INT			0x000b8
++#define CLK_I2S_SEL			0x000c0
++
++#define CLK_MIPI0_CFG_CTRL		0x000c4
++#define CLK_MIPI0_CFG_DIV_INT		0x000c8
++#define CLK_MIPI0_CFG_SEL		0x000d0
++
++#define CLK_MIPI1_CFG_CTRL		0x000d4
++#define CLK_MIPI1_CFG_DIV_INT		0x000d8
++#define CLK_MIPI1_CFG_SEL		0x000e0
++
++#define CLK_PCIE_AUX_CTRL		0x000e4
++#define CLK_PCIE_AUX_DIV_INT		0x000e8
++#define CLK_PCIE_AUX_SEL		0x000f0
++
++#define CLK_USBH0_MICROFRAME_CTRL	0x000f4
++#define CLK_USBH0_MICROFRAME_DIV_INT	0x000f8
++#define CLK_USBH0_MICROFRAME_SEL	0x00100
++
++#define CLK_USBH1_MICROFRAME_CTRL	0x00104
++#define CLK_USBH1_MICROFRAME_DIV_INT	0x00108
++#define CLK_USBH1_MICROFRAME_SEL	0x00110
++
++#define CLK_USBH0_SUSPEND_CTRL		0x00114
++#define CLK_USBH0_SUSPEND_DIV_INT	0x00118
++#define CLK_USBH0_SUSPEND_SEL		0x00120
++
++#define CLK_USBH1_SUSPEND_CTRL		0x00124
++#define CLK_USBH1_SUSPEND_DIV_INT	0x00128
++#define CLK_USBH1_SUSPEND_SEL		0x00130
++
++#define CLK_ETH_TSU_CTRL		0x00134
++#define CLK_ETH_TSU_DIV_INT		0x00138
++#define CLK_ETH_TSU_SEL			0x00140
++
++#define CLK_ADC_CTRL			0x00144
++#define CLK_ADC_DIV_INT			0x00148
++#define CLK_ADC_SEL			0x00150
++
++#define CLK_SDIO_TIMER_CTRL		0x00154
++#define CLK_SDIO_TIMER_DIV_INT		0x00158
++#define CLK_SDIO_TIMER_SEL		0x00160
++
++#define CLK_SDIO_ALT_SRC_CTRL		0x00164
++#define CLK_SDIO_ALT_SRC_DIV_INT	0x00168
++#define CLK_SDIO_ALT_SRC_SEL		0x00170
++
++#define CLK_GP0_CTRL			0x00174
++#define CLK_GP0_DIV_INT			0x00178
++#define CLK_GP0_DIV_FRAC		0x0017c
++#define CLK_GP0_SEL			0x00180
++
++#define CLK_GP1_CTRL			0x00184
++#define CLK_GP1_DIV_INT			0x00188
++#define CLK_GP1_DIV_FRAC		0x0018c
++#define CLK_GP1_SEL			0x00190
++
++#define CLK_GP2_CTRL			0x00194
++#define CLK_GP2_DIV_INT			0x00198
++#define CLK_GP2_DIV_FRAC		0x0019c
++#define CLK_GP2_SEL			0x001a0
++
++#define CLK_GP3_CTRL			0x001a4
++#define CLK_GP3_DIV_INT			0x001a8
++#define CLK_GP3_DIV_FRAC		0x001ac
++#define CLK_GP3_SEL			0x001b0
++
++#define CLK_GP4_CTRL			0x001b4
++#define CLK_GP4_DIV_INT			0x001b8
++#define CLK_GP4_DIV_FRAC		0x001bc
++#define CLK_GP4_SEL			0x001c0
++
++#define CLK_GP5_CTRL			0x001c4
++#define CLK_GP5_DIV_INT			0x001c8
++#define CLK_GP5_DIV_FRAC		0x001cc
++#define CLK_GP5_SEL			0x001d0
++
++#define CLK_SYS_RESUS_CTRL		0x0020c
++
++#define CLK_SLOW_SYS_RESUS_CTRL		0x00214
++
++#define FC0_REF_KHZ			0x0021c
++#define FC0_MIN_KHZ			0x00220
++#define FC0_MAX_KHZ			0x00224
++#define FC0_DELAY			0x00228
++#define FC0_INTERVAL			0x0022c
++#define FC0_SRC				0x00230
++#define FC0_STATUS			0x00234
++#define FC0_RESULT			0x00238
++#define FC_SIZE				0x20
++#define FC_COUNT			8
++#define FC_NUM(idx, off)		((idx) * 32 + (off))
++
++#define AUX_SEL				1
++
++#define VIDEO_CLOCKS_OFFSET		0x4000
++#define VIDEO_CLK_VEC_CTRL		(VIDEO_CLOCKS_OFFSET + 0x0000)
++#define VIDEO_CLK_VEC_DIV_INT		(VIDEO_CLOCKS_OFFSET + 0x0004)
++#define VIDEO_CLK_VEC_SEL		(VIDEO_CLOCKS_OFFSET + 0x000c)
++#define VIDEO_CLK_DPI_CTRL		(VIDEO_CLOCKS_OFFSET + 0x0010)
++#define VIDEO_CLK_DPI_DIV_INT		(VIDEO_CLOCKS_OFFSET + 0x0014)
++#define VIDEO_CLK_DPI_SEL		(VIDEO_CLOCKS_OFFSET + 0x001c)
++#define VIDEO_CLK_MIPI0_DPI_CTRL	(VIDEO_CLOCKS_OFFSET + 0x0020)
++#define VIDEO_CLK_MIPI0_DPI_DIV_INT	(VIDEO_CLOCKS_OFFSET + 0x0024)
++#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC	(VIDEO_CLOCKS_OFFSET + 0x0028)
++#define VIDEO_CLK_MIPI0_DPI_SEL		(VIDEO_CLOCKS_OFFSET + 0x002c)
++#define VIDEO_CLK_MIPI1_DPI_CTRL	(VIDEO_CLOCKS_OFFSET + 0x0030)
++#define VIDEO_CLK_MIPI1_DPI_DIV_INT	(VIDEO_CLOCKS_OFFSET + 0x0034)
++#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC	(VIDEO_CLOCKS_OFFSET + 0x0038)
++#define VIDEO_CLK_MIPI1_DPI_SEL		(VIDEO_CLOCKS_OFFSET + 0x003c)
++
++#define DIV_INT_8BIT_MAX		0x000000ffu /* max divide for most clocks */
++#define DIV_INT_16BIT_MAX		0x0000ffffu /* max divide for GPx, PWM */
++#define DIV_INT_24BIT_MAX               0x00ffffffu /* max divide for CLK_SYS */
++
++#define FC0_STATUS_DONE			BIT(4)
++#define FC0_STATUS_RUNNING		BIT(8)
++#define FC0_RESULT_FRAC_SHIFT		5
++
++#define PLL_PRIM_DIV1_SHIFT		16
++#define PLL_PRIM_DIV1_MASK		0x00070000
++#define PLL_PRIM_DIV2_SHIFT		12
++#define PLL_PRIM_DIV2_MASK		0x00007000
++
++#define PLL_SEC_DIV_SHIFT		8
++#define PLL_SEC_DIV_WIDTH		5
++#define PLL_SEC_DIV_MASK		0x00001f00
++
++#define PLL_CS_LOCK			BIT(31)
++#define PLL_CS_REFDIV_SHIFT		0
++
++#define PLL_PWR_PD			BIT(0)
++#define PLL_PWR_DACPD			BIT(1)
++#define PLL_PWR_DSMPD			BIT(2)
++#define PLL_PWR_POSTDIVPD		BIT(3)
++#define PLL_PWR_4PHASEPD		BIT(4)
++#define PLL_PWR_VCOPD			BIT(5)
++#define PLL_PWR_MASK			0x0000003f
++
++#define PLL_SEC_RST			BIT(16)
++#define PLL_SEC_IMPL			BIT(31)
++
++/* PLL phase output for both PRI and SEC */
++#define PLL_PH_EN			BIT(4)
++#define PLL_PH_PHASE_SHIFT		0
++
++#define RP1_PLL_PHASE_0			0
++#define RP1_PLL_PHASE_90		1
++#define RP1_PLL_PHASE_180		2
++#define RP1_PLL_PHASE_270		3
++
++/* Clock fields for all clocks */
++#define CLK_CTRL_ENABLE			BIT(11)
++#define CLK_CTRL_AUXSRC_MASK		0x000003e0
++#define CLK_CTRL_AUXSRC_SHIFT		5
++#define CLK_CTRL_SRC_SHIFT		0
++#define CLK_DIV_FRAC_BITS		16
++
++#define KHz				1000
++#define MHz				(KHz * KHz)
++#define LOCK_TIMEOUT_NS			100000000
++#define FC_TIMEOUT_NS			100000000
++
++#define MAX_CLK_PARENTS	8
++
++#define MEASURE_CLOCK_RATE
++const char * const fc0_ref_clk_name = "clk_slow_sys";
++
++#define ABS_DIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
++#define DIV_U64_NEAREST(a, b) div_u64(((a) + ((b) >> 1)), (b))
++
++/*
++ * Names of the reference clock for the pll cores.  This name must match
++ * the DT reference clock-output-name.
++ */
++static const char *const ref_clock = "xosc";
++
++/*
++ * Secondary PLL channel output divider table.
++ * Divider values range from 8 to 19.
++ * Invalid values default to 19
++ */
++static const struct clk_div_table pll_sec_div_table[] = {
++	{ 0x00, 19 },
++	{ 0x01, 19 },
++	{ 0x02, 19 },
++	{ 0x03, 19 },
++	{ 0x04, 19 },
++	{ 0x05, 19 },
++	{ 0x06, 19 },
++	{ 0x07, 19 },
++	{ 0x08,  8 },
++	{ 0x09,  9 },
++	{ 0x0a, 10 },
++	{ 0x0b, 11 },
++	{ 0x0c, 12 },
++	{ 0x0d, 13 },
++	{ 0x0e, 14 },
++	{ 0x0f, 15 },
++	{ 0x10, 16 },
++	{ 0x11, 17 },
++	{ 0x12, 18 },
++	{ 0x13, 19 },
++	{ 0x14, 19 },
++	{ 0x15, 19 },
++	{ 0x16, 19 },
++	{ 0x17, 19 },
++	{ 0x18, 19 },
++	{ 0x19, 19 },
++	{ 0x1a, 19 },
++	{ 0x1b, 19 },
++	{ 0x1c, 19 },
++	{ 0x1d, 19 },
++	{ 0x1e, 19 },
++	{ 0x1f, 19 },
++	{ 0 }
++};
++
++struct rp1_clockman {
++	struct device *dev;
++	void __iomem *regs;
++	spinlock_t regs_lock; /* spinlock for all clocks */
++
++	/* Must be last */
++	struct clk_hw_onecell_data onecell;
++};
++
++struct rp1_pll_core_data {
++	const char *name;
++	u32 cs_reg;
++	u32 pwr_reg;
++	u32 fbdiv_int_reg;
++	u32 fbdiv_frac_reg;
++	unsigned long flags;
++	u32 fc0_src;
++};
++
++struct rp1_pll_data {
++	const char *name;
++	const char *source_pll;
++	u32 ctrl_reg;
++	unsigned long flags;
++	u32 fc0_src;
++};
++
++struct rp1_pll_ph_data {
++	const char *name;
++	const char *source_pll;
++	unsigned int phase;
++	unsigned int fixed_divider;
++	u32 ph_reg;
++	unsigned long flags;
++	u32 fc0_src;
++};
++
++struct rp1_pll_divider_data {
++	const char *name;
++	const char *source_pll;
++	u32 sec_reg;
++	unsigned long flags;
++	u32 fc0_src;
++};
++
++struct rp1_clock_data {
++	const char *name;
++	const char *const parents[MAX_CLK_PARENTS];
++	int num_std_parents;
++	int num_aux_parents;
++	unsigned long flags;
++	u32 clk_src_mask;
++	u32 ctrl_reg;
++	u32 div_int_reg;
++	u32 div_frac_reg;
++	u32 sel_reg;
++	u32 div_int_max;
++	u32 fc0_src;
++};
++
++struct rp1_pll_core {
++	struct clk_hw hw;
++	struct rp1_clockman *clockman;
++	const struct rp1_pll_core_data *data;
++	unsigned long cached_rate;
++};
++
++struct rp1_pll {
++	struct clk_hw hw;
++	struct clk_divider div;
++	struct rp1_clockman *clockman;
++	const struct rp1_pll_data *data;
++	unsigned long cached_rate;
++};
++
++struct rp1_pll_ph {
++	struct clk_hw hw;
++	struct rp1_clockman *clockman;
++	const struct rp1_pll_ph_data *data;
++};
++
++struct rp1_clock {
++	struct clk_hw hw;
++	struct rp1_clockman *clockman;
++	const struct rp1_clock_data *data;
++	unsigned long cached_rate;
++};
++
++static void rp1_debugfs_regset(struct rp1_clockman *clockman, u32 base,
++			       const struct debugfs_reg32 *regs,
++			       size_t nregs, struct dentry *dentry)
++{
++	struct debugfs_regset32 *regset;
++
++	regset = devm_kzalloc(clockman->dev, sizeof(*regset), GFP_KERNEL);
++	if (!regset)
++		return;
++
++	regset->regs = regs;
++	regset->nregs = nregs;
++	regset->base = clockman->regs + base;
++
++	debugfs_create_regset32("regdump", 0444, dentry, regset);
++}
++
++static inline u32 set_register_field(u32 reg, u32 val, u32 mask, u32 shift)
++{
++	reg &= ~mask;
++	reg |= (val << shift) & mask;
++	return reg;
++}
++
++static inline
++void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val)
++{
++	writel(val, clockman->regs + reg);
++}
++
++static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg)
++{
++	return readl(clockman->regs + reg);
++}
++
++#ifdef MEASURE_CLOCK_RATE
++static unsigned long clockman_measure_clock(struct rp1_clockman *clockman,
++					    const char *clk_name,
++					    unsigned int fc0_src)
++{
++	struct clk *ref_clk = __clk_lookup(fc0_ref_clk_name);
++	unsigned long result;
++	ktime_t timeout;
++	unsigned int fc_idx, fc_offset, fc_src;
++
++	fc_idx = fc0_src / 32;
++	fc_src = fc0_src % 32;
++
++	/* fc_src == 0 is invalid. */
++	if (!fc_src || fc_idx >= FC_COUNT)
++		return 0;
++
++	fc_offset = fc_idx * FC_SIZE;
++
++	/* Ensure the frequency counter is idle. */
++	timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS);
++	while (clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_RUNNING) {
++		if (ktime_after(ktime_get(), timeout)) {
++			dev_err(clockman->dev, "%s: FC0 busy timeout\n",
++				clk_name);
++			return 0;
++		}
++		cpu_relax();
++	}
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, fc_offset + FC0_REF_KHZ,
++		       clk_get_rate(ref_clk) / KHz);
++	clockman_write(clockman, fc_offset + FC0_MIN_KHZ, 0);
++	clockman_write(clockman, fc_offset + FC0_MAX_KHZ, 0x1ffffff);
++	clockman_write(clockman, fc_offset + FC0_INTERVAL, 8);
++	clockman_write(clockman, fc_offset + FC0_DELAY, 7);
++	clockman_write(clockman, fc_offset + FC0_SRC, fc_src);
++	spin_unlock(&clockman->regs_lock);
++
++	/* Ensure the frequency counter is idle. */
++	timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS);
++	while (!(clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_DONE)) {
++		if (ktime_after(ktime_get(), timeout)) {
++			dev_err(clockman->dev, "%s: FC0 wait timeout\n",
++				clk_name);
++			return 0;
++		}
++		cpu_relax();
++	}
++
++	result = clockman_read(clockman, fc_offset + FC0_RESULT);
++
++	/* Disable FC0 */
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, fc_offset + FC0_SRC, 0);
++	spin_unlock(&clockman->regs_lock);
++
++	return result;
++}
++#endif
++
++static int rp1_pll_core_is_on(struct clk_hw *hw)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++	u32 pwr = clockman_read(clockman, data->pwr_reg);
++
++	return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD);
++}
++
++static int rp1_pll_core_on(struct clk_hw *hw)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++	u32 fbdiv_frac;
++	ktime_t timeout;
++
++	spin_lock(&clockman->regs_lock);
++
++	if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) {
++		/* Reset to a known state. */
++		clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK);
++		clockman_write(clockman, data->fbdiv_int_reg, 20);
++		clockman_write(clockman, data->fbdiv_frac_reg, 0);
++		clockman_write(clockman, data->cs_reg, 1 << PLL_CS_REFDIV_SHIFT);
++	}
++
++	/* Come out of reset. */
++	fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
++	clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
++	spin_unlock(&clockman->regs_lock);
++
++	/* Wait for the PLL to lock. */
++	timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
++	while (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) {
++		if (ktime_after(ktime_get(), timeout)) {
++			dev_err(clockman->dev, "%s: can't lock PLL\n",
++				clk_hw_get_name(hw));
++			return -ETIMEDOUT;
++		}
++		cpu_relax();
++	}
++
++	return 0;
++}
++
++static void rp1_pll_core_off(struct clk_hw *hw)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->pwr_reg, 0);
++	spin_unlock(&clockman->regs_lock);
++}
++
++static inline unsigned long get_pll_core_divider(struct clk_hw *hw,
++						 unsigned long rate,
++						 unsigned long parent_rate,
++						 u32 *div_int, u32 *div_frac)
++{
++	unsigned long calc_rate;
++	u32 fbdiv_int, fbdiv_frac;
++	u64 div_fp64; /* 32.32 fixed point fraction. */
++
++	/* Factor of reference clock to VCO frequency. */
++	div_fp64 = (u64)(rate) << 32;
++	div_fp64 = DIV_U64_NEAREST(div_fp64, parent_rate);
++
++	/* Round the fractional component at 24 bits. */
++	div_fp64 += 1 << (32 - 24 - 1);
++
++	fbdiv_int = div_fp64 >> 32;
++	fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff;
++
++	calc_rate =
++		((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24;
++
++	*div_int = fbdiv_int;
++	*div_frac = fbdiv_frac;
++
++	return calc_rate;
++}
++
++static int rp1_pll_core_set_rate(struct clk_hw *hw,
++				 unsigned long rate, unsigned long parent_rate)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++	unsigned long calc_rate;
++	u32 fbdiv_int, fbdiv_frac;
++
++	// todo: is this needed??
++	//rp1_pll_off(hw);
++
++	/* Disable dividers to start with. */
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->fbdiv_int_reg, 0);
++	clockman_write(clockman, data->fbdiv_frac_reg, 0);
++	spin_unlock(&clockman->regs_lock);
++
++	calc_rate = get_pll_core_divider(hw, rate, parent_rate,
++					 &fbdiv_int, &fbdiv_frac);
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
++	clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int);
++	clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac);
++	spin_unlock(&clockman->regs_lock);
++
++	/* Check that reference frequency is no greater than VCO / 16. */
++	BUG_ON(parent_rate > (rate / 16));
++
++	pll_core->cached_rate = calc_rate;
++
++	spin_lock(&clockman->regs_lock);
++	/* Don't need to divide ref unless parent_rate > (output freq / 16) */
++	clockman_write(clockman, data->cs_reg,
++		       clockman_read(clockman, data->cs_reg) |
++				     (1 << PLL_CS_REFDIV_SHIFT));
++	spin_unlock(&clockman->regs_lock);
++
++	return 0;
++}
++
++static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw,
++					      unsigned long parent_rate)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++	u32 fbdiv_int, fbdiv_frac;
++	unsigned long calc_rate;
++
++	fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg);
++	fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
++	calc_rate =
++		((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24;
++
++	return calc_rate;
++}
++
++static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate,
++				    unsigned long *parent_rate)
++{
++	u32 fbdiv_int, fbdiv_frac;
++	long calc_rate;
++
++	calc_rate = get_pll_core_divider(hw, rate, *parent_rate,
++					 &fbdiv_int, &fbdiv_frac);
++	return calc_rate;
++}
++
++static void rp1_pll_core_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++	struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++	struct rp1_clockman *clockman = pll_core->clockman;
++	const struct rp1_pll_core_data *data = pll_core->data;
++	struct debugfs_reg32 *regs;
++
++	regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL);
++	if (!regs)
++		return;
++
++	regs[0].name = "cs";
++	regs[0].offset = data->cs_reg;
++	regs[1].name = "pwr";
++	regs[1].offset = data->pwr_reg;
++	regs[2].name = "fbdiv_int";
++	regs[2].offset = data->fbdiv_int_reg;
++	regs[3].name = "fbdiv_frac";
++	regs[3].offset = data->fbdiv_frac_reg;
++
++	rp1_debugfs_regset(clockman, 0, regs, 4, dentry);
++}
++
++static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate,
++				  u32 *divider1, u32 *divider2)
++{
++	unsigned int div1, div2;
++	unsigned int best_div1 = 7, best_div2 = 7;
++	unsigned long best_rate_diff =
++		ABS_DIFF(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate);
++	long rate_diff, calc_rate;
++
++	for (div1 = 1; div1 <= 7; div1++) {
++		for (div2 = 1; div2 <= div1; div2++) {
++			calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2);
++			rate_diff = ABS_DIFF(calc_rate, rate);
++
++			if (calc_rate == rate) {
++				best_div1 = div1;
++				best_div2 = div2;
++				goto done;
++			} else if (rate_diff < best_rate_diff) {
++				best_div1 = div1;
++				best_div2 = div2;
++				best_rate_diff = rate_diff;
++			}
++		}
++	}
++
++done:
++	*divider1 = best_div1;
++	*divider2 = best_div2;
++}
++
++static int rp1_pll_set_rate(struct clk_hw *hw,
++			    unsigned long rate, unsigned long parent_rate)
++{
++	struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++	struct rp1_clockman *clockman = pll->clockman;
++	const struct rp1_pll_data *data = pll->data;
++	u32 prim, prim_div1, prim_div2;
++
++	get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2);
++
++	spin_lock(&clockman->regs_lock);
++	prim = clockman_read(clockman, data->ctrl_reg);
++	prim = set_register_field(prim, prim_div1, PLL_PRIM_DIV1_MASK,
++				  PLL_PRIM_DIV1_SHIFT);
++	prim = set_register_field(prim, prim_div2, PLL_PRIM_DIV2_MASK,
++				  PLL_PRIM_DIV2_SHIFT);
++	clockman_write(clockman, data->ctrl_reg, prim);
++	spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++	clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
++					 unsigned long parent_rate)
++{
++	struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++	struct rp1_clockman *clockman = pll->clockman;
++	const struct rp1_pll_data *data = pll->data;
++	u32 prim, prim_div1, prim_div2;
++
++	prim = clockman_read(clockman, data->ctrl_reg);
++	prim_div1 = (prim & PLL_PRIM_DIV1_MASK) >> PLL_PRIM_DIV1_SHIFT;
++	prim_div2 = (prim & PLL_PRIM_DIV2_MASK) >> PLL_PRIM_DIV2_SHIFT;
++
++	if (!prim_div1 || !prim_div2) {
++		dev_err(clockman->dev, "%s: (%s) zero divider value\n",
++			__func__, data->name);
++		return 0;
++	}
++
++	return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2);
++}
++
++static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate,
++			       unsigned long *parent_rate)
++{
++	u32 div1, div2;
++
++	get_pll_prim_dividers(rate, *parent_rate, &div1, &div2);
++
++	return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2);
++}
++
++static void rp1_pll_debug_init(struct clk_hw *hw,
++			       struct dentry *dentry)
++{
++	struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++	struct rp1_clockman *clockman = pll->clockman;
++	const struct rp1_pll_data *data = pll->data;
++	struct debugfs_reg32 *regs;
++
++	regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++	if (!regs)
++		return;
++
++	regs[0].name = "prim";
++	regs[0].offset = data->ctrl_reg;
++
++	rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_pll_ph_is_on(struct clk_hw *hw)
++{
++	struct rp1_pll_ph *pll = container_of(hw, struct rp1_pll_ph, hw);
++	struct rp1_clockman *clockman = pll->clockman;
++	const struct rp1_pll_ph_data *data = pll->data;
++
++	return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN);
++}
++
++static int rp1_pll_ph_on(struct clk_hw *hw)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	struct rp1_clockman *clockman = pll_ph->clockman;
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++	u32 ph_reg;
++
++	/* todo: ensure pri/sec is enabled! */
++	spin_lock(&clockman->regs_lock);
++	ph_reg = clockman_read(clockman, data->ph_reg);
++	ph_reg |= data->phase << PLL_PH_PHASE_SHIFT;
++	ph_reg |= PLL_PH_EN;
++	clockman_write(clockman, data->ph_reg, ph_reg);
++	spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++	clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static void rp1_pll_ph_off(struct clk_hw *hw)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	struct rp1_clockman *clockman = pll_ph->clockman;
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->ph_reg,
++		       clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN);
++	spin_unlock(&clockman->regs_lock);
++}
++
++static int rp1_pll_ph_set_rate(struct clk_hw *hw,
++			       unsigned long rate, unsigned long parent_rate)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++	struct rp1_clockman *clockman = pll_ph->clockman;
++
++	/* Nothing really to do here! */
++	WARN_ON(data->fixed_divider != 1 && data->fixed_divider != 2);
++	WARN_ON(rate != parent_rate / data->fixed_divider);
++
++#ifdef MEASURE_CLOCK_RATE
++	if (rp1_pll_ph_is_on(hw))
++		clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw,
++					    unsigned long parent_rate)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++
++	return parent_rate / data->fixed_divider;
++}
++
++static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate,
++				  unsigned long *parent_rate)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++
++	return *parent_rate / data->fixed_divider;
++}
++
++static void rp1_pll_ph_debug_init(struct clk_hw *hw,
++				  struct dentry *dentry)
++{
++	struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++	const struct rp1_pll_ph_data *data = pll_ph->data;
++	struct rp1_clockman *clockman = pll_ph->clockman;
++	struct debugfs_reg32 *regs;
++
++	regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++	if (!regs)
++		return;
++
++	regs[0].name = "ph_reg";
++	regs[0].offset = data->ph_reg;
++
++	rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_pll_divider_is_on(struct clk_hw *hw)
++{
++	struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++	struct rp1_clockman *clockman = divider->clockman;
++	const struct rp1_pll_data *data = divider->data;
++
++	return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST);
++}
++
++static int rp1_pll_divider_on(struct clk_hw *hw)
++{
++	struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++	struct rp1_clockman *clockman = divider->clockman;
++	const struct rp1_pll_data *data = divider->data;
++
++	spin_lock(&clockman->regs_lock);
++	/* Check the implementation bit is set! */
++	WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL));
++	clockman_write(clockman, data->ctrl_reg,
++		       clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST);
++	spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++	clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static void rp1_pll_divider_off(struct clk_hw *hw)
++{
++	struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++	struct rp1_clockman *clockman = divider->clockman;
++	const struct rp1_pll_data *data = divider->data;
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->ctrl_reg, PLL_SEC_RST);
++	spin_unlock(&clockman->regs_lock);
++}
++
++static int rp1_pll_divider_set_rate(struct clk_hw *hw,
++				    unsigned long rate,
++				    unsigned long parent_rate)
++{
++	struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++	struct rp1_clockman *clockman = divider->clockman;
++	const struct rp1_pll_data *data = divider->data;
++	u32 div, sec;
++
++	div = DIV_ROUND_UP_ULL(parent_rate, rate);
++	div = clamp(div, 8u, 19u);
++
++	spin_lock(&clockman->regs_lock);
++	sec = clockman_read(clockman, data->ctrl_reg);
++	sec = set_register_field(sec, div, PLL_SEC_DIV_MASK, PLL_SEC_DIV_SHIFT);
++
++	/* Must keep the divider in reset to change the value. */
++	sec |= PLL_SEC_RST;
++	clockman_write(clockman, data->ctrl_reg, sec);
++
++	// todo: must sleep 10 pll vco cycles
++	sec &= ~PLL_SEC_RST;
++	clockman_write(clockman, data->ctrl_reg, sec);
++	spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++	if (rp1_pll_divider_is_on(hw))
++		clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw,
++						 unsigned long parent_rate)
++{
++	return clk_divider_ops.recalc_rate(hw, parent_rate);
++}
++
++static long rp1_pll_divider_round_rate(struct clk_hw *hw,
++				       unsigned long rate,
++				       unsigned long *parent_rate)
++{
++	return clk_divider_ops.round_rate(hw, rate, parent_rate);
++}
++
++static void rp1_pll_divider_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++	struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++	struct rp1_clockman *clockman = divider->clockman;
++	const struct rp1_pll_data *data = divider->data;
++	struct debugfs_reg32 *regs;
++
++	regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++	if (!regs)
++		return;
++
++	regs[0].name = "sec";
++	regs[0].offset = data->ctrl_reg;
++
++	rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_clock_is_on(struct clk_hw *hw)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++
++	return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE);
++}
++
++static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw,
++					   unsigned long parent_rate)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++	u64 calc_rate;
++	u64 div;
++
++	u32 frac;
++
++	div = clockman_read(clockman, data->div_int_reg);
++	frac = (data->div_frac_reg != 0) ?
++		clockman_read(clockman, data->div_frac_reg) : 0;
++
++	/* If the integer portion of the divider is 0, treat it as 2^16 */
++	if (!div)
++		div = 1 << 16;
++
++	div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS));
++
++	calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS;
++	calc_rate = div64_u64(calc_rate, div);
++
++	return calc_rate;
++}
++
++static int rp1_clock_on(struct clk_hw *hw)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->ctrl_reg,
++		       clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE);
++	spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++	clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static void rp1_clock_off(struct clk_hw *hw)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++
++	spin_lock(&clockman->regs_lock);
++	clockman_write(clockman, data->ctrl_reg,
++		       clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE);
++	spin_unlock(&clockman->regs_lock);
++}
++
++static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate,
++				const struct rp1_clock_data *data)
++{
++	u64 div;
++
++	/*
++	 * Due to earlier rounding, calculated parent_rate may differ from
++	 * expected value. Don't fail on a small discrepancy near unity divide.
++	 */
++	if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS))
++		return 0;
++
++	/*
++	 * Always express div in fixed-point format for fractional division;
++	 * If no fractional divider is present, the fraction part will be zero.
++	 */
++	if (data->div_frac_reg) {
++		div = (u64)parent_rate << CLK_DIV_FRAC_BITS;
++		div = DIV_U64_NEAREST(div, rate);
++	} else {
++		div = DIV_U64_NEAREST(parent_rate, rate);
++		div <<= CLK_DIV_FRAC_BITS;
++	}
++
++	div = clamp(div,
++		    1ull << CLK_DIV_FRAC_BITS,
++		    (u64)data->div_int_max << CLK_DIV_FRAC_BITS);
++
++	return div;
++}
++
++static u8 rp1_clock_get_parent(struct clk_hw *hw)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++	u32 sel, ctrl;
++	u8 parent;
++
++	/* Sel is one-hot, so find the first bit set */
++	sel = clockman_read(clockman, data->sel_reg);
++	parent = ffs(sel) - 1;
++
++	/* sel == 0 implies the parent clock is not enabled yet. */
++	if (!sel) {
++		/* Read the clock src from the CTRL register instead */
++		ctrl = clockman_read(clockman, data->ctrl_reg);
++		parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT;
++	}
++
++	if (parent >= data->num_std_parents)
++		parent = AUX_SEL;
++
++	if (parent == AUX_SEL) {
++		/*
++		 * Clock parent is an auxiliary source, so get the parent from
++		 * the AUXSRC register field.
++		 */
++		ctrl = clockman_read(clockman, data->ctrl_reg);
++		parent = (ctrl & CLK_CTRL_AUXSRC_MASK) >> CLK_CTRL_AUXSRC_SHIFT;
++		parent += data->num_std_parents;
++	}
++
++	return parent;
++}
++
++static int rp1_clock_set_parent(struct clk_hw *hw, u8 index)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++	u32 ctrl, sel;
++
++	spin_lock(&clockman->regs_lock);
++	ctrl = clockman_read(clockman, data->ctrl_reg);
++
++	if (index >= data->num_std_parents) {
++		/* This is an aux source request */
++		if (index >= data->num_std_parents + data->num_aux_parents)
++			return -EINVAL;
++
++		/* Select parent from aux list */
++		ctrl = set_register_field(ctrl, index - data->num_std_parents,
++					  CLK_CTRL_AUXSRC_MASK,
++					  CLK_CTRL_AUXSRC_SHIFT);
++		/* Set src to aux list */
++		ctrl = set_register_field(ctrl, AUX_SEL, data->clk_src_mask,
++					  CLK_CTRL_SRC_SHIFT);
++	} else {
++		ctrl = set_register_field(ctrl, index, data->clk_src_mask,
++					  CLK_CTRL_SRC_SHIFT);
++	}
++
++	clockman_write(clockman, data->ctrl_reg, ctrl);
++	spin_unlock(&clockman->regs_lock);
++
++	sel = rp1_clock_get_parent(hw);
++	WARN(sel != index, "(%s): Parent index req %u returned back %u\n",
++	     data->name, index, sel);
++
++	return 0;
++}
++
++static int rp1_clock_set_rate_and_parent(struct clk_hw *hw,
++					 unsigned long rate,
++					 unsigned long parent_rate,
++					 u8 parent)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++	u32 div = rp1_clock_choose_div(rate, parent_rate, data);
++
++	WARN(rate > 4000000000ll, "rate is -ve (%d)\n", (int)rate);
++
++	if (WARN(!div,
++		 "clk divider calculated as 0! (%s, rate %ld, parent rate %ld)\n",
++		 data->name, rate, parent_rate))
++		div = 1 << CLK_DIV_FRAC_BITS;
++
++	spin_lock(&clockman->regs_lock);
++
++	clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS);
++	if (data->div_frac_reg)
++		clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS));
++
++	spin_unlock(&clockman->regs_lock);
++
++	if (parent != 0xff)
++		rp1_clock_set_parent(hw, parent);
++
++#ifdef MEASURE_CLOCK_RATE
++	if (rp1_clock_is_on(hw))
++		clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++	return 0;
++}
++
++static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate,
++			      unsigned long parent_rate)
++{
++	return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
++}
++
++static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
++					   int parent_idx,
++					   unsigned long rate,
++					   unsigned long *prate,
++					   unsigned long *calc_rate)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	const struct rp1_clock_data *data = clock->data;
++	struct clk_hw *parent;
++	u32 div;
++	u64 tmp;
++
++	parent = clk_hw_get_parent_by_index(hw, parent_idx);
++	*prate = clk_hw_get_rate(parent);
++	div = rp1_clock_choose_div(rate, *prate, data);
++
++	if (!div) {
++		*calc_rate = 0;
++		return;
++	}
++
++	/* Recalculate to account for rounding errors */
++	tmp = (u64)*prate << CLK_DIV_FRAC_BITS;
++	tmp = div_u64(tmp, div);
++	*calc_rate = tmp;
++}
++
++static int rp1_clock_determine_rate(struct clk_hw *hw,
++				    struct clk_rate_request *req)
++{
++	struct clk_hw *parent, *best_parent = NULL;
++	unsigned long best_rate = 0;
++	unsigned long best_prate = 0;
++	unsigned long best_rate_diff = ULONG_MAX;
++	unsigned long prate, calc_rate;
++	size_t i;
++
++	/*
++	 * If the NO_REPARENT flag is set, try to use existing parent.
++	 */
++	if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) {
++		i = rp1_clock_get_parent(hw);
++		parent = clk_hw_get_parent_by_index(hw, i);
++		if (parent) {
++			rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
++						       &calc_rate);
++			if (calc_rate > 0) {
++				req->best_parent_hw = parent;
++				req->best_parent_rate = prate;
++				req->rate = calc_rate;
++				return 0;
++			}
++		}
++	}
++
++	/*
++	 * Select parent clock that results in the closest rate (lower or
++	 * higher)
++	 */
++	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++		parent = clk_hw_get_parent_by_index(hw, i);
++		if (!parent)
++			continue;
++
++		rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
++					       &calc_rate);
++
++		if (ABS_DIFF(calc_rate, req->rate) < best_rate_diff) {
++			best_parent = parent;
++			best_prate = prate;
++			best_rate = calc_rate;
++			best_rate_diff = ABS_DIFF(calc_rate, req->rate);
++
++			if (best_rate_diff == 0)
++				break;
++		}
++	}
++
++	if (best_rate == 0)
++		return -EINVAL;
++
++	req->best_parent_hw = best_parent;
++	req->best_parent_rate = best_prate;
++	req->rate = best_rate;
++
++	return 0;
++}
++
++static void rp1_clk_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++	struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++	struct rp1_clockman *clockman = clock->clockman;
++	const struct rp1_clock_data *data = clock->data;
++	struct debugfs_reg32 *regs;
++	int i;
++
++	regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL);
++	if (!regs)
++		return;
++
++	i = 0;
++	regs[i].name = "ctrl";
++	regs[i++].offset = data->ctrl_reg;
++	regs[i].name = "div_int";
++	regs[i++].offset = data->div_int_reg;
++	regs[i].name = "div_frac";
++	regs[i++].offset = data->div_frac_reg;
++	regs[i].name = "sel";
++	regs[i++].offset = data->sel_reg;
++
++	rp1_debugfs_regset(clockman, 0, regs, i, dentry);
++}
++
++static const struct clk_ops rp1_pll_core_ops = {
++	.is_prepared = rp1_pll_core_is_on,
++	.prepare = rp1_pll_core_on,
++	.unprepare = rp1_pll_core_off,
++	.set_rate = rp1_pll_core_set_rate,
++	.recalc_rate = rp1_pll_core_recalc_rate,
++	.round_rate = rp1_pll_core_round_rate,
++	.debug_init = rp1_pll_core_debug_init,
++};
++
++static const struct clk_ops rp1_pll_ops = {
++	.set_rate = rp1_pll_set_rate,
++	.recalc_rate = rp1_pll_recalc_rate,
++	.round_rate = rp1_pll_round_rate,
++	.debug_init = rp1_pll_debug_init,
++};
++
++static const struct clk_ops rp1_pll_ph_ops = {
++	.is_prepared = rp1_pll_ph_is_on,
++	.prepare = rp1_pll_ph_on,
++	.unprepare = rp1_pll_ph_off,
++	.set_rate = rp1_pll_ph_set_rate,
++	.recalc_rate = rp1_pll_ph_recalc_rate,
++	.round_rate = rp1_pll_ph_round_rate,
++	.debug_init = rp1_pll_ph_debug_init,
++};
++
++static const struct clk_ops rp1_pll_divider_ops = {
++	.is_prepared = rp1_pll_divider_is_on,
++	.prepare = rp1_pll_divider_on,
++	.unprepare = rp1_pll_divider_off,
++	.set_rate = rp1_pll_divider_set_rate,
++	.recalc_rate = rp1_pll_divider_recalc_rate,
++	.round_rate = rp1_pll_divider_round_rate,
++	.debug_init = rp1_pll_divider_debug_init,
++};
++
++static const struct clk_ops rp1_clk_ops = {
++	.is_prepared = rp1_clock_is_on,
++	.prepare = rp1_clock_on,
++	.unprepare = rp1_clock_off,
++	.recalc_rate = rp1_clock_recalc_rate,
++	.get_parent = rp1_clock_get_parent,
++	.set_parent = rp1_clock_set_parent,
++	.set_rate_and_parent = rp1_clock_set_rate_and_parent,
++	.set_rate = rp1_clock_set_rate,
++	.determine_rate = rp1_clock_determine_rate,
++	.debug_init = rp1_clk_debug_init,
++};
++
++static bool rp1_clk_is_claimed(const char *name);
++
++static struct clk_hw *rp1_register_pll_core(struct rp1_clockman *clockman,
++					    const void *data)
++{
++	const struct rp1_pll_core_data *pll_core_data = data;
++	struct rp1_pll_core *pll_core;
++	struct clk_init_data init;
++	int ret;
++
++	memset(&init, 0, sizeof(init));
++
++	/* All of the PLL cores derive from the external oscillator. */
++	init.parent_names = &ref_clock;
++	init.num_parents = 1;
++	init.name = pll_core_data->name;
++	init.ops = &rp1_pll_core_ops;
++	init.flags = pll_core_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL;
++
++	pll_core = kzalloc(sizeof(*pll_core), GFP_KERNEL);
++	if (!pll_core)
++		return NULL;
++
++	pll_core->clockman = clockman;
++	pll_core->data = pll_core_data;
++	pll_core->hw.init = &init;
++
++	ret = devm_clk_hw_register(clockman->dev, &pll_core->hw);
++	if (ret) {
++		kfree(pll_core);
++		return NULL;
++	}
++
++	return &pll_core->hw;
++}
++
++static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman,
++				       const void *data)
++{
++	const struct rp1_pll_data *pll_data = data;
++	struct rp1_pll *pll;
++	struct clk_init_data init;
++	int ret;
++
++	memset(&init, 0, sizeof(init));
++
++	init.parent_names = &pll_data->source_pll;
++	init.num_parents = 1;
++	init.name = pll_data->name;
++	init.ops = &rp1_pll_ops;
++	init.flags = pll_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL;
++
++	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
++	if (!pll)
++		return NULL;
++
++	pll->clockman = clockman;
++	pll->data = pll_data;
++	pll->hw.init = &init;
++
++	ret = devm_clk_hw_register(clockman->dev, &pll->hw);
++	if (ret) {
++		kfree(pll);
++		return NULL;
++	}
++
++	return &pll->hw;
++}
++
++static struct clk_hw *rp1_register_pll_ph(struct rp1_clockman *clockman,
++					  const void *data)
++{
++	const struct rp1_pll_ph_data *ph_data = data;
++	struct rp1_pll_ph *ph;
++	struct clk_init_data init;
++	int ret;
++
++	memset(&init, 0, sizeof(init));
++
++	/* All of the PLLs derive from the external oscillator. */
++	init.parent_names = &ph_data->source_pll;
++	init.num_parents = 1;
++	init.name = ph_data->name;
++	init.ops = &rp1_pll_ph_ops;
++	init.flags = ph_data->flags | CLK_IGNORE_UNUSED;
++
++	ph = kzalloc(sizeof(*ph), GFP_KERNEL);
++	if (!ph)
++		return NULL;
++
++	ph->clockman = clockman;
++	ph->data = ph_data;
++	ph->hw.init = &init;
++
++	ret = devm_clk_hw_register(clockman->dev, &ph->hw);
++	if (ret) {
++		kfree(ph);
++		return NULL;
++	}
++
++	return &ph->hw;
++}
++
++static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman,
++					       const void *data)
++{
++	const struct rp1_pll_data *divider_data = data;
++	struct rp1_pll *divider;
++	struct clk_init_data init;
++	int ret;
++
++	memset(&init, 0, sizeof(init));
++
++	init.parent_names = &divider_data->source_pll;
++	init.num_parents = 1;
++	init.name = divider_data->name;
++	init.ops = &rp1_pll_divider_ops;
++	init.flags = divider_data->flags | CLK_IGNORE_UNUSED;
++
++	divider = devm_kzalloc(clockman->dev, sizeof(*divider), GFP_KERNEL);
++	if (!divider)
++		return NULL;
++
++	divider->div.reg = clockman->regs + divider_data->ctrl_reg;
++	divider->div.shift = PLL_SEC_DIV_SHIFT;
++	divider->div.width = PLL_SEC_DIV_WIDTH;
++	divider->div.flags = CLK_DIVIDER_ROUND_CLOSEST;
++	divider->div.lock = &clockman->regs_lock;
++	divider->div.hw.init = &init;
++	divider->div.table = pll_sec_div_table;
++
++	if (!rp1_clk_is_claimed(divider_data->source_pll))
++		init.flags |= CLK_IS_CRITICAL;
++	if (!rp1_clk_is_claimed(divider_data->name))
++		divider->div.flags |= CLK_IS_CRITICAL;
++
++	divider->clockman = clockman;
++	divider->data = divider_data;
++
++	ret = devm_clk_hw_register(clockman->dev, &divider->div.hw);
++	if (ret)
++		return ERR_PTR(ret);
++
++	return &divider->div.hw;
++}
++
++static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman,
++					 const void *data)
++{
++	const struct rp1_clock_data *clock_data = data;
++	struct rp1_clock *clock;
++	struct clk_init_data init;
++	int ret;
++
++	BUG_ON(MAX_CLK_PARENTS <
++	       clock_data->num_std_parents + clock_data->num_aux_parents);
++	/* There must be a gap for the AUX selector */
++	BUG_ON((clock_data->num_std_parents > AUX_SEL) &&
++	       strcmp("-", clock_data->parents[AUX_SEL]));
++
++	memset(&init, 0, sizeof(init));
++	init.parent_names = clock_data->parents;
++	init.num_parents =
++		clock_data->num_std_parents + clock_data->num_aux_parents;
++	init.name = clock_data->name;
++	init.flags = clock_data->flags | CLK_IGNORE_UNUSED;
++	init.ops = &rp1_clk_ops;
++
++	clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL);
++	if (!clock)
++		return NULL;
++
++	clock->clockman = clockman;
++	clock->data = clock_data;
++	clock->hw.init = &init;
++
++	ret = devm_clk_hw_register(clockman->dev, &clock->hw);
++	if (ret)
++		return ERR_PTR(ret);
++
++	return &clock->hw;
++}
++
++struct rp1_clk_desc {
++	struct clk_hw *(*clk_register)(struct rp1_clockman *clockman,
++				       const void *data);
++	const void *data;
++};
++
++/* Assignment helper macros for different clock types. */
++#define _REGISTER(f, ...) { .clk_register = f, .data = __VA_ARGS__ }
++
++#define REGISTER_PLL_CORE(...)	_REGISTER(&rp1_register_pll_core,	\
++					  &(struct rp1_pll_core_data)	\
++					  {__VA_ARGS__})
++
++#define REGISTER_PLL(...)	_REGISTER(&rp1_register_pll,		\
++					  &(struct rp1_pll_data)		\
++					  {__VA_ARGS__})
++
++#define REGISTER_PLL_PH(...)	_REGISTER(&rp1_register_pll_ph,		\
++					  &(struct rp1_pll_ph_data)	\
++					  {__VA_ARGS__})
++
++#define REGISTER_PLL_DIV(...)	_REGISTER(&rp1_register_pll_divider,	\
++					  &(struct rp1_pll_data)	\
++					  {__VA_ARGS__})
++
++#define REGISTER_CLK(...)	_REGISTER(&rp1_register_clock,		\
++					  &(struct rp1_clock_data)	\
++					  {__VA_ARGS__})
++
++static const struct rp1_clk_desc clk_desc_array[] = {
++	[RP1_PLL_SYS_CORE] = REGISTER_PLL_CORE(
++				.name = "pll_sys_core",
++				.cs_reg = PLL_SYS_CS,
++				.pwr_reg = PLL_SYS_PWR,
++				.fbdiv_int_reg = PLL_SYS_FBDIV_INT,
++				.fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC,
++				),
++
++	[RP1_PLL_AUDIO_CORE] = REGISTER_PLL_CORE(
++				.name = "pll_audio_core",
++				.cs_reg = PLL_AUDIO_CS,
++				.pwr_reg = PLL_AUDIO_PWR,
++				.fbdiv_int_reg = PLL_AUDIO_FBDIV_INT,
++				.fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC,
++				),
++
++	[RP1_PLL_VIDEO_CORE] = REGISTER_PLL_CORE(
++				.name = "pll_video_core",
++				.cs_reg = PLL_VIDEO_CS,
++				.pwr_reg = PLL_VIDEO_PWR,
++				.fbdiv_int_reg = PLL_VIDEO_FBDIV_INT,
++				.fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC,
++				),
++
++	[RP1_PLL_SYS] = REGISTER_PLL(
++				.name = "pll_sys",
++				.source_pll = "pll_sys_core",
++				.ctrl_reg = PLL_SYS_PRIM,
++				.fc0_src = FC_NUM(0, 2),
++				),
++
++	[RP1_PLL_AUDIO] = REGISTER_PLL(
++				.name = "pll_audio",
++				.source_pll = "pll_audio_core",
++				.ctrl_reg = PLL_AUDIO_PRIM,
++				.fc0_src = FC_NUM(4, 2),
++				),
++
++	[RP1_PLL_VIDEO] = REGISTER_PLL(
++				.name = "pll_video",
++				.source_pll = "pll_video_core",
++				.ctrl_reg = PLL_VIDEO_PRIM,
++				.fc0_src = FC_NUM(3, 2),
++				),
++
++	[RP1_PLL_SYS_PRI_PH] = REGISTER_PLL_PH(
++				.name = "pll_sys_pri_ph",
++				.source_pll = "pll_sys",
++				.ph_reg = PLL_SYS_PRIM,
++				.fixed_divider = 2,
++				.phase = RP1_PLL_PHASE_0,
++				.fc0_src = FC_NUM(1, 2),
++				),
++
++	[RP1_PLL_AUDIO_PRI_PH] = REGISTER_PLL_PH(
++				.name = "pll_audio_pri_ph",
++				.source_pll = "pll_audio",
++				.ph_reg = PLL_AUDIO_PRIM,
++				.fixed_divider = 2,
++				.phase = RP1_PLL_PHASE_0,
++				.fc0_src = FC_NUM(5, 1),
++				),
++
++	[RP1_PLL_SYS_SEC] = REGISTER_PLL_DIV(
++				.name = "pll_sys_sec",
++				.source_pll = "pll_sys_core",
++				.ctrl_reg = PLL_SYS_SEC,
++				.fc0_src = FC_NUM(2, 2),
++				),
++
++	[RP1_PLL_AUDIO_SEC] = REGISTER_PLL_DIV(
++				.name = "pll_audio_sec",
++				.source_pll = "pll_audio_core",
++				.ctrl_reg = PLL_AUDIO_SEC,
++				.fc0_src = FC_NUM(6, 2),
++				),
++
++	[RP1_PLL_VIDEO_SEC] = REGISTER_PLL_DIV(
++				.name = "pll_video_sec",
++				.source_pll = "pll_video_core",
++				.ctrl_reg = PLL_VIDEO_SEC,
++				.fc0_src = FC_NUM(5, 3),
++				),
++
++	[RP1_CLK_SYS] = REGISTER_CLK(
++				.name = "clk_sys",
++				.parents = {"xosc", "-", "pll_sys"},
++				.num_std_parents = 3,
++				.num_aux_parents = 0,
++				.ctrl_reg = CLK_SYS_CTRL,
++				.div_int_reg = CLK_SYS_DIV_INT,
++				.sel_reg = CLK_SYS_SEL,
++				.div_int_max = DIV_INT_24BIT_MAX,
++				.fc0_src = FC_NUM(0, 4),
++				.clk_src_mask = 0x3,
++				),
++
++	[RP1_CLK_SLOW_SYS] = REGISTER_CLK(
++				.name = "clk_slow_sys",
++				.parents = {"xosc"},
++				.num_std_parents = 1,
++				.num_aux_parents = 0,
++				.ctrl_reg = CLK_SLOW_SYS_CTRL,
++				.div_int_reg = CLK_SLOW_SYS_DIV_INT,
++				.sel_reg = CLK_SLOW_SYS_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(1, 4),
++				.clk_src_mask = 0x1,
++				),
++
++	[RP1_CLK_UART] = REGISTER_CLK(
++				.name = "clk_uart",
++				.parents = {"pll_sys_pri_ph",
++					    "pll_video",
++					    "xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 3,
++				.ctrl_reg = CLK_UART_CTRL,
++				.div_int_reg = CLK_UART_DIV_INT,
++				.sel_reg = CLK_UART_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(6, 7),
++				),
++
++	[RP1_CLK_ETH] = REGISTER_CLK(
++				.name = "clk_eth",
++				.parents = {"-"},
++				.num_std_parents = 1,
++				.num_aux_parents = 0,
++				.ctrl_reg = CLK_ETH_CTRL,
++				.div_int_reg = CLK_ETH_DIV_INT,
++				.sel_reg = CLK_ETH_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(4, 6),
++				),
++
++	[RP1_CLK_PWM0] = REGISTER_CLK(
++				.name = "clk_pwm0",
++				.parents = {"pll_audio_pri_ph",
++					    "pll_video_sec",
++					    "xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 3,
++				.ctrl_reg = CLK_PWM0_CTRL,
++				.div_int_reg = CLK_PWM0_DIV_INT,
++				.div_frac_reg = CLK_PWM0_DIV_FRAC,
++				.sel_reg = CLK_PWM0_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(0, 5),
++				),
++
++	[RP1_CLK_PWM1] = REGISTER_CLK(
++				.name = "clk_pwm1",
++				.parents = {"pll_audio_pri_ph",
++					    "pll_video_sec",
++					    "xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 3,
++				.ctrl_reg = CLK_PWM1_CTRL,
++				.div_int_reg = CLK_PWM1_DIV_INT,
++				.div_frac_reg = CLK_PWM1_DIV_FRAC,
++				.sel_reg = CLK_PWM1_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(1, 5),
++				),
++
++	[RP1_CLK_AUDIO_IN] = REGISTER_CLK(
++				.name = "clk_audio_in",
++				.parents = {"-"},
++				.num_std_parents = 1,
++				.num_aux_parents = 0,
++				.ctrl_reg = CLK_AUDIO_IN_CTRL,
++				.div_int_reg = CLK_AUDIO_IN_DIV_INT,
++				.sel_reg = CLK_AUDIO_IN_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(2, 5),
++				),
++
++	[RP1_CLK_AUDIO_OUT] = REGISTER_CLK(
++				.name = "clk_audio_out",
++				.parents = {"-"},
++				.num_std_parents = 1,
++				.num_aux_parents = 0,
++				.ctrl_reg = CLK_AUDIO_OUT_CTRL,
++				.div_int_reg = CLK_AUDIO_OUT_DIV_INT,
++				.sel_reg = CLK_AUDIO_OUT_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(3, 5),
++				),
++
++	[RP1_CLK_I2S] = REGISTER_CLK(
++				.name = "clk_i2s",
++				.parents = {"xosc",
++					    "pll_audio",
++					    "pll_audio_sec"},
++				.num_std_parents = 0,
++				.num_aux_parents = 3,
++				.ctrl_reg = CLK_I2S_CTRL,
++				.div_int_reg = CLK_I2S_DIV_INT,
++				.sel_reg = CLK_I2S_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(4, 4),
++				),
++
++	[RP1_CLK_MIPI0_CFG] = REGISTER_CLK(
++				.name = "clk_mipi0_cfg",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_MIPI0_CFG_CTRL,
++				.div_int_reg = CLK_MIPI0_CFG_DIV_INT,
++				.sel_reg = CLK_MIPI0_CFG_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(4, 5),
++				),
++
++	[RP1_CLK_MIPI1_CFG] = REGISTER_CLK(
++				.name = "clk_mipi1_cfg",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_MIPI1_CFG_CTRL,
++				.div_int_reg = CLK_MIPI1_CFG_DIV_INT,
++				.sel_reg = CLK_MIPI1_CFG_SEL,
++				.clk_src_mask = 1,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(5, 6),
++				),
++
++	[RP1_CLK_ETH_TSU] = REGISTER_CLK(
++				.name = "clk_eth_tsu",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_ETH_TSU_CTRL,
++				.div_int_reg = CLK_ETH_TSU_DIV_INT,
++				.sel_reg = CLK_ETH_TSU_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(5, 7),
++				),
++
++	[RP1_CLK_ADC] = REGISTER_CLK(
++				.name = "clk_adc",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_ADC_CTRL,
++				.div_int_reg = CLK_ADC_DIV_INT,
++				.sel_reg = CLK_ADC_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(5, 5),
++				),
++
++	[RP1_CLK_SDIO_TIMER] = REGISTER_CLK(
++				.name = "clk_sdio_timer",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_SDIO_TIMER_CTRL,
++				.div_int_reg = CLK_SDIO_TIMER_DIV_INT,
++				.sel_reg = CLK_SDIO_TIMER_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(3, 4),
++				),
++
++	[RP1_CLK_SDIO_ALT_SRC] = REGISTER_CLK(
++				.name = "clk_sdio_alt_src",
++				.parents = {"pll_sys"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_SDIO_ALT_SRC_CTRL,
++				.div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT,
++				.sel_reg = CLK_SDIO_ALT_SRC_SEL,
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(5, 4),
++				),
++
++	[RP1_CLK_GP0] = REGISTER_CLK(
++				.name = "clk_gp0",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP0_CTRL,
++				.div_int_reg = CLK_GP0_DIV_INT,
++				.div_frac_reg = CLK_GP0_DIV_FRAC,
++				.sel_reg = CLK_GP0_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(0, 1),
++				),
++
++	[RP1_CLK_GP1] = REGISTER_CLK(
++				.name = "clk_gp1",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP1_CTRL,
++				.div_int_reg = CLK_GP1_DIV_INT,
++				.div_frac_reg = CLK_GP1_DIV_FRAC,
++				.sel_reg = CLK_GP1_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(1, 1),
++				),
++
++	[RP1_CLK_GP2] = REGISTER_CLK(
++				.name = "clk_gp2",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP2_CTRL,
++				.div_int_reg = CLK_GP2_DIV_INT,
++				.div_frac_reg = CLK_GP2_DIV_FRAC,
++				.sel_reg = CLK_GP2_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(2, 1),
++				),
++
++	[RP1_CLK_GP3] = REGISTER_CLK(
++				.name = "clk_gp3",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP3_CTRL,
++				.div_int_reg = CLK_GP3_DIV_INT,
++				.div_frac_reg = CLK_GP3_DIV_FRAC,
++				.sel_reg = CLK_GP3_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(3, 1),
++				),
++
++	[RP1_CLK_GP4] = REGISTER_CLK(
++				.name = "clk_gp4",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP4_CTRL,
++				.div_int_reg = CLK_GP4_DIV_INT,
++				.div_frac_reg = CLK_GP4_DIV_FRAC,
++				.sel_reg = CLK_GP4_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(4, 1),
++				),
++
++	[RP1_CLK_GP5] = REGISTER_CLK(
++				.name = "clk_gp5",
++				.parents = {"xosc"},
++				.num_std_parents = 0,
++				.num_aux_parents = 1,
++				.ctrl_reg = CLK_GP5_CTRL,
++				.div_int_reg = CLK_GP5_DIV_INT,
++				.div_frac_reg = CLK_GP5_DIV_FRAC,
++				.sel_reg = CLK_GP5_SEL,
++				.div_int_max = DIV_INT_16BIT_MAX,
++				.fc0_src = FC_NUM(5, 1),
++				),
++
++	[RP1_CLK_VEC] = REGISTER_CLK(
++				.name = "clk_vec",
++				.parents = {"pll_sys_pri_ph",
++					    "pll_video_sec",
++					    "pll_video",
++					    "clk_gp0",
++					    "clk_gp1",
++					    "clk_gp2",
++					    "clk_gp3",
++					    "clk_gp4"},
++				.num_std_parents = 0,
++				.num_aux_parents = 8, /* XXX in fact there are more than 8 */
++				.ctrl_reg = VIDEO_CLK_VEC_CTRL,
++				.div_int_reg = VIDEO_CLK_VEC_DIV_INT,
++				.sel_reg = VIDEO_CLK_VEC_SEL,
++				.flags = CLK_SET_RATE_NO_REPARENT, /* Let VEC driver set parent */
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(0, 6),
++				),
++
++	[RP1_CLK_DPI] = REGISTER_CLK(
++				.name = "clk_dpi",
++				.parents = {"pll_sys",
++					    "pll_video_sec",
++					    "pll_video",
++					    "clk_gp0",
++					    "clk_gp1",
++					    "clk_gp2",
++					    "clk_gp3",
++					    "clk_gp4"},
++				.num_std_parents = 0,
++				.num_aux_parents = 8, /* XXX in fact there are more than 8 */
++				.ctrl_reg = VIDEO_CLK_DPI_CTRL,
++				.div_int_reg = VIDEO_CLK_DPI_DIV_INT,
++				.sel_reg = VIDEO_CLK_DPI_SEL,
++				.flags = CLK_SET_RATE_NO_REPARENT, /* Let DPI driver set parent */
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(1, 6),
++				),
++
++	[RP1_CLK_MIPI0_DPI] = REGISTER_CLK(
++				.name = "clk_mipi0_dpi",
++				.parents = {"pll_sys",
++					    "pll_video_sec",
++					    "pll_video",
++					    "clksrc_mipi0_dsi_byteclk",
++					    "clk_gp0",
++					    "clk_gp1",
++					    "clk_gp2",
++					    "clk_gp3"},
++				.num_std_parents = 0,
++				.num_aux_parents = 8, /* XXX in fact there are more than 8 */
++				.ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL,
++				.div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT,
++				.div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC,
++				.sel_reg = VIDEO_CLK_MIPI0_DPI_SEL,
++				.flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(2, 6),
++				),
++
++	[RP1_CLK_MIPI1_DPI] = REGISTER_CLK(
++				.name = "clk_mipi1_dpi",
++				.parents = {"pll_sys",
++					    "pll_video_sec",
++					    "pll_video",
++					    "clksrc_mipi1_dsi_byteclk",
++					    "clk_gp0",
++					    "clk_gp1",
++					    "clk_gp2",
++					    "clk_gp3"},
++				.num_std_parents = 0,
++				.num_aux_parents = 8, /* XXX in fact there are more than 8 */
++				.ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL,
++				.div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT,
++				.div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC,
++				.sel_reg = VIDEO_CLK_MIPI1_DPI_SEL,
++				.flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */
++				.div_int_max = DIV_INT_8BIT_MAX,
++				.fc0_src = FC_NUM(3, 6),
++				),
++};
++
++static bool rp1_clk_claimed[ARRAY_SIZE(clk_desc_array)];
++
++static bool rp1_clk_is_claimed(const char *name)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
++		if (clk_desc_array[i].data) {
++			const char *clk_name = *(const char **)(clk_desc_array[i].data);
++
++			if (!strcmp(name, clk_name))
++				return rp1_clk_claimed[i];
++		}
++	}
++
++	return false;
++}
++
++static int rp1_clk_probe(struct platform_device *pdev)
++{
++	const struct rp1_clk_desc *desc;
++	struct device *dev = &pdev->dev;
++	struct rp1_clockman *clockman;
++	struct resource *res;
++	struct clk_hw **hws;
++	const size_t asize = ARRAY_SIZE(clk_desc_array);
++	u32 chip_id, platform;
++	unsigned int i;
++	u32 clk_id;
++	int ret;
++
++	clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize),
++				GFP_KERNEL);
++	if (!clockman)
++		return -ENOMEM;
++
++	rp1_get_platform(&chip_id, &platform);
++
++	spin_lock_init(&clockman->regs_lock);
++	clockman->dev = dev;
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	clockman->regs = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(clockman->regs))
++		return PTR_ERR(clockman->regs);
++
++	memset(rp1_clk_claimed, 0, sizeof(rp1_clk_claimed));
++	for (i = 0;
++	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
++					 i, &clk_id);
++	     i++)
++		rp1_clk_claimed[clk_id] = true;
++
++	platform_set_drvdata(pdev, clockman);
++
++	clockman->onecell.num = asize;
++	hws = clockman->onecell.hws;
++
++	for (i = 0; i < asize; i++) {
++		desc = &clk_desc_array[i];
++		if (desc->clk_register && desc->data)
++			hws[i] = desc->clk_register(clockman, desc->data);
++	}
++
++	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
++				     &clockman->onecell);
++	if (ret)
++		return ret;
++
++	return 0;
++}
++
++static const struct of_device_id rp1_clk_of_match[] = {
++	{ .compatible = "raspberrypi,rp1-clocks" },
++	{}
++};
++MODULE_DEVICE_TABLE(of, rp1_clk_of_match);
++
++static struct platform_driver rp1_clk_driver = {
++	.driver = {
++		.name = "rp1-clk",
++		.of_match_table = rp1_clk_of_match,
++	},
++	.probe = rp1_clk_probe,
++};
++
++static int __init __rp1_clk_driver_init(void)
++{
++	return platform_driver_register(&rp1_clk_driver);
++}
++postcore_initcall(__rp1_clk_driver_init);
++
++MODULE_AUTHOR("Naushir Patuck <[email protected]>");
++MODULE_DESCRIPTION("RP1 clock driver");
++MODULE_LICENSE("GPL");
+--- a/include/dt-bindings/clock/rp1.h
++++ b/include/dt-bindings/clock/rp1.h
+@@ -13,39 +13,40 @@
+ 
+ #define RP1_PLL_SYS_PRI_PH		6
+ #define RP1_PLL_SYS_SEC_PH		7
++#define RP1_PLL_AUDIO_PRI_PH		8
+ 
+-#define RP1_PLL_SYS_SEC			8
+-#define RP1_PLL_AUDIO_SEC		9
+-#define RP1_PLL_VIDEO_SEC		10
++#define RP1_PLL_SYS_SEC			9
++#define RP1_PLL_AUDIO_SEC		10
++#define RP1_PLL_VIDEO_SEC		11
+ 
+-#define RP1_CLK_SYS			11
+-#define RP1_CLK_SLOW_SYS		12
+-#define RP1_CLK_DMA			13
+-#define RP1_CLK_UART			14
+-#define RP1_CLK_ETH			15
+-#define RP1_CLK_PWM0			16
+-#define RP1_CLK_PWM1			17
+-#define RP1_CLK_AUDIO_IN		18
+-#define RP1_CLK_AUDIO_OUT		19
+-#define RP1_CLK_I2S			20
+-#define RP1_CLK_MIPI0_CFG		21
+-#define RP1_CLK_MIPI1_CFG		22
+-#define RP1_CLK_PCIE_AUX		23
+-#define RP1_CLK_USBH0_MICROFRAME	24
+-#define RP1_CLK_USBH1_MICROFRAME	25
+-#define RP1_CLK_USBH0_SUSPEND		26
+-#define RP1_CLK_USBH1_SUSPEND		27
+-#define RP1_CLK_ETH_TSU			28
+-#define RP1_CLK_ADC			29
+-#define RP1_CLK_SDIO_TIMER		30
+-#define RP1_CLK_SDIO_ALT_SRC		31
+-#define RP1_CLK_GP0			32
+-#define RP1_CLK_GP1			33
+-#define RP1_CLK_GP2			34
+-#define RP1_CLK_GP3			35
+-#define RP1_CLK_GP4			36
+-#define RP1_CLK_GP5			37
+-#define RP1_CLK_VEC			38
+-#define RP1_CLK_DPI			39
+-#define RP1_CLK_MIPI0_DPI		40
+-#define RP1_CLK_MIPI1_DPI		41
++#define RP1_CLK_SYS			12
++#define RP1_CLK_SLOW_SYS		13
++#define RP1_CLK_DMA			14
++#define RP1_CLK_UART			15
++#define RP1_CLK_ETH			16
++#define RP1_CLK_PWM0			17
++#define RP1_CLK_PWM1			18
++#define RP1_CLK_AUDIO_IN		19
++#define RP1_CLK_AUDIO_OUT		20
++#define RP1_CLK_I2S			21
++#define RP1_CLK_MIPI0_CFG		22
++#define RP1_CLK_MIPI1_CFG		23
++#define RP1_CLK_PCIE_AUX		24
++#define RP1_CLK_USBH0_MICROFRAME	25
++#define RP1_CLK_USBH1_MICROFRAME	26
++#define RP1_CLK_USBH0_SUSPEND		27
++#define RP1_CLK_USBH1_SUSPEND		28
++#define RP1_CLK_ETH_TSU			29
++#define RP1_CLK_ADC			30
++#define RP1_CLK_SDIO_TIMER		31
++#define RP1_CLK_SDIO_ALT_SRC		32
++#define RP1_CLK_GP0			33
++#define RP1_CLK_GP1			34
++#define RP1_CLK_GP2			35
++#define RP1_CLK_GP3			36
++#define RP1_CLK_GP4			37
++#define RP1_CLK_GP5			38
++#define RP1_CLK_VEC			39
++#define RP1_CLK_DPI			40
++#define RP1_CLK_MIPI0_DPI		41
++#define RP1_CLK_MIPI1_DPI		42

+ 60 - 0
target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch

@@ -0,0 +1,60 @@
+From 19b934ce3763c9465c5c80302f7c142d30b75869 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 28 Oct 2022 14:13:30 +0100
+Subject: [PATCH] dt-bindings: pinctrl: Add bindings for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ include/dt-bindings/pinctrl/rp1.h | 46 +++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+ create mode 100644 include/dt-bindings/pinctrl/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/pinctrl/rp1.h
+@@ -0,0 +1,46 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Header providing constants for RP1 pinctrl bindings.
++ *
++ * Copyright (C) 2019-2022 Raspberry Pi Ltd.
++ */
++
++#ifndef __DT_BINDINGS_PINCTRL_RP1_H__
++#define __DT_BINDINGS_PINCTRL_RP1_H__
++
++/* brcm,function property */
++#define RP1_FSEL_GPIO_IN	0
++#define RP1_FSEL_GPIO_OUT	1
++#define RP1_FSEL_ALT0_LEGACY	4
++#define RP1_FSEL_ALT1_LEGACY	5
++#define RP1_FSEL_ALT2_LEGACY	6
++#define RP1_FSEL_ALT3_LEGACY	7
++#define RP1_FSEL_ALT4_LEGACY	3
++#define RP1_FSEL_ALT5_LEGACY	2
++#define RP1_FSEL_ALT0		0x08
++#define RP1_FSEL_ALT0INV	0x09
++#define RP1_FSEL_ALT1		0x0a
++#define RP1_FSEL_ALT1INV	0x0b
++#define RP1_FSEL_ALT2		0x0c
++#define RP1_FSEL_ALT2INV	0x0d
++#define RP1_FSEL_ALT3		0x0e
++#define RP1_FSEL_ALT3INV	0x0f
++#define RP1_FSEL_ALT4		0x10
++#define RP1_FSEL_ALT4INV	0x11
++#define RP1_FSEL_ALT5		0x12
++#define RP1_FSEL_ALT5INV	0x13
++#define RP1_FSEL_ALT6		0x14
++#define RP1_FSEL_ALT6INV	0x15
++#define RP1_FSEL_ALT7		0x16
++#define RP1_FSEL_ALT7INV	0x17
++#define RP1_FSEL_ALT8		0x18
++#define RP1_FSEL_ALT8INV	0x19
++#define RP1_FSEL_NONE		0x1a
++
++/* brcm,pull property */
++#define RP1_PUD_OFF		0
++#define RP1_PUD_DOWN		1
++#define RP1_PUD_UP		2
++#define RP1_PUD_KEEP		3
++
++#endif /* __DT_BINDINGS_PINCTRL_RP1_H__ */

+ 1666 - 0
target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch

@@ -0,0 +1,1666 @@
+From 4d4cc5be473a7767052122a87393a83d10f9ed41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 10 Oct 2022 14:21:11 +0100
+Subject: [PATCH] pinctrl: Add rp1 driver
+
+RP1 exposes GPIOs. Add a pinctrl driver to allow control of those.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/pinctrl/Kconfig           |    7 +
+ drivers/pinctrl/Makefile          |    1 +
+ drivers/pinctrl/pinctrl-rp1.c     | 1571 +++++++++++++++++++++++++++++
+ include/dt-bindings/pinctrl/rp1.h |   46 -
+ 4 files changed, 1579 insertions(+), 46 deletions(-)
+ create mode 100644 drivers/pinctrl/pinctrl-rp1.c
+ delete mode 100644 include/dt-bindings/pinctrl/rp1.h
+
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -512,6 +512,13 @@ config PINCTRL_ZYNQMP
+ 	  This driver can also be built as a module. If so, the module
+ 	  will be called pinctrl-zynqmp.
+ 
++config PINCTRL_RP1
++	bool "Pinctrl driver for RP1"
++	select PINMUX
++	select PINCONF
++	select GENERIC_PINCONF
++	select GPIOLIB_IRQCHIP
++
+ source "drivers/pinctrl/actions/Kconfig"
+ source "drivers/pinctrl/aspeed/Kconfig"
+ source "drivers/pinctrl/bcm/Kconfig"
+--- a/drivers/pinctrl/Makefile
++++ b/drivers/pinctrl/Makefile
+@@ -42,6 +42,7 @@ obj-$(CONFIG_PINCTRL_PIC32)	+= pinctrl-p
+ obj-$(CONFIG_PINCTRL_PISTACHIO)	+= pinctrl-pistachio.o
+ obj-$(CONFIG_PINCTRL_RK805)	+= pinctrl-rk805.o
+ obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
++obj-$(CONFIG_PINCTRL_RP1)	+= pinctrl-rp1.o
+ obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
+ obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
+ obj-$(CONFIG_PINCTRL_STMFX) 	+= pinctrl-stmfx.o
+--- /dev/null
++++ b/drivers/pinctrl/pinctrl-rp1.c
+@@ -0,0 +1,1571 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Driver for Raspberry Pi RP1 GPIO unit (pinctrl + GPIO)
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * This driver is inspired by:
++ * pinctrl-bcm2835.c, please see original file for copyright information
++ */
++
++#include <linux/bitmap.h>
++#include <linux/bitops.h>
++#include <linux/bug.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/gpio/driver.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/irqdesc.h>
++#include <linux/init.h>
++#include <linux/of_address.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/pinctrl/machine.h>
++#include <linux/pinctrl/pinconf.h>
++#include <linux/pinctrl/pinctrl.h>
++#include <linux/pinctrl/pinmux.h>
++#include <linux/pinctrl/pinconf-generic.h>
++#include <linux/platform_device.h>
++#include <linux/seq_file.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++#include "core.h"
++#include "pinconf.h"
++#include "pinctrl-utils.h"
++
++#define MODULE_NAME "pinctrl-rp1"
++#define RP1_NUM_GPIOS	54
++#define RP1_NUM_BANKS	3
++
++#define RP1_RW_OFFSET			0x0000
++#define RP1_XOR_OFFSET			0x1000
++#define RP1_SET_OFFSET			0x2000
++#define RP1_CLR_OFFSET			0x3000
++
++#define RP1_GPIO_STATUS			0x0000
++#define RP1_GPIO_CTRL			0x0004
++
++#define RP1_GPIO_PCIE_INTE		0x011c
++#define RP1_GPIO_PCIE_INTS		0x0124
++
++#define RP1_GPIO_EVENTS_SHIFT_RAW	20
++#define RP1_GPIO_STATUS_FALLING		BIT(20)
++#define RP1_GPIO_STATUS_RISING		BIT(21)
++#define RP1_GPIO_STATUS_LOW		BIT(22)
++#define RP1_GPIO_STATUS_HIGH		BIT(23)
++
++#define RP1_GPIO_EVENTS_SHIFT_FILTERED	24
++#define RP1_GPIO_STATUS_F_FALLING	BIT(24)
++#define RP1_GPIO_STATUS_F_RISING	BIT(25)
++#define RP1_GPIO_STATUS_F_LOW		BIT(26)
++#define RP1_GPIO_STATUS_F_HIGH		BIT(27)
++
++#define RP1_GPIO_CTRL_FUNCSEL_LSB	0
++#define RP1_GPIO_CTRL_FUNCSEL_MASK	0x0000001f
++#define RP1_GPIO_CTRL_OUTOVER_LSB	12
++#define RP1_GPIO_CTRL_OUTOVER_MASK	0x00003000
++#define RP1_GPIO_CTRL_OEOVER_LSB	14
++#define RP1_GPIO_CTRL_OEOVER_MASK	0x0000c000
++#define RP1_GPIO_CTRL_INOVER_LSB	16
++#define RP1_GPIO_CTRL_INOVER_MASK	0x00030000
++#define RP1_GPIO_CTRL_IRQEN_FALLING	BIT(20)
++#define RP1_GPIO_CTRL_IRQEN_RISING	BIT(21)
++#define RP1_GPIO_CTRL_IRQEN_LOW		BIT(22)
++#define RP1_GPIO_CTRL_IRQEN_HIGH	BIT(23)
++#define RP1_GPIO_CTRL_IRQEN_F_FALLING	BIT(24)
++#define RP1_GPIO_CTRL_IRQEN_F_RISING	BIT(25)
++#define RP1_GPIO_CTRL_IRQEN_F_LOW	BIT(26)
++#define RP1_GPIO_CTRL_IRQEN_F_HIGH	BIT(27)
++#define RP1_GPIO_CTRL_IRQRESET		BIT(28)
++#define RP1_GPIO_CTRL_IRQOVER_LSB	30
++#define RP1_GPIO_CTRL_IRQOVER_MASK	0xc0000000
++
++#define RP1_INT_EDGE_FALLING		BIT(0)
++#define RP1_INT_EDGE_RISING		BIT(1)
++#define RP1_INT_LEVEL_LOW		BIT(2)
++#define RP1_INT_LEVEL_HIGH		BIT(3)
++#define RP1_INT_MASK			0xf
++
++#define RP1_INT_EDGE_BOTH		(RP1_INT_EDGE_FALLING |	\
++					 RP1_INT_EDGE_RISING)
++#define RP1_PUD_OFF			0
++#define RP1_PUD_DOWN			1
++#define RP1_PUD_UP			2
++
++#define RP1_FSEL_COUNT			9
++
++#define RP1_FSEL_ALT0			0x00
++#define RP1_FSEL_GPIO			0x05
++#define RP1_FSEL_NONE			0x09
++#define RP1_FSEL_NONE_HW		0x1f
++
++#define RP1_DIR_OUTPUT			0
++#define RP1_DIR_INPUT			1
++
++#define RP1_OUTOVER_PERI		0
++#define RP1_OUTOVER_INVPERI		1
++#define RP1_OUTOVER_LOW			2
++#define RP1_OUTOVER_HIGH		3
++
++#define RP1_OEOVER_PERI			0
++#define RP1_OEOVER_INVPERI		1
++#define RP1_OEOVER_DISABLE		2
++#define RP1_OEOVER_ENABLE		3
++
++#define RP1_INOVER_PERI			0
++#define RP1_INOVER_INVPERI		1
++#define RP1_INOVER_LOW			2
++#define RP1_INOVER_HIGH			3
++
++#define RP1_RIO_OUT			0x00
++#define RP1_RIO_OE			0x04
++#define RP1_RIO_IN			0x08
++
++#define RP1_PAD_SLEWFAST_MASK		0x00000001
++#define RP1_PAD_SLEWFAST_LSB		0
++#define RP1_PAD_SCHMITT_MASK		0x00000002
++#define RP1_PAD_SCHMITT_LSB		1
++#define RP1_PAD_PULL_MASK		0x0000000c
++#define RP1_PAD_PULL_LSB		2
++#define RP1_PAD_DRIVE_MASK		0x00000030
++#define RP1_PAD_DRIVE_LSB		4
++#define RP1_PAD_IN_ENABLE_MASK		0x00000040
++#define RP1_PAD_IN_ENABLE_LSB		6
++#define RP1_PAD_OUT_DISABLE_MASK	0x00000080
++#define RP1_PAD_OUT_DISABLE_LSB		7
++
++#define RP1_PAD_DRIVE_2MA		0x00000000
++#define RP1_PAD_DRIVE_4MA		0x00000010
++#define RP1_PAD_DRIVE_8MA		0x00000020
++#define RP1_PAD_DRIVE_12MA		0x00000030
++
++#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB))
++#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB)))
++
++#define FUNC(f) \
++	[func_##f] = #f
++#define RP1_MAX_FSEL 8
++#define PIN(i, f0, f1, f2, f3, f4, f5, f6, f7, f8) \
++	[i] = { \
++		.funcs = { \
++			func_##f0, \
++			func_##f1, \
++			func_##f2, \
++			func_##f3, \
++			func_##f4, \
++			func_##f5, \
++			func_##f6, \
++			func_##f7, \
++			func_##f8, \
++		}, \
++	}
++
++#define LEGACY_MAP(n, f0, f1, f2, f3, f4, f5) \
++	[n] = { \
++		func_gpio, \
++		func_gpio, \
++		func_##f5, \
++		func_##f4, \
++		func_##f0, \
++		func_##f1, \
++		func_##f2, \
++		func_##f3, \
++	}
++
++struct rp1_iobank_desc {
++	int min_gpio;
++	int num_gpios;
++	int gpio_offset;
++	int inte_offset;
++	int ints_offset;
++	int rio_offset;
++	int pads_offset;
++};
++
++struct rp1_pin_info {
++	u8 num;
++	u8 bank;
++	u8 offset;
++	u8 fsel;
++	u8 irq_type;
++
++	void __iomem *gpio;
++	void __iomem *rio;
++	void __iomem *inte;
++	void __iomem *ints;
++	void __iomem *pad;
++};
++
++enum funcs {
++	func_alt0,
++	func_alt1,
++	func_alt2,
++	func_alt3,
++	func_alt4,
++	func_gpio,
++	func_alt6,
++	func_alt7,
++	func_alt8,
++	func_none,
++	func_aaud,
++	func_dcd0,
++	func_dpi,
++	func_dsi0_te_ext,
++	func_dsi1_te_ext,
++	func_dsr0,
++	func_dtr0,
++	func_gpclk0,
++	func_gpclk1,
++	func_gpclk2,
++	func_gpclk3,
++	func_gpclk4,
++	func_gpclk5,
++	func_i2c0,
++	func_i2c1,
++	func_i2c2,
++	func_i2c3,
++	func_i2c4,
++	func_i2c5,
++	func_i2c6,
++	func_i2s0,
++	func_i2s1,
++	func_i2s2,
++	func_ir,
++	func_mic,
++	func_pcie_clkreq_n,
++	func_pio,
++	func_proc_rio,
++	func_pwm0,
++	func_pwm1,
++	func_ri0,
++	func_sd0,
++	func_sd1,
++	func_spi0,
++	func_spi1,
++	func_spi2,
++	func_spi3,
++	func_spi4,
++	func_spi5,
++	func_spi6,
++	func_spi7,
++	func_spi8,
++	func_uart0,
++	func_uart1,
++	func_uart2,
++	func_uart3,
++	func_uart4,
++	func_uart5,
++	func_vbus0,
++	func_vbus1,
++	func_vbus2,
++	func_vbus3,
++	func__,
++	func_count = func__,
++	func_invalid = func__,
++};
++
++struct rp1_pin_funcs {
++	u8 funcs[RP1_FSEL_COUNT];
++};
++
++struct rp1_pinctrl {
++	struct device *dev;
++	void __iomem *gpio_base;
++	void __iomem *rio_base;
++	void __iomem *pads_base;
++	int irq[RP1_NUM_BANKS];
++	struct rp1_pin_info pins[RP1_NUM_GPIOS];
++
++	struct pinctrl_dev *pctl_dev;
++	struct gpio_chip gpio_chip;
++	struct pinctrl_gpio_range gpio_range;
++
++	raw_spinlock_t irq_lock[RP1_NUM_BANKS];
++};
++
++const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = {
++	/*         gpio   inte    ints     rio    pads */
++	{  0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 },
++	{ 28,  6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 },
++	{ 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 },
++};
++
++/* pins are just named GPIO0..GPIO53 */
++#define RP1_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
++static struct pinctrl_pin_desc rp1_gpio_pins[] = {
++	RP1_GPIO_PIN(0),
++	RP1_GPIO_PIN(1),
++	RP1_GPIO_PIN(2),
++	RP1_GPIO_PIN(3),
++	RP1_GPIO_PIN(4),
++	RP1_GPIO_PIN(5),
++	RP1_GPIO_PIN(6),
++	RP1_GPIO_PIN(7),
++	RP1_GPIO_PIN(8),
++	RP1_GPIO_PIN(9),
++	RP1_GPIO_PIN(10),
++	RP1_GPIO_PIN(11),
++	RP1_GPIO_PIN(12),
++	RP1_GPIO_PIN(13),
++	RP1_GPIO_PIN(14),
++	RP1_GPIO_PIN(15),
++	RP1_GPIO_PIN(16),
++	RP1_GPIO_PIN(17),
++	RP1_GPIO_PIN(18),
++	RP1_GPIO_PIN(19),
++	RP1_GPIO_PIN(20),
++	RP1_GPIO_PIN(21),
++	RP1_GPIO_PIN(22),
++	RP1_GPIO_PIN(23),
++	RP1_GPIO_PIN(24),
++	RP1_GPIO_PIN(25),
++	RP1_GPIO_PIN(26),
++	RP1_GPIO_PIN(27),
++	RP1_GPIO_PIN(28),
++	RP1_GPIO_PIN(29),
++	RP1_GPIO_PIN(30),
++	RP1_GPIO_PIN(31),
++	RP1_GPIO_PIN(32),
++	RP1_GPIO_PIN(33),
++	RP1_GPIO_PIN(34),
++	RP1_GPIO_PIN(35),
++	RP1_GPIO_PIN(36),
++	RP1_GPIO_PIN(37),
++	RP1_GPIO_PIN(38),
++	RP1_GPIO_PIN(39),
++	RP1_GPIO_PIN(40),
++	RP1_GPIO_PIN(41),
++	RP1_GPIO_PIN(42),
++	RP1_GPIO_PIN(43),
++	RP1_GPIO_PIN(44),
++	RP1_GPIO_PIN(45),
++	RP1_GPIO_PIN(46),
++	RP1_GPIO_PIN(47),
++	RP1_GPIO_PIN(48),
++	RP1_GPIO_PIN(49),
++	RP1_GPIO_PIN(50),
++	RP1_GPIO_PIN(51),
++	RP1_GPIO_PIN(52),
++	RP1_GPIO_PIN(53),
++};
++
++/* one pin per group */
++static const char * const rp1_gpio_groups[] = {
++	"gpio0",
++	"gpio1",
++	"gpio2",
++	"gpio3",
++	"gpio4",
++	"gpio5",
++	"gpio6",
++	"gpio7",
++	"gpio8",
++	"gpio9",
++	"gpio10",
++	"gpio11",
++	"gpio12",
++	"gpio13",
++	"gpio14",
++	"gpio15",
++	"gpio16",
++	"gpio17",
++	"gpio18",
++	"gpio19",
++	"gpio20",
++	"gpio21",
++	"gpio22",
++	"gpio23",
++	"gpio24",
++	"gpio25",
++	"gpio26",
++	"gpio27",
++	"gpio28",
++	"gpio29",
++	"gpio30",
++	"gpio31",
++	"gpio32",
++	"gpio33",
++	"gpio34",
++	"gpio35",
++	"gpio36",
++	"gpio37",
++	"gpio38",
++	"gpio39",
++	"gpio40",
++	"gpio41",
++	"gpio42",
++	"gpio43",
++	"gpio44",
++	"gpio45",
++	"gpio46",
++	"gpio47",
++	"gpio48",
++	"gpio49",
++	"gpio50",
++	"gpio51",
++	"gpio52",
++	"gpio53",
++};
++
++static const char * const rp1_func_names[] = {
++	FUNC(alt0),
++	FUNC(alt1),
++	FUNC(alt2),
++	FUNC(alt3),
++	FUNC(alt4),
++	FUNC(gpio),
++	FUNC(alt6),
++	FUNC(alt7),
++	FUNC(alt8),
++	FUNC(none),
++	FUNC(aaud),
++	FUNC(dcd0),
++	FUNC(dpi),
++	FUNC(dsi0_te_ext),
++	FUNC(dsi1_te_ext),
++	FUNC(dsr0),
++	FUNC(dtr0),
++	FUNC(gpclk0),
++	FUNC(gpclk1),
++	FUNC(gpclk2),
++	FUNC(gpclk3),
++	FUNC(gpclk4),
++	FUNC(gpclk5),
++	FUNC(i2c0),
++	FUNC(i2c1),
++	FUNC(i2c2),
++	FUNC(i2c3),
++	FUNC(i2c4),
++	FUNC(i2c5),
++	FUNC(i2c6),
++	FUNC(i2s0),
++	FUNC(i2s1),
++	FUNC(i2s2),
++	FUNC(ir),
++	FUNC(mic),
++	FUNC(pcie_clkreq_n),
++	FUNC(pio),
++	FUNC(proc_rio),
++	FUNC(pwm0),
++	FUNC(pwm1),
++	FUNC(ri0),
++	FUNC(sd0),
++	FUNC(sd1),
++	FUNC(spi0),
++	FUNC(spi1),
++	FUNC(spi2),
++	FUNC(spi3),
++	FUNC(spi4),
++	FUNC(spi5),
++	FUNC(spi6),
++	FUNC(spi7),
++	FUNC(spi8),
++	FUNC(uart0),
++	FUNC(uart1),
++	FUNC(uart2),
++	FUNC(uart3),
++	FUNC(uart4),
++	FUNC(uart5),
++	FUNC(vbus0),
++	FUNC(vbus1),
++	FUNC(vbus2),
++	FUNC(vbus3),
++	[func_invalid] = "?"
++};
++
++static const struct rp1_pin_funcs rp1_gpio_pin_funcs[] = {
++	PIN(0, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
++	PIN(1, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
++	PIN(2, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
++	PIN(3, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
++	PIN(4, gpclk0, dpi, uart2, i2c2, ri0, gpio, proc_rio, pio, spi3),
++	PIN(5, gpclk1, dpi, uart2, i2c2, dtr0, gpio, proc_rio, pio, spi3),
++	PIN(6, gpclk2, dpi, uart2, i2c3, dcd0, gpio, proc_rio, pio, spi3),
++	PIN(7, spi0, dpi, uart2, i2c3, dsr0, gpio, proc_rio, pio, spi3),
++	PIN(8, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
++	PIN(9, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
++	PIN(10, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
++	PIN(11, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
++	PIN(12, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
++	PIN(13, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
++	PIN(14, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
++	PIN(15, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
++	PIN(16, spi1, dpi, dsi0_te_ext, _, uart0, gpio, proc_rio, pio, _),
++	PIN(17, spi1, dpi, dsi1_te_ext, _, uart0, gpio, proc_rio, pio, _),
++	PIN(18, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, gpclk1),
++	PIN(19, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, _),
++	PIN(20, spi1, dpi, i2s0, gpclk0, i2s1, gpio, proc_rio, pio, _),
++	PIN(21, spi1, dpi, i2s0, gpclk1, i2s1, gpio, proc_rio, pio, _),
++	PIN(22, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
++	PIN(23, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
++	PIN(24, sd0, dpi, i2s0, _, i2s1, gpio, proc_rio, pio, spi2),
++	PIN(25, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi3),
++	PIN(26, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi5),
++	PIN(27, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi1),
++	PIN(28, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
++	PIN(29, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
++	PIN(30, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++	PIN(31, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++	PIN(32, sd1, gpclk3, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++	PIN(33, sd1, gpclk4, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++	PIN(34, pwm1, gpclk3, vbus0, i2c4, mic, gpio, proc_rio, _, _),
++	PIN(35, spi8, pwm1, vbus0, i2c4, mic, gpio, proc_rio, _, _),
++	PIN(36, spi8, uart5, pcie_clkreq_n, i2c5, mic, gpio, proc_rio, _, _),
++	PIN(37, spi8, uart5, mic, i2c5, pcie_clkreq_n, gpio, proc_rio, _, _),
++	PIN(38, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi0_te_ext, _),
++	PIN(39, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi1_te_ext, _),
++	PIN(40, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
++	PIN(41, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
++	PIN(42, gpclk5, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
++	PIN(43, gpclk4, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
++	PIN(44, gpclk5, i2c5, pwm1, spi6, i2s2, gpio, proc_rio, _, _),
++	PIN(45, pwm1, i2c5, spi7, spi6, i2s2, gpio, proc_rio, _, _),
++	PIN(46, gpclk3, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi0_te_ext, _),
++	PIN(47, gpclk5, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi1_te_ext, _),
++	PIN(48, pwm1, pcie_clkreq_n, spi7, mic, uart5, gpio, proc_rio, _, _),
++	PIN(49, spi8, spi7, i2c5, aaud, uart5, gpio, proc_rio, _, _),
++	PIN(50, spi8, spi7, i2c5, aaud, vbus2, gpio, proc_rio, _, _),
++	PIN(51, spi8, spi7, i2c6, aaud, vbus2, gpio, proc_rio, _, _),
++	PIN(52, spi8, _, i2c6, aaud, vbus3, gpio, proc_rio, _, _),
++	PIN(53, spi8, spi7, _, pcie_clkreq_n, vbus3, gpio, proc_rio, _, _),
++};
++
++static const u8 legacy_fsel_map[][8] = {
++	LEGACY_MAP(0, i2c0, _, dpi, spi2, uart1, _),
++	LEGACY_MAP(1, i2c0, _, dpi, spi2, uart1, _),
++	LEGACY_MAP(2, i2c1, _, dpi, spi2, uart1, _),
++	LEGACY_MAP(3, i2c1, _, dpi, spi2, uart1, _),
++	LEGACY_MAP(4, gpclk0, _, dpi, spi3, uart2, i2c2),
++	LEGACY_MAP(5, gpclk1, _, dpi, spi3, uart2, i2c2),
++	LEGACY_MAP(6, gpclk2, _, dpi, spi3, uart2, i2c3),
++	LEGACY_MAP(7, spi0, _, dpi, spi3, uart2, i2c3),
++	LEGACY_MAP(8, spi0, _, dpi, _, uart3, i2c0),
++	LEGACY_MAP(9, spi0, _, dpi, _, uart3, i2c0),
++	LEGACY_MAP(10, spi0, _, dpi, _, uart3, i2c1),
++	LEGACY_MAP(11, spi0, _, dpi, _, uart3, i2c1),
++	LEGACY_MAP(12, pwm0, _, dpi, spi5, uart4, i2c2),
++	LEGACY_MAP(13, pwm0, _, dpi, spi5, uart4, i2c2),
++	LEGACY_MAP(14, uart0, _, dpi, spi5, uart4, _),
++	LEGACY_MAP(15, uart0, _, dpi, spi5, uart4, _),
++	LEGACY_MAP(16, _, _, dpi, uart0, spi1, _),
++	LEGACY_MAP(17, _, _, dpi, uart0, spi1, _),
++	LEGACY_MAP(18, i2s0, _, dpi, _, spi1, pwm0),
++	LEGACY_MAP(19, i2s0, _, dpi, _, spi1, pwm0),
++	LEGACY_MAP(20, i2s0, _, dpi, _, spi1, gpclk0),
++	LEGACY_MAP(21, i2s0, _, dpi, _, spi1, gpclk1),
++	LEGACY_MAP(22, sd0, _, dpi, _, _, i2c3),
++	LEGACY_MAP(23, sd0, _, dpi, _, _, i2c3),
++	LEGACY_MAP(24, sd0, _, dpi, _, _, spi2),
++	LEGACY_MAP(25, sd0, _, dpi, _, _, spi3),
++	LEGACY_MAP(26, sd0, _, dpi, _, _, spi5),
++	LEGACY_MAP(27, sd0, _, dpi, _, _, _),
++};
++
++static const char * const irq_type_names[] = {
++	[IRQ_TYPE_NONE] = "none",
++	[IRQ_TYPE_EDGE_RISING] = "edge-rising",
++	[IRQ_TYPE_EDGE_FALLING] = "edge-falling",
++	[IRQ_TYPE_EDGE_BOTH] = "edge-both",
++	[IRQ_TYPE_LEVEL_HIGH] = "level-high",
++	[IRQ_TYPE_LEVEL_LOW] = "level-low",
++};
++
++static int rp1_pinconf_set(struct pinctrl_dev *pctldev,
++			   unsigned int offset, unsigned long *configs,
++			   unsigned int num_configs);
++
++static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip,
++					unsigned int offset)
++{
++	struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++
++	if (pc && offset < RP1_NUM_GPIOS)
++		return &pc->pins[offset];
++	return NULL;
++}
++
++static struct rp1_pin_info *rp1_get_pin_pctl(struct pinctrl_dev *pctldev,
++					     unsigned int offset)
++{
++	struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++	if (pc && offset < RP1_NUM_GPIOS)
++		return &pc->pins[offset];
++	return NULL;
++}
++
++static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set)
++{
++	u32 padctrl = readl(pin->pad);
++
++	padctrl &= ~clr;
++	padctrl |= set;
++
++	writel(padctrl, pin->pad);
++}
++
++static void rp1_input_enable(struct rp1_pin_info *pin, int value)
++{
++	rp1_pad_update(pin, RP1_PAD_IN_ENABLE_MASK,
++		       value ? RP1_PAD_IN_ENABLE_MASK : 0);
++}
++
++static void rp1_output_enable(struct rp1_pin_info *pin, int value)
++{
++	rp1_pad_update(pin, RP1_PAD_OUT_DISABLE_MASK,
++		       value ? 0 : RP1_PAD_OUT_DISABLE_MASK);
++}
++
++static u32 rp1_get_fsel(struct rp1_pin_info *pin)
++{
++	u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
++	u32 oeover = FLD_GET(ctrl, RP1_GPIO_CTRL_OEOVER);
++	u32 fsel = FLD_GET(ctrl, RP1_GPIO_CTRL_FUNCSEL);
++
++	if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT)
++		fsel = RP1_FSEL_NONE;
++
++	return fsel;
++}
++
++static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel)
++{
++	u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
++
++	if (fsel >= RP1_FSEL_COUNT)
++		fsel = RP1_FSEL_NONE_HW;
++
++	rp1_input_enable(pin, 1);
++	rp1_output_enable(pin, 1);
++
++	if (fsel == RP1_FSEL_NONE) {
++		FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE);
++	} else {
++		FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI);
++		FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI);
++	}
++	FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel);
++	writel(ctrl, pin->gpio + RP1_GPIO_CTRL);
++}
++
++static int rp1_get_dir(struct rp1_pin_info *pin)
++{
++	return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ?
++		RP1_DIR_INPUT : RP1_DIR_OUTPUT;
++}
++
++static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
++{
++	int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET;
++
++	writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset);
++}
++
++static int rp1_get_value(struct rp1_pin_info *pin)
++{
++	return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset));
++}
++
++static void rp1_set_value(struct rp1_pin_info *pin, int value)
++{
++	/* Assume the pin is already an output */
++	writel(1 << pin->offset,
++	       pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
++}
++
++static int rp1_gpio_get(struct gpio_chip *chip, unsigned offset)
++{
++	struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++	int ret;
++
++	if (!pin)
++		return -EINVAL;
++	ret = rp1_get_value(pin);
++	return ret;
++}
++
++static void rp1_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
++{
++	struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++	if (pin)
++		rp1_set_value(pin, value);
++}
++
++static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
++{
++	struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++	u32 fsel;
++
++	if (!pin)
++		return -EINVAL;
++	fsel = rp1_get_fsel(pin);
++	if (fsel != RP1_FSEL_GPIO)
++		return -EINVAL;
++	return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ?
++		GPIO_LINE_DIRECTION_OUT :
++		GPIO_LINE_DIRECTION_IN;
++}
++
++static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++{
++	struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++	if (!pin)
++		return -EINVAL;
++	rp1_set_dir(pin, RP1_DIR_INPUT);
++	rp1_set_fsel(pin, RP1_FSEL_GPIO);
++	return 0;
++}
++
++static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
++				     int value)
++{
++	struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++	if (!pin)
++		return -EINVAL;
++	rp1_set_value(pin, value);
++	rp1_set_dir(pin, RP1_DIR_OUTPUT);
++	rp1_set_fsel(pin, RP1_FSEL_GPIO);
++	return 0;
++}
++
++static int rp1_gpio_set_config(struct gpio_chip *gc, unsigned offset,
++			       unsigned long config)
++{
++	struct rp1_pinctrl *pc = gpiochip_get_data(gc);
++	unsigned long configs[] = { config };
++
++	return rp1_pinconf_set(pc->pctl_dev, offset, configs,
++			      ARRAY_SIZE(configs));
++}
++
++static const struct gpio_chip rp1_gpio_chip = {
++	.label = MODULE_NAME,
++	.owner = THIS_MODULE,
++	.request = gpiochip_generic_request,
++	.free = gpiochip_generic_free,
++	.direction_input = rp1_gpio_direction_input,
++	.direction_output = rp1_gpio_direction_output,
++	.get_direction = rp1_gpio_get_direction,
++	.get = rp1_gpio_get,
++	.set = rp1_gpio_set,
++	.base = -1,
++	.set_config = rp1_gpio_set_config,
++	.ngpio = RP1_NUM_GPIOS,
++	.can_sleep = false,
++};
++
++static void rp1_gpio_irq_handler(struct irq_desc *desc)
++{
++	struct gpio_chip *chip = irq_desc_get_handler_data(desc);
++	struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++	struct irq_chip *host_chip = irq_desc_get_chip(desc);
++	const struct rp1_iobank_desc *bank;
++	int irq = irq_desc_get_irq(desc);
++	unsigned long ints;
++	int b;
++
++	if (pc->irq[0] == irq)
++		bank = &rp1_iobanks[0];
++	else if (pc->irq[1] == irq)
++		bank = &rp1_iobanks[1];
++	else
++		bank = &rp1_iobanks[2];
++
++	chained_irq_enter(host_chip, desc);
++
++	ints = readl(pc->gpio_base + bank->ints_offset);
++	for_each_set_bit(b, &ints, 32) {
++		struct rp1_pin_info *pin = rp1_get_pin(chip, b);
++
++		writel(RP1_GPIO_CTRL_IRQRESET,
++		       pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++		generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.domain,
++						     bank->gpio_offset + b));
++	}
++
++	chained_irq_exit(host_chip, desc);
++}
++
++static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable)
++{
++	writel(1 << pin->offset,
++	       pin->inte + (enable ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
++	if (!enable)
++		/* Clear any latched events */
++		writel(RP1_GPIO_CTRL_IRQRESET,
++		       pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++}
++
++static void rp1_gpio_irq_enable(struct irq_data *data)
++{
++	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++	unsigned gpio = irqd_to_hwirq(data);
++	struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++	rp1_gpio_irq_config(pin, true);
++}
++
++static void rp1_gpio_irq_disable(struct irq_data *data)
++{
++	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++	unsigned gpio = irqd_to_hwirq(data);
++	struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++	rp1_gpio_irq_config(pin, false);
++}
++
++static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type)
++{
++	u32 irq_flags;
++
++	switch (type) {
++	case IRQ_TYPE_NONE:
++		irq_flags = 0;
++		break;
++	case IRQ_TYPE_EDGE_RISING:
++		irq_flags = RP1_INT_EDGE_RISING;
++		break;
++	case IRQ_TYPE_EDGE_FALLING:
++		irq_flags = RP1_INT_EDGE_FALLING;
++		break;
++	case IRQ_TYPE_EDGE_BOTH:
++		irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING;
++		break;
++	case IRQ_TYPE_LEVEL_HIGH:
++		irq_flags = RP1_INT_LEVEL_HIGH;
++		break;
++	case IRQ_TYPE_LEVEL_LOW:
++		irq_flags = RP1_INT_LEVEL_LOW;
++		break;
++
++	default:
++		return -EINVAL;
++	}
++
++	/* Clear them all */
++	writel(RP1_INT_MASK << RP1_GPIO_EVENTS_SHIFT_RAW,
++	       pin->gpio + RP1_CLR_OFFSET + RP1_GPIO_CTRL);
++	/* Set those that are needed */
++	writel(irq_flags << RP1_GPIO_EVENTS_SHIFT_RAW,
++	       pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++	pin->irq_type = type;
++
++	return 0;
++}
++
++static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type)
++{
++	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++	struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++	unsigned gpio = irqd_to_hwirq(data);
++	struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++	int bank = pin->bank;
++	unsigned long flags;
++	int ret;
++
++	raw_spin_lock_irqsave(&pc->irq_lock[bank], flags);
++
++	ret = rp1_irq_set_type(pin, type);
++	if (!ret) {
++		if (type & IRQ_TYPE_EDGE_BOTH)
++			irq_set_handler_locked(data, handle_edge_irq);
++		else
++			irq_set_handler_locked(data, handle_level_irq);
++	}
++
++	raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags);
++
++	return ret;
++}
++
++static void rp1_gpio_irq_ack(struct irq_data *data)
++{
++	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++	unsigned gpio = irqd_to_hwirq(data);
++	struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++	/* Clear any latched events */
++	writel(RP1_GPIO_CTRL_IRQRESET, pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++}
++
++static struct irq_chip rp1_gpio_irq_chip = {
++	.name = MODULE_NAME,
++	.irq_enable = rp1_gpio_irq_enable,
++	.irq_disable = rp1_gpio_irq_disable,
++	.irq_set_type = rp1_gpio_irq_set_type,
++	.irq_ack = rp1_gpio_irq_ack,
++	.irq_mask = rp1_gpio_irq_disable,
++	.irq_unmask = rp1_gpio_irq_enable,
++	.flags = IRQCHIP_IMMUTABLE,
++};
++
++static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev)
++{
++	return ARRAY_SIZE(rp1_gpio_groups);
++}
++
++static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev,
++					   unsigned selector)
++{
++	return rp1_gpio_groups[selector];
++}
++
++static enum funcs rp1_get_fsel_func(unsigned pin, unsigned fsel)
++{
++	if (pin < RP1_NUM_GPIOS) {
++		if (fsel < RP1_FSEL_COUNT)
++			return rp1_gpio_pin_funcs[pin].funcs[fsel];
++		else if (fsel == RP1_FSEL_NONE)
++			return func_none;
++	}
++	return func_invalid;
++}
++
++static int rp1_pctl_get_group_pins(struct pinctrl_dev *pctldev,
++				   unsigned selector,
++				   const unsigned **pins,
++				   unsigned *num_pins)
++{
++	*pins = &rp1_gpio_pins[selector].number;
++	*num_pins = 1;
++
++	return 0;
++}
++
++static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
++				  struct seq_file *s,
++				  unsigned offset)
++{
++	struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	struct gpio_chip *chip = &pc->gpio_chip;
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++	u32 fsel = rp1_get_fsel(pin);
++	enum funcs func = rp1_get_fsel_func(offset, fsel);
++	int value = rp1_get_value(pin);
++	int irq = irq_find_mapping(chip->irq.domain, offset);
++
++	seq_printf(s, "function %s (%s) in %s; irq %d (%s)",
++		   rp1_func_names[fsel], rp1_func_names[func],
++		   value ? "hi" : "lo",
++		   irq, irq_type_names[pin->irq_type]);
++}
++
++static void rp1_pctl_dt_free_map(struct pinctrl_dev *pctldev,
++				 struct pinctrl_map *maps, unsigned num_maps)
++{
++	int i;
++
++	for (i = 0; i < num_maps; i++)
++		if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
++			kfree(maps[i].data.configs.configs);
++
++	kfree(maps);
++}
++
++static int rp1_pctl_legacy_map_func(struct rp1_pinctrl *pc,
++				    struct device_node *np, u32 pin, u32 fnum,
++				    struct pinctrl_map *maps,
++				    unsigned int *num_maps)
++{
++	struct pinctrl_map *map = &maps[*num_maps];
++	enum funcs func;
++
++	if (fnum >= ARRAY_SIZE(legacy_fsel_map[0])) {
++		dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum);
++		return -EINVAL;
++	}
++
++	func = legacy_fsel_map[pin][fnum];
++	if (func == func_invalid) {
++		dev_err(pc->dev, "%pOF: brcm,function %d not supported on pin %d\n",
++			np, fnum, pin);
++	}
++
++	map->type = PIN_MAP_TYPE_MUX_GROUP;
++	map->data.mux.group = rp1_gpio_groups[pin];
++	map->data.mux.function = rp1_func_names[func];
++	(*num_maps)++;
++
++	return 0;
++}
++
++static int rp1_pctl_legacy_map_pull(struct rp1_pinctrl *pc,
++				    struct device_node *np, u32 pin, u32 pull,
++				    struct pinctrl_map *maps,
++				    unsigned int *num_maps)
++{
++	struct pinctrl_map *map = &maps[*num_maps];
++	enum pin_config_param param;
++	unsigned long *configs;
++
++	switch (pull) {
++	case RP1_PUD_OFF:
++		param = PIN_CONFIG_BIAS_DISABLE;
++		break;
++	case RP1_PUD_DOWN:
++		param = PIN_CONFIG_BIAS_PULL_DOWN;
++		break;
++	case RP1_PUD_UP:
++		param = PIN_CONFIG_BIAS_PULL_UP;
++		break;
++	default:
++		dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull);
++		return -EINVAL;
++	}
++
++	configs = kzalloc(sizeof(*configs), GFP_KERNEL);
++	if (!configs)
++		return -ENOMEM;
++
++	configs[0] = pinconf_to_config_packed(param, 0);
++	map->type = PIN_MAP_TYPE_CONFIGS_PIN;
++	map->data.configs.group_or_pin = rp1_gpio_pins[pin].name;
++	map->data.configs.configs = configs;
++	map->data.configs.num_configs = 1;
++	(*num_maps)++;
++
++	return 0;
++}
++
++static int rp1_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
++				   struct device_node *np,
++				   struct pinctrl_map **map,
++				   unsigned int *num_maps)
++{
++	struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++	struct property *pins, *funcs, *pulls;
++	int num_pins, num_funcs, num_pulls, maps_per_pin;
++	struct pinctrl_map *maps;
++	unsigned long *configs = NULL;
++	const char *function = NULL;
++	unsigned int reserved_maps;
++	int num_configs = 0;
++	int i, err;
++	u32 pin, func, pull;
++
++	/* Check for legacy pin declaration */
++	pins = of_find_property(np, "brcm,pins", NULL);
++
++	if (!pins) /* Assume generic bindings in this node */
++		return pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps);
++
++	funcs = of_find_property(np, "brcm,function", NULL);
++	if (!funcs)
++		of_property_read_string(np, "function", &function);
++
++	pulls = of_find_property(np, "brcm,pull", NULL);
++	if (!pulls)
++		pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
++
++	if (!function && !funcs && !num_configs && !pulls) {
++		dev_err(pc->dev,
++			"%pOF: no function, brcm,function, brcm,pull, etc.\n",
++			np);
++		return -EINVAL;
++	}
++
++	num_pins = pins->length / 4;
++	num_funcs = funcs ? (funcs->length / 4) : 0;
++	num_pulls = pulls ? (pulls->length / 4) : 0;
++
++	if (num_funcs > 1 && num_funcs != num_pins) {
++		dev_err(pc->dev,
++			"%pOF: brcm,function must have 1 or %d entries\n",
++			np, num_pins);
++		return -EINVAL;
++	}
++
++	if (num_pulls > 1 && num_pulls != num_pins) {
++		dev_err(pc->dev,
++			"%pOF: brcm,pull must have 1 or %d entries\n",
++			np, num_pins);
++		return -EINVAL;
++	}
++
++	maps_per_pin = 0;
++	if (function || num_funcs)
++		maps_per_pin++;
++	if (num_configs || num_pulls)
++		maps_per_pin++;
++	reserved_maps = num_pins * maps_per_pin;
++	maps = kcalloc(reserved_maps, sizeof(*maps), GFP_KERNEL);
++	if (!maps)
++		return -ENOMEM;
++
++	*num_maps = 0;
++
++	for (i = 0; i < num_pins; i++) {
++		err = of_property_read_u32_index(np, "brcm,pins", i, &pin);
++		if (err)
++			goto out;
++		if (pin >= ARRAY_SIZE(legacy_fsel_map)) {
++			dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n",
++				np, pin);
++			err = -EINVAL;
++			goto out;
++		}
++
++		if (num_funcs) {
++			err = of_property_read_u32_index(np, "brcm,function",
++							 (num_funcs > 1) ? i : 0,
++							 &func);
++			if (err)
++				goto out;
++			err = rp1_pctl_legacy_map_func(pc, np, pin, func,
++						       maps, num_maps);
++		} else if (function) {
++			err = pinctrl_utils_add_map_mux(pctldev, &maps,
++							&reserved_maps, num_maps,
++							rp1_gpio_groups[pin],
++							function);
++		}
++
++		if (err)
++			goto out;
++
++		if (num_pulls) {
++			err = of_property_read_u32_index(np, "brcm,pull",
++							 (num_pulls > 1) ? i : 0,
++							 &pull);
++			if (err)
++				goto out;
++			err = rp1_pctl_legacy_map_pull(pc, np, pin, pull,
++						       maps, num_maps);
++		} else if (num_configs) {
++			err = pinctrl_utils_add_map_configs(pctldev, &maps,
++							    &reserved_maps, num_maps,
++							    rp1_gpio_groups[pin],
++							    configs, num_configs,
++							    PIN_MAP_TYPE_CONFIGS_PIN);
++		}
++
++		if (err)
++			goto out;
++	}
++
++	*map = maps;
++
++	return 0;
++
++out:
++	rp1_pctl_dt_free_map(pctldev, maps, reserved_maps);
++	return err;
++}
++
++static const struct pinctrl_ops rp1_pctl_ops = {
++	.get_groups_count = rp1_pctl_get_groups_count,
++	.get_group_name = rp1_pctl_get_group_name,
++	.get_group_pins = rp1_pctl_get_group_pins,
++	.pin_dbg_show = rp1_pctl_pin_dbg_show,
++	.dt_node_to_map = rp1_pctl_dt_node_to_map,
++	.dt_free_map = rp1_pctl_dt_free_map,
++};
++
++static int rp1_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
++{
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++	u32 fsel = rp1_get_fsel(pin);
++
++	/* Return non-GPIOs to GPIO_IN */
++	if (fsel != RP1_FSEL_GPIO) {
++		rp1_set_dir(pin, RP1_DIR_INPUT);
++		rp1_set_fsel(pin, RP1_FSEL_GPIO);
++	}
++
++	return 0;
++}
++
++static int rp1_pmx_get_functions_count(struct pinctrl_dev *pctldev)
++{
++	return func_count;
++}
++
++static const char *rp1_pmx_get_function_name(struct pinctrl_dev *pctldev,
++					     unsigned selector)
++{
++	return (selector < func_count) ? rp1_func_names[selector] : NULL;
++}
++
++static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev,
++				       unsigned selector,
++				       const char * const **groups,
++				       unsigned * const num_groups)
++{
++	/* every pin can do every function */
++	*groups = rp1_gpio_groups;
++	*num_groups = ARRAY_SIZE(rp1_gpio_groups);
++
++	return 0;
++}
++
++static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned func_selector,
++		       unsigned group_selector)
++{
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, group_selector);
++	const u8 *pin_funcs;
++	int fsel;
++
++	/* func_selector is an enum funcs, so needs translation */
++
++	if (func_selector >= RP1_FSEL_COUNT) {
++		/* Convert to an fsel number */
++		pin_funcs = rp1_gpio_pin_funcs[pin->num].funcs;
++		for (fsel = 0; fsel < RP1_FSEL_COUNT; fsel++) {
++			if (pin_funcs[fsel] == func_selector)
++				break;
++		}
++	} else {
++		fsel = (int)func_selector;
++	}
++
++	if (fsel >= RP1_FSEL_COUNT && fsel != RP1_FSEL_NONE)
++		return -EINVAL;
++
++	rp1_set_fsel(pin, fsel);
++
++	return 0;
++}
++
++static void rp1_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
++				      struct pinctrl_gpio_range *range,
++				      unsigned offset)
++{
++	(void)rp1_pmx_free(pctldev, offset);
++}
++
++static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
++				      struct pinctrl_gpio_range *range,
++				      unsigned offset,
++				      bool input)
++{
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++
++	rp1_set_dir(pin, input);
++	rp1_set_fsel(pin, RP1_FSEL_GPIO);
++
++	return 0;
++}
++
++static const struct pinmux_ops rp1_pmx_ops = {
++	.free = rp1_pmx_free,
++	.get_functions_count = rp1_pmx_get_functions_count,
++	.get_function_name = rp1_pmx_get_function_name,
++	.get_function_groups = rp1_pmx_get_function_groups,
++	.set_mux = rp1_pmx_set,
++	.gpio_disable_free = rp1_pmx_gpio_disable_free,
++	.gpio_set_direction = rp1_pmx_gpio_set_direction,
++};
++
++static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg)
++{
++	u32 padctrl = readl(pin->pad);
++
++	FLD_SET(padctrl, RP1_PAD_PULL, arg & 0x3);
++
++	writel(padctrl, pin->pad);
++}
++
++/* Generic pinconf methods */
++
++static int rp1_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset,
++			   unsigned long *configs, unsigned int num_configs)
++{
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++	u32 param, arg;
++	int i;
++
++	if (!pin)
++		return -EINVAL;
++
++	for (i = 0; i < num_configs; i++) {
++		param = pinconf_to_config_param(configs[i]);
++		arg = pinconf_to_config_argument(configs[i]);
++
++		switch (param) {
++		case PIN_CONFIG_BIAS_DISABLE:
++			rp1_pull_config_set(pin, RP1_PUD_OFF);
++			break;
++
++		case PIN_CONFIG_BIAS_PULL_DOWN:
++			rp1_pull_config_set(pin, RP1_PUD_DOWN);
++			break;
++
++		case PIN_CONFIG_BIAS_PULL_UP:
++			rp1_pull_config_set(pin, RP1_PUD_UP);
++			break;
++
++		case PIN_CONFIG_INPUT_ENABLE:
++			rp1_input_enable(pin, arg);
++			break;
++
++		case PIN_CONFIG_OUTPUT_ENABLE:
++			rp1_output_enable(pin, arg);
++			break;
++
++		case PIN_CONFIG_OUTPUT:
++			rp1_set_value(pin, arg);
++			rp1_set_dir(pin, RP1_DIR_OUTPUT);
++			rp1_set_fsel(pin, RP1_FSEL_GPIO);
++			break;
++
++		case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
++			rp1_pad_update(pin, RP1_PAD_SCHMITT_MASK,
++				       arg ? RP1_PAD_SCHMITT_MASK : 0);
++			break;
++
++		case PIN_CONFIG_SLEW_RATE:
++			rp1_pad_update(pin, RP1_PAD_SLEWFAST_MASK,
++				       arg ? RP1_PAD_SLEWFAST_MASK : 0);
++			break;
++
++		case PIN_CONFIG_DRIVE_STRENGTH:
++			switch (arg) {
++			case 2:
++				arg = RP1_PAD_DRIVE_2MA;
++				break;
++			case 4:
++				arg = RP1_PAD_DRIVE_4MA;
++				break;
++			case 8:
++				arg = RP1_PAD_DRIVE_8MA;
++				break;
++			case 12:
++				arg = RP1_PAD_DRIVE_12MA;
++				break;
++			default:
++				return -ENOTSUPP;
++			}
++			rp1_pad_update(pin, RP1_PAD_DRIVE_MASK, arg);
++			break;
++
++		default:
++			return -ENOTSUPP;
++
++		} /* switch param type */
++	} /* for each config */
++
++	return 0;
++}
++
++static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned offset,
++			   unsigned long *config)
++{
++	struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++	enum pin_config_param param = pinconf_to_config_param(*config);
++	u32 padctrl;
++	u32 arg;
++
++	if (!pin)
++		return -EINVAL;
++
++	padctrl = readl(pin->pad);
++
++	switch (param) {
++	case PIN_CONFIG_INPUT_ENABLE:
++		arg = !!(padctrl & RP1_PAD_IN_ENABLE_MASK);
++		break;
++	case PIN_CONFIG_OUTPUT_ENABLE:
++		arg = !(padctrl & RP1_PAD_OUT_DISABLE_MASK);
++		break;
++	case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
++		arg = !!(padctrl & RP1_PAD_SCHMITT_MASK);
++		break;
++	case PIN_CONFIG_SLEW_RATE:
++		arg = !!(padctrl & RP1_PAD_SLEWFAST_MASK);
++		break;
++	case PIN_CONFIG_DRIVE_STRENGTH:
++		switch (padctrl & RP1_PAD_DRIVE_MASK) {
++		case RP1_PAD_DRIVE_2MA:
++			arg = 2;
++			break;
++		case RP1_PAD_DRIVE_4MA:
++			arg = 4;
++			break;
++		case RP1_PAD_DRIVE_8MA:
++			arg = 8;
++			break;
++		case RP1_PAD_DRIVE_12MA:
++			arg = 12;
++			break;
++		}
++		break;
++	case PIN_CONFIG_BIAS_DISABLE:
++		arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_OFF << RP1_PAD_PULL_LSB));
++		break;
++	case PIN_CONFIG_BIAS_PULL_DOWN:
++		arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_DOWN << RP1_PAD_PULL_LSB));
++		break;
++
++	case PIN_CONFIG_BIAS_PULL_UP:
++		arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_UP << RP1_PAD_PULL_LSB));
++		break;
++	default:
++		return -ENOTSUPP;
++	}
++
++	*config = pinconf_to_config_packed(param, arg);
++
++	return 0;
++}
++
++static const struct pinconf_ops rp1_pinconf_ops = {
++	.is_generic = true,
++	.pin_config_get = rp1_pinconf_get,
++	.pin_config_set = rp1_pinconf_set,
++};
++
++static struct pinctrl_desc rp1_pinctrl_desc = {
++	.name = MODULE_NAME,
++	.pins = rp1_gpio_pins,
++	.npins = ARRAY_SIZE(rp1_gpio_pins),
++	.pctlops = &rp1_pctl_ops,
++	.pmxops = &rp1_pmx_ops,
++	.confops = &rp1_pinconf_ops,
++	.owner = THIS_MODULE,
++};
++
++static struct pinctrl_gpio_range rp1_pinctrl_gpio_range = {
++	.name = MODULE_NAME,
++	.npins = RP1_NUM_GPIOS,
++};
++
++static const struct of_device_id rp1_pinctrl_match[] = {
++	{
++		.compatible = "raspberrypi,rp1-gpio",
++		.data = &rp1_pinconf_ops,
++	},
++	{}
++};
++
++static inline void __iomem *devm_auto_iomap(struct platform_device *pdev,
++					    unsigned int index)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++
++	if (np)
++		return devm_of_iomap(dev, np, (int)index, NULL);
++	else
++		return devm_platform_ioremap_resource(pdev, index);
++}
++
++static int rp1_pinctrl_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++	struct rp1_pinctrl *pc;
++	struct gpio_irq_chip *girq;
++	int err, i;
++
++	BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_pins) != RP1_NUM_GPIOS);
++	BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_groups) != RP1_NUM_GPIOS);
++
++	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
++	if (!pc)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, pc);
++	pc->dev = dev;
++
++	pc->gpio_base = devm_auto_iomap(pdev, 0);
++	if (IS_ERR(pc->gpio_base)) {
++		dev_err(dev, "could not get GPIO IO memory\n");
++		return PTR_ERR(pc->gpio_base);
++	}
++
++	pc->rio_base = devm_auto_iomap(pdev, 1);
++	if (IS_ERR(pc->rio_base)) {
++		dev_err(dev, "could not get RIO IO memory\n");
++		return PTR_ERR(pc->rio_base);
++	}
++
++	pc->pads_base = devm_auto_iomap(pdev, 2);
++	if (IS_ERR(pc->pads_base)) {
++		dev_err(dev, "could not get PADS IO memory\n");
++		return PTR_ERR(pc->pads_base);
++	}
++
++	pc->gpio_chip = rp1_gpio_chip;
++	pc->gpio_chip.parent = dev;
++
++	for (i = 0; i < RP1_NUM_BANKS; i++) {
++		const struct rp1_iobank_desc *bank = &rp1_iobanks[i];
++		int j;
++
++		for (j = 0; j < bank->num_gpios; j++) {
++			struct rp1_pin_info *pin =
++				&pc->pins[bank->min_gpio + j];
++
++			pin->num = bank->min_gpio + j;
++			pin->bank = i;
++			pin->offset = j;
++
++			pin->gpio = pc->gpio_base + bank->gpio_offset +
++				    j * sizeof(u32) * 2;
++			pin->inte = pc->gpio_base + bank->inte_offset;
++			pin->ints = pc->gpio_base + bank->ints_offset;
++			pin->rio  = pc->rio_base + bank->rio_offset;
++			pin->pad  = pc->pads_base + bank->pads_offset +
++				    j * sizeof(u32);
++		}
++
++		raw_spin_lock_init(&pc->irq_lock[i]);
++	}
++
++	pc->pctl_dev = devm_pinctrl_register(dev, &rp1_pinctrl_desc, pc);
++	if (IS_ERR(pc->pctl_dev))
++		return PTR_ERR(pc->pctl_dev);
++
++	girq = &pc->gpio_chip.irq;
++	girq->chip = &rp1_gpio_irq_chip;
++	girq->parent_handler = rp1_gpio_irq_handler;
++	girq->num_parents = RP1_NUM_BANKS;
++	girq->parents = pc->irq;
++
++	/*
++	 * Use the same handler for all groups: this is necessary
++	 * since we use one gpiochip to cover all lines - the
++	 * irq handler then needs to figure out which group and
++	 * bank that was firing the IRQ and look up the per-group
++	 * and bank data.
++	 */
++	for (i = 0; i < RP1_NUM_BANKS; i++) {
++		pc->irq[i] = irq_of_parse_and_map(np, i);
++		if (!pc->irq[i]) {
++			girq->num_parents = i;
++			break;
++		}
++	}
++
++	girq->default_type = IRQ_TYPE_NONE;
++	girq->handler = handle_level_irq;
++
++	err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
++	if (err) {
++		dev_err(dev, "could not add GPIO chip\n");
++		return err;
++	}
++
++	pc->gpio_range = rp1_pinctrl_gpio_range;
++	pc->gpio_range.base = pc->gpio_chip.base;
++	pc->gpio_range.gc = &pc->gpio_chip;
++	pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
++
++	return 0;
++}
++
++static struct platform_driver rp1_pinctrl_driver = {
++	.probe = rp1_pinctrl_probe,
++	.driver = {
++		.name = MODULE_NAME,
++		.of_match_table = rp1_pinctrl_match,
++		.suppress_bind_attrs = true,
++	},
++};
++builtin_platform_driver(rp1_pinctrl_driver);
+--- a/include/dt-bindings/pinctrl/rp1.h
++++ /dev/null
+@@ -1,46 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-/*
+- * Header providing constants for RP1 pinctrl bindings.
+- *
+- * Copyright (C) 2019-2022 Raspberry Pi Ltd.
+- */
+-
+-#ifndef __DT_BINDINGS_PINCTRL_RP1_H__
+-#define __DT_BINDINGS_PINCTRL_RP1_H__
+-
+-/* brcm,function property */
+-#define RP1_FSEL_GPIO_IN	0
+-#define RP1_FSEL_GPIO_OUT	1
+-#define RP1_FSEL_ALT0_LEGACY	4
+-#define RP1_FSEL_ALT1_LEGACY	5
+-#define RP1_FSEL_ALT2_LEGACY	6
+-#define RP1_FSEL_ALT3_LEGACY	7
+-#define RP1_FSEL_ALT4_LEGACY	3
+-#define RP1_FSEL_ALT5_LEGACY	2
+-#define RP1_FSEL_ALT0		0x08
+-#define RP1_FSEL_ALT0INV	0x09
+-#define RP1_FSEL_ALT1		0x0a
+-#define RP1_FSEL_ALT1INV	0x0b
+-#define RP1_FSEL_ALT2		0x0c
+-#define RP1_FSEL_ALT2INV	0x0d
+-#define RP1_FSEL_ALT3		0x0e
+-#define RP1_FSEL_ALT3INV	0x0f
+-#define RP1_FSEL_ALT4		0x10
+-#define RP1_FSEL_ALT4INV	0x11
+-#define RP1_FSEL_ALT5		0x12
+-#define RP1_FSEL_ALT5INV	0x13
+-#define RP1_FSEL_ALT6		0x14
+-#define RP1_FSEL_ALT6INV	0x15
+-#define RP1_FSEL_ALT7		0x16
+-#define RP1_FSEL_ALT7INV	0x17
+-#define RP1_FSEL_ALT8		0x18
+-#define RP1_FSEL_ALT8INV	0x19
+-#define RP1_FSEL_NONE		0x1a
+-
+-/* brcm,pull property */
+-#define RP1_PUD_OFF		0
+-#define RP1_PUD_DOWN		1
+-#define RP1_PUD_UP		2
+-#define RP1_PUD_KEEP		3
+-
+-#endif /* __DT_BINDINGS_PINCTRL_RP1_H__ */

+ 129 - 0
target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch

@@ -0,0 +1,129 @@
+From f88da9e21d8eff58eeb9280ae96bf9593121d8eb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 12 Oct 2022 13:24:51 +0100
+Subject: [PATCH] serial: pl011: rp1 uart support
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/tty/serial/amba-pl011.c | 96 +++++++++++++++++++++++++++++++++
+ 1 file changed, 96 insertions(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -152,6 +152,20 @@ static const struct vendor_data vendor_s
+ 	.fixed_options		= true,
+ };
+ 
++static struct vendor_data vendor_arm_axi = {
++	.reg_offset		= pl011_std_offsets,
++	.ifls			= UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
++	.fr_busy		= UART01x_FR_BUSY,
++	.fr_dsr			= UART01x_FR_DSR,
++	.fr_cts			= UART01x_FR_CTS,
++	.fr_ri			= UART011_FR_RI,
++	.oversampling		= false,
++	.dma_threshold		= false,
++	.cts_event_workaround	= false,
++	.always_enabled		= false,
++	.fixed_options		= false,
++};
++
+ #ifdef CONFIG_ACPI_SPCR_TABLE
+ static const struct vendor_data vendor_qdt_qdf2400_e44 = {
+ 	.reg_offset		= pl011_std_offsets,
+@@ -2972,6 +2986,86 @@ static struct platform_driver arm_sbsa_u
+ 	},
+ };
+ 
++static int pl011_axi_probe(struct platform_device *pdev)
++{
++	struct uart_amba_port *uap;
++	struct vendor_data *vendor =  &vendor_arm_axi;
++	struct resource *r;
++	unsigned int periphid;
++	int portnr, ret, irq;
++
++	portnr = pl011_find_free_port();
++	if (portnr < 0)
++		return portnr;
++
++	uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port),
++			   GFP_KERNEL);
++	if (!uap)
++		return -ENOMEM;
++
++	uap->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(uap->clk))
++		return PTR_ERR(uap->clk);
++
++	if (of_property_read_bool(pdev->dev.of_node, "cts-event-workaround")) {
++		vendor->cts_event_workaround = true;
++		dev_info(&pdev->dev, "cts_event_workaround enabled\n");
++	}
++
++	irq = platform_get_irq(pdev, 0);
++	if (irq < 0)
++		return irq;
++
++	periphid = 0x00241011; /* A safe default */
++	of_property_read_u32(pdev->dev.of_node, "arm,primecell-periphid",
++			     &periphid);
++
++	uap->reg_offset = vendor->reg_offset;
++	uap->vendor = vendor;
++	uap->fifosize = (AMBA_REV_BITS(periphid) < 3) ? 16 : 32;
++	uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
++	uap->port.irq = irq;
++	uap->port.ops = &amba_pl011_pops;
++
++	snprintf(uap->type, sizeof(uap->type), "PL011 AXI");
++
++	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++	ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
++	if (ret)
++		return ret;
++
++	platform_set_drvdata(pdev, uap);
++
++	return pl011_register_port(uap);
++}
++
++static int pl011_axi_remove(struct platform_device *pdev)
++{
++	struct uart_amba_port *uap = platform_get_drvdata(pdev);
++
++	uart_remove_one_port(&amba_reg, &uap->port);
++	pl011_unregister_port(uap);
++	return 0;
++}
++
++static const struct of_device_id pl011_axi_of_match[] = {
++	{ .compatible = "arm,pl011-axi" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, pl011_axi_of_match);
++
++static struct platform_driver pl011_axi_platform_driver = {
++	.probe		= pl011_axi_probe,
++	.remove		= pl011_axi_remove,
++	.driver	= {
++		.name	= "pl011-axi",
++		.pm	= &pl011_dev_pm_ops,
++		.of_match_table = of_match_ptr(pl011_axi_of_match),
++		.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
++	},
++};
++
+ static const struct amba_id pl011_ids[] = {
+ 	{
+ 		.id	= 0x00041011,
+@@ -3005,6 +3099,8 @@ static int __init pl011_init(void)
+ 
+ 	if (platform_driver_register(&arm_sbsa_uart_platform_driver))
+ 		pr_warn("could not register SBSA UART platform driver\n");
++	if (platform_driver_register(&pl011_axi_platform_driver))
++		pr_warn("could not register PL011 AXI platform driver\n");
+ 	return amba_driver_register(&pl011_driver);
+ }
+ 

+ 83 - 0
target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch

@@ -0,0 +1,83 @@
+From 4a5ac516ca0a820e7c006ae408872009e37e114b Mon Sep 17 00:00:00 2001
+From: Liam Fraser <[email protected]>
+Date: Thu, 14 Mar 2019 16:01:26 +0000
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: define sdio timeout clocks
+
+Signed-off-by: Liam Fraser <[email protected]>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 12 ++++++++++++
+ drivers/mmc/host/sdhci-pltfm.c      |  8 ++++++++
+ drivers/mmc/host/sdhci-pltfm.h      |  3 +++
+ 3 files changed, 23 insertions(+)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -330,6 +330,7 @@ static const struct sdhci_ops sdhci_dwcm
+ 	.set_bus_width		= sdhci_set_bus_width,
+ 	.set_uhs_signaling	= dwcmshc_set_uhs_signaling,
+ 	.get_max_clock		= dwcmshc_get_max_clock,
++	.get_timeout_clock	= sdhci_pltfm_clk_get_timeout_clock,
+ 	.reset			= sdhci_reset,
+ 	.adma_write_desc	= dwcmshc_adma_write_desc,
+ };
+@@ -500,6 +501,16 @@ static int dwcmshc_probe(struct platform
+ 			clk_prepare_enable(priv->bus_clk);
+ 	}
+ 
++	pltfm_host->timeout_clk = devm_clk_get(&pdev->dev, "timeout");
++	if (IS_ERR(pltfm_host->timeout_clk)) {
++		err = PTR_ERR(pltfm_host->timeout_clk);
++		dev_err(&pdev->dev, "failed to get timeout clk: %d\n", err);
++		goto free_pltfm;
++	}
++	err = clk_prepare_enable(pltfm_host->timeout_clk);
++	if (err)
++		goto free_pltfm;
++
+ 	err = mmc_of_parse(host->mmc);
+ 	if (err)
+ 		goto err_clk;
+@@ -550,6 +561,7 @@ err_setup_host:
+ 	sdhci_cleanup_host(host);
+ err_clk:
+ 	clk_disable_unprepare(pltfm_host->clk);
++	clk_disable_unprepare(pltfm_host->timeout_clk);
+ 	clk_disable_unprepare(priv->bus_clk);
+ 	if (rk_priv)
+ 		clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
+--- a/drivers/mmc/host/sdhci-pltfm.c
++++ b/drivers/mmc/host/sdhci-pltfm.c
+@@ -33,6 +33,14 @@ unsigned int sdhci_pltfm_clk_get_max_clo
+ }
+ EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
+ 
++unsigned int sdhci_pltfm_clk_get_timeout_clock(struct sdhci_host *host)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++
++	return clk_get_rate(pltfm_host->timeout_clk);
++}
++EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_timeout_clock);
++
+ static const struct sdhci_ops sdhci_pltfm_ops = {
+ 	.set_clock = sdhci_set_clock,
+ 	.set_bus_width = sdhci_set_bus_width,
+--- a/drivers/mmc/host/sdhci-pltfm.h
++++ b/drivers/mmc/host/sdhci-pltfm.h
+@@ -20,6 +20,7 @@ struct sdhci_pltfm_data {
+ 
+ struct sdhci_pltfm_host {
+ 	struct clk *clk;
++	struct clk *timeout_clk;
+ 
+ 	/* migrate from sdhci_of_host */
+ 	unsigned int clock;
+@@ -106,6 +107,8 @@ extern int sdhci_pltfm_unregister(struct
+ 
+ extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
+ 
++extern unsigned int sdhci_pltfm_clk_get_timeout_clock(struct sdhci_host *host);
++
+ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
+ {
+ 	return host->private;

+ 83 - 0
target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch

@@ -0,0 +1,83 @@
+From 14a43b3fd43bf9b230f93d1eba276d40aac969ba Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 12 Oct 2022 14:07:32 +0100
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: rp1 sdio changes
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 29 ++++++++++++++++++++++++++---
+ 1 file changed, 26 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -87,6 +87,7 @@ struct rk35xx_priv {
+ 
+ struct dwcmshc_priv {
+ 	struct clk	*bus_clk;
++	struct clk	*sdio_clk;
+ 	int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */
+ 	void *priv; /* pointer to SoC private stuff */
+ };
+@@ -114,6 +115,17 @@ static void dwcmshc_adma_write_desc(stru
+ 	sdhci_adma_write_desc(host, desc, addr, len, cmd);
+ }
+ 
++static void dwcmshc_set_clock(struct sdhci_host *host, unsigned int clock)
++{
++	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
++
++	if (priv->sdio_clk)
++		clk_set_rate(priv->sdio_clk, clock);
++
++	sdhci_set_clock(host, clock);
++}
++
+ static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
+ {
+ 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+@@ -326,7 +338,7 @@ static void rk35xx_sdhci_reset(struct sd
+ }
+ 
+ static const struct sdhci_ops sdhci_dwcmshc_ops = {
+-	.set_clock		= sdhci_set_clock,
++	.set_clock		= dwcmshc_set_clock,
+ 	.set_bus_width		= sdhci_set_bus_width,
+ 	.set_uhs_signaling	= dwcmshc_set_uhs_signaling,
+ 	.get_max_clock		= dwcmshc_get_max_clock,
+@@ -346,8 +358,10 @@ static const struct sdhci_ops sdhci_dwcm
+ 
+ static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
+ 	.ops = &sdhci_dwcmshc_ops,
+-	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+-	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
++	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
++		  SDHCI_QUIRK_BROKEN_CARD_DETECTION,
++	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
++		   SDHCI_QUIRK2_BROKEN_HS200,
+ };
+ 
+ #ifdef CONFIG_ACPI
+@@ -499,6 +513,14 @@ static int dwcmshc_probe(struct platform
+ 		priv->bus_clk = devm_clk_get(dev, "bus");
+ 		if (!IS_ERR(priv->bus_clk))
+ 			clk_prepare_enable(priv->bus_clk);
++
++		pltfm_host->timeout_clk = devm_clk_get(dev, "timeout");
++		if (!IS_ERR(pltfm_host->timeout_clk))
++			err = clk_prepare_enable(pltfm_host->timeout_clk);
++		if (err)
++			goto free_pltfm;
++
++		priv->sdio_clk = devm_clk_get_optional(&pdev->dev, "sdio");
+ 	}
+ 
+ 	pltfm_host->timeout_clk = devm_clk_get(&pdev->dev, "timeout");
+@@ -516,6 +538,7 @@ static int dwcmshc_probe(struct platform
+ 		goto err_clk;
+ 
+ 	sdhci_get_of_property(pdev);
++	sdhci_enable_v4_mode(host);
+ 
+ 	priv->vendor_specific_area1 =
+ 		sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK;

+ 641 - 0
target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch

@@ -0,0 +1,641 @@
+From b427cc1a83404f48b12dec2efbef076b38df6218 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 12 Oct 2022 14:20:07 +0100
+Subject: [PATCH] clk: rp1: Add sdio-clk driver
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/clk/Kconfig        |   6 +
+ drivers/clk/Makefile       |   1 +
+ drivers/clk/clk-rp1-sdio.c | 600 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 607 insertions(+)
+ create mode 100644 drivers/clk/clk-rp1-sdio.c
+
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -96,6 +96,12 @@ config COMMON_CLK_RP1
+ 	help
+ 	  Enable common clock framework support for Raspberry Pi RP1
+ 
++config COMMON_CLK_RP1_SDIO
++	tristate "Clock driver for the RP1 SDIO interfaces"
++	depends on MFD_RP1
++	help
++	  SDIO clock driver for the RP1 support chip
++
+ config COMMON_CLK_HI655X
+ 	tristate "Clock driver for Hi655x" if EXPERT
+ 	depends on (MFD_HI655X_PMIC || COMPILE_TEST)
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -59,6 +59,7 @@ obj-$(CONFIG_COMMON_CLK_PWM)		+= clk-pwm
+ obj-$(CONFIG_CLK_QORIQ)			+= clk-qoriq.o
+ obj-$(CONFIG_COMMON_CLK_RK808)		+= clk-rk808.o
+ obj-$(CONFIG_COMMON_CLK_RP1)		+= clk-rp1.o
++obj-$(CONFIG_COMMON_CLK_RP1_SDIO)	+= clk-rp1-sdio.o
+ obj-$(CONFIG_COMMON_CLK_HI655X)		+= clk-hi655x.o
+ obj-$(CONFIG_COMMON_CLK_S2MPS11)	+= clk-s2mps11.o
+ obj-$(CONFIG_COMMON_CLK_SCMI)           += clk-scmi.o
+--- /dev/null
++++ b/drivers/clk/clk-rp1-sdio.c
+@@ -0,0 +1,600 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * SDIO clock driver for RP1
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ */
++
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++// Register    : MODE
++#define MODE        0x00000000
++#define MODE_BITS   0x70030000
++#define MODE_RESET  0x00000000
++// Field       : MODE_STEPS_PER_CYCLE
++#define MODE_STEPS_PER_CYCLE_RESET          0x0
++#define MODE_STEPS_PER_CYCLE_BITS           0x70000000
++#define MODE_STEPS_PER_CYCLE_MSB            30
++#define MODE_STEPS_PER_CYCLE_LSB            28
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_20 0x0
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_10 0x1
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_16 0x2
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_8  0x3
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_12 0x4
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_6  0x5
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_5  0x6
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_4  0x7
++// Field       : MODE_SRC_SEL
++#define MODE_SRC_SEL_RESET                   0x0
++#define MODE_SRC_SEL_BITS                    0x00030000
++#define MODE_SRC_SEL_MSB                     17
++#define MODE_SRC_SEL_LSB                     16
++#define MODE_SRC_SEL_VALUE_STOP              0x0
++#define MODE_SRC_SEL_VALUE_CLK_ALT_SRC       0x1
++#define MODE_SRC_SEL_VALUE_PLL_SYS_VCO       0x2
++#define MODE_SRC_SEL_VALUE_PLL_SYS_VCO_AGAIN 0x3
++// Register    : FROMIP
++#define FROMIP        0x00000004
++#define FROMIP_BITS   0x0f9713ff
++#define FROMIP_RESET  0x00000000
++// Field       : FROMIP_TUNING_CCLK_SEL
++#define FROMIP_TUNING_CCLK_SEL_RESET  0x0
++#define FROMIP_TUNING_CCLK_SEL_BITS   0x0f000000
++#define FROMIP_TUNING_CCLK_SEL_MSB    27
++#define FROMIP_TUNING_CCLK_SEL_LSB    24
++// Field       : FROMIP_TUNING_CCLK_UPDATE
++#define FROMIP_TUNING_CCLK_UPDATE_RESET  0x0
++#define FROMIP_TUNING_CCLK_UPDATE_BITS   0x00800000
++#define FROMIP_TUNING_CCLK_UPDATE_MSB    23
++#define FROMIP_TUNING_CCLK_UPDATE_LSB    23
++// Field       : FROMIP_SAMPLE_CCLK_SEL
++#define FROMIP_SAMPLE_CCLK_SEL_RESET  0x0
++#define FROMIP_SAMPLE_CCLK_SEL_BITS   0x00100000
++#define FROMIP_SAMPLE_CCLK_SEL_MSB    20
++#define FROMIP_SAMPLE_CCLK_SEL_LSB    20
++// Field       : FROMIP_CLK2CARD_ON
++#define FROMIP_CLK2CARD_ON_RESET  0x0
++#define FROMIP_CLK2CARD_ON_BITS   0x00040000
++#define FROMIP_CLK2CARD_ON_MSB    18
++#define FROMIP_CLK2CARD_ON_LSB    18
++// Field       : FROMIP_CARD_CLK_STABLE
++#define FROMIP_CARD_CLK_STABLE_RESET  0x0
++#define FROMIP_CARD_CLK_STABLE_BITS   0x00020000
++#define FROMIP_CARD_CLK_STABLE_MSB    17
++#define FROMIP_CARD_CLK_STABLE_LSB    17
++// Field       : FROMIP_CARD_CLK_EN
++#define FROMIP_CARD_CLK_EN_RESET  0x0
++#define FROMIP_CARD_CLK_EN_BITS   0x00010000
++#define FROMIP_CARD_CLK_EN_MSB    16
++#define FROMIP_CARD_CLK_EN_LSB    16
++// Field       : FROMIP_CLK_GEN_SEL
++#define FROMIP_CLK_GEN_SEL_RESET  0x0
++#define FROMIP_CLK_GEN_SEL_BITS   0x00001000
++#define FROMIP_CLK_GEN_SEL_MSB    12
++#define FROMIP_CLK_GEN_SEL_LSB    12
++// Field       : FROMIP_FREQ_SEL
++#define FROMIP_FREQ_SEL_RESET  0x000
++#define FROMIP_FREQ_SEL_BITS   0x000003ff
++#define FROMIP_FREQ_SEL_MSB    9
++#define FROMIP_FREQ_SEL_LSB    0
++// Register    : LOCAL
++#define LOCAL        0x00000008
++#define LOCAL_BITS   0x1f9713ff
++#define LOCAL_RESET  0x00000000
++// Field       : LOCAL_TUNING_CCLK_SEL
++#define LOCAL_TUNING_CCLK_SEL_RESET  0x00
++#define LOCAL_TUNING_CCLK_SEL_BITS   0x1f000000
++#define LOCAL_TUNING_CCLK_SEL_MSB    28
++#define LOCAL_TUNING_CCLK_SEL_LSB    24
++// Field       : LOCAL_TUNING_CCLK_UPDATE
++#define LOCAL_TUNING_CCLK_UPDATE_RESET  0x0
++#define LOCAL_TUNING_CCLK_UPDATE_BITS   0x00800000
++#define LOCAL_TUNING_CCLK_UPDATE_MSB    23
++#define LOCAL_TUNING_CCLK_UPDATE_LSB    23
++// Field       : LOCAL_SAMPLE_CCLK_SEL
++#define LOCAL_SAMPLE_CCLK_SEL_RESET  0x0
++#define LOCAL_SAMPLE_CCLK_SEL_BITS   0x00100000
++#define LOCAL_SAMPLE_CCLK_SEL_MSB    20
++#define LOCAL_SAMPLE_CCLK_SEL_LSB    20
++// Field       : LOCAL_CLK2CARD_ON
++#define LOCAL_CLK2CARD_ON_RESET  0x0
++#define LOCAL_CLK2CARD_ON_BITS   0x00040000
++#define LOCAL_CLK2CARD_ON_MSB    18
++#define LOCAL_CLK2CARD_ON_LSB    18
++// Field       : LOCAL_CARD_CLK_STABLE
++#define LOCAL_CARD_CLK_STABLE_RESET  0x0
++#define LOCAL_CARD_CLK_STABLE_BITS   0x00020000
++#define LOCAL_CARD_CLK_STABLE_MSB    17
++#define LOCAL_CARD_CLK_STABLE_LSB    17
++// Field       : LOCAL_CARD_CLK_EN
++#define LOCAL_CARD_CLK_EN_RESET  0x0
++#define LOCAL_CARD_CLK_EN_BITS   0x00010000
++#define LOCAL_CARD_CLK_EN_MSB    16
++#define LOCAL_CARD_CLK_EN_LSB    16
++// Field       : LOCAL_CLK_GEN_SEL
++#define LOCAL_CLK_GEN_SEL_RESET               0x0
++#define LOCAL_CLK_GEN_SEL_BITS                0x00001000
++#define LOCAL_CLK_GEN_SEL_MSB                 12
++#define LOCAL_CLK_GEN_SEL_LSB                 12
++#define LOCAL_CLK_GEN_SEL_VALUE_PROGCLOCKMODE 0x0
++#define LOCAL_CLK_GEN_SEL_VALUE_DIVCLOCKMODE  0x1
++// Field       : LOCAL_FREQ_SEL
++#define LOCAL_FREQ_SEL_RESET  0x000
++#define LOCAL_FREQ_SEL_BITS   0x000003ff
++#define LOCAL_FREQ_SEL_MSB    9
++#define LOCAL_FREQ_SEL_LSB    0
++// Register    : USE_LOCAL
++#define USE_LOCAL        0x0000000c
++#define USE_LOCAL_BITS   0x01951001
++#define USE_LOCAL_RESET  0x00000000
++// Field       : USE_LOCAL_TUNING_CCLK_SEL
++#define USE_LOCAL_TUNING_CCLK_SEL_RESET  0x0
++#define USE_LOCAL_TUNING_CCLK_SEL_BITS   0x01000000
++#define USE_LOCAL_TUNING_CCLK_SEL_MSB    24
++#define USE_LOCAL_TUNING_CCLK_SEL_LSB    24
++// Field       : USE_LOCAL_TUNING_CCLK_UPDATE
++#define USE_LOCAL_TUNING_CCLK_UPDATE_RESET  0x0
++#define USE_LOCAL_TUNING_CCLK_UPDATE_BITS   0x00800000
++#define USE_LOCAL_TUNING_CCLK_UPDATE_MSB    23
++#define USE_LOCAL_TUNING_CCLK_UPDATE_LSB    23
++// Field       : USE_LOCAL_SAMPLE_CCLK_SEL
++#define USE_LOCAL_SAMPLE_CCLK_SEL_RESET  0x0
++#define USE_LOCAL_SAMPLE_CCLK_SEL_BITS   0x00100000
++#define USE_LOCAL_SAMPLE_CCLK_SEL_MSB    20
++#define USE_LOCAL_SAMPLE_CCLK_SEL_LSB    20
++// Field       : USE_LOCAL_CLK2CARD_ON
++#define USE_LOCAL_CLK2CARD_ON_RESET  0x0
++#define USE_LOCAL_CLK2CARD_ON_BITS   0x00040000
++#define USE_LOCAL_CLK2CARD_ON_MSB    18
++#define USE_LOCAL_CLK2CARD_ON_LSB    18
++// Field       : USE_LOCAL_CARD_CLK_EN
++#define USE_LOCAL_CARD_CLK_EN_RESET  0x0
++#define USE_LOCAL_CARD_CLK_EN_BITS   0x00010000
++#define USE_LOCAL_CARD_CLK_EN_MSB    16
++#define USE_LOCAL_CARD_CLK_EN_LSB    16
++// Field       : USE_LOCAL_CLK_GEN_SEL
++#define USE_LOCAL_CLK_GEN_SEL_RESET  0x0
++#define USE_LOCAL_CLK_GEN_SEL_BITS   0x00001000
++#define USE_LOCAL_CLK_GEN_SEL_MSB    12
++#define USE_LOCAL_CLK_GEN_SEL_LSB    12
++// Field       : USE_LOCAL_FREQ_SEL
++#define USE_LOCAL_FREQ_SEL_RESET  0x0
++#define USE_LOCAL_FREQ_SEL_BITS   0x00000001
++#define USE_LOCAL_FREQ_SEL_MSB    0
++#define USE_LOCAL_FREQ_SEL_LSB    0
++// Register    : SD_DELAY
++#define SD_DELAY        0x00000010
++#define SD_DELAY_BITS   0x0000001f
++#define SD_DELAY_RESET  0x00000000
++// Field       : SD_DELAY_STEPS
++#define SD_DELAY_STEPS_RESET  0x00
++#define SD_DELAY_STEPS_BITS   0x0000001f
++#define SD_DELAY_STEPS_MSB    4
++#define SD_DELAY_STEPS_LSB    0
++// Register    : RX_DELAY
++#define RX_DELAY        0x00000014
++#define RX_DELAY_BITS   0x19f3331f
++#define RX_DELAY_RESET  0x00000000
++// Field       : RX_DELAY_BYPASS
++#define RX_DELAY_BYPASS_RESET  0x0
++#define RX_DELAY_BYPASS_BITS   0x10000000
++#define RX_DELAY_BYPASS_MSB    28
++#define RX_DELAY_BYPASS_LSB    28
++// Field       : RX_DELAY_FAIL_ACTUAL
++#define RX_DELAY_FAIL_ACTUAL_RESET  0x0
++#define RX_DELAY_FAIL_ACTUAL_BITS   0x08000000
++#define RX_DELAY_FAIL_ACTUAL_MSB    27
++#define RX_DELAY_FAIL_ACTUAL_LSB    27
++// Field       : RX_DELAY_ACTUAL
++#define RX_DELAY_ACTUAL_RESET  0x00
++#define RX_DELAY_ACTUAL_BITS   0x01f00000
++#define RX_DELAY_ACTUAL_MSB    24
++#define RX_DELAY_ACTUAL_LSB    20
++// Field       : RX_DELAY_OFFSET
++#define RX_DELAY_OFFSET_RESET  0x0
++#define RX_DELAY_OFFSET_BITS   0x00030000
++#define RX_DELAY_OFFSET_MSB    17
++#define RX_DELAY_OFFSET_LSB    16
++// Field       : RX_DELAY_OVERFLOW
++#define RX_DELAY_OVERFLOW_RESET       0x0
++#define RX_DELAY_OVERFLOW_BITS        0x00003000
++#define RX_DELAY_OVERFLOW_MSB         13
++#define RX_DELAY_OVERFLOW_LSB         12
++#define RX_DELAY_OVERFLOW_VALUE_ALLOW 0x0
++#define RX_DELAY_OVERFLOW_VALUE_CLAMP 0x1
++#define RX_DELAY_OVERFLOW_VALUE_FAIL  0x2
++// Field       : RX_DELAY_MAP
++#define RX_DELAY_MAP_RESET         0x0
++#define RX_DELAY_MAP_BITS          0x00000300
++#define RX_DELAY_MAP_MSB           9
++#define RX_DELAY_MAP_LSB           8
++#define RX_DELAY_MAP_VALUE_DIRECT  0x0
++#define RX_DELAY_MAP_VALUE         0x1
++#define RX_DELAY_MAP_VALUE_STRETCH 0x2
++// Field       : RX_DELAY_FIXED
++#define RX_DELAY_FIXED_RESET  0x00
++#define RX_DELAY_FIXED_BITS   0x0000001f
++#define RX_DELAY_FIXED_MSB    4
++#define RX_DELAY_FIXED_LSB    0
++// Register    : NDIV
++#define NDIV        0x00000018
++#define NDIV_BITS   0x1fff0000
++#define NDIV_RESET  0x00110000
++// Field       : NDIV_DIVB
++#define NDIV_DIVB_RESET  0x001
++#define NDIV_DIVB_BITS   0x1ff00000
++#define NDIV_DIVB_MSB    28
++#define NDIV_DIVB_LSB    20
++// Field       : NDIV_DIVA
++#define NDIV_DIVA_RESET  0x1
++#define NDIV_DIVA_BITS   0x000f0000
++#define NDIV_DIVA_MSB    19
++#define NDIV_DIVA_LSB    16
++// Register    : CS
++#define CS        0x0000001c
++#define CS_BITS   0x00111101
++#define CS_RESET  0x00000001
++// Field       : CS_RX_DEL_UPDATED
++#define CS_RX_DEL_UPDATED_RESET  0x0
++#define CS_RX_DEL_UPDATED_BITS   0x00100000
++#define CS_RX_DEL_UPDATED_MSB    20
++#define CS_RX_DEL_UPDATED_LSB    20
++// Field       : CS_RX_CLK_RUNNING
++#define CS_RX_CLK_RUNNING_RESET  0x0
++#define CS_RX_CLK_RUNNING_BITS   0x00010000
++#define CS_RX_CLK_RUNNING_MSB    16
++#define CS_RX_CLK_RUNNING_LSB    16
++// Field       : CS_SD_CLK_RUNNING
++#define CS_SD_CLK_RUNNING_RESET  0x0
++#define CS_SD_CLK_RUNNING_BITS   0x00001000
++#define CS_SD_CLK_RUNNING_MSB    12
++#define CS_SD_CLK_RUNNING_LSB    12
++// Field       : CS_TX_CLK_RUNNING
++#define CS_TX_CLK_RUNNING_RESET  0x0
++#define CS_TX_CLK_RUNNING_BITS   0x00000100
++#define CS_TX_CLK_RUNNING_MSB    8
++#define CS_TX_CLK_RUNNING_LSB    8
++// Field       : CS_RESET
++#define CS_RESET_RESET  0x1
++#define CS_RESET_BITS   0x00000001
++#define CS_RESET_MSB    0
++#define CS_RESET_LSB    0
++
++#define FPGA_SRC_RATE 400000000
++
++/* Base number of steps to delay in relation to tx clk.
++ * The relationship of the 3 clocks are as follows:
++ * tx_clk: This clock is provided to the controller. Data is sent out
++ * to the pads using this clock.
++ * sd_clk: This clock is sent out to the card.
++ * rx_clk: This clock is used to sample the data coming back from the card.
++ * This may need to be several steps ahead of the tx_clk. The default rx delay
++ * is used as a base delay, and can be further adjusted by the sd host
++ * controller during the tuning process if using a DDR50 or faster SD card
++ */
++/*
++ * PRJY-1813 - the default SD clock delay needs to be set to ~60% of the total
++ * number of steps to meet tISU (>6ns) and tIH (>2ns) in high-speed mode.
++ * On FPGA this means delay SDCLK by 5, and sample RX with a delay of 6.
++ */
++#define DEFAULT_RX_DELAY 6
++#define DEFAULT_SD_DELAY 5
++
++struct rp1_sdio_clkgen {
++	struct device *dev;
++
++	/* Source clock. Either PLL VCO or fixed freq on FPGA */
++	struct clk *src_clk;
++	/* Desired base frequency. Max freq card can go */
++	struct clk *base_clk;
++
++	struct clk_hw hw;
++	void __iomem *regs;
++
++	/* Starting value of local register before changing freq */
++	u32 local_base;
++};
++
++static inline void clkgen_write(struct rp1_sdio_clkgen *clkgen, u32 reg, u32 val)
++{
++	dev_dbg(clkgen->dev, "%s: write reg 0x%x: 0x%x\n", __func__, reg, val);
++	writel(val, clkgen->regs + reg);
++}
++
++static inline u32 clkgen_read(struct rp1_sdio_clkgen *clkgen, u32 reg)
++{
++	u32 val = readl(clkgen->regs + reg);
++
++	dev_dbg(clkgen->dev, "%s: read reg 0x%x: 0x%x\n", __func__, reg, val);
++	return val;
++}
++
++static int get_steps(unsigned int steps)
++{
++	int ret = -1;
++
++	if (steps == 4)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_4;
++	else if (steps == 5)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_5;
++	else if (steps == 6)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_6;
++	else if (steps == 8)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_8;
++	else if (steps == 10)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_10;
++	else if (steps == 12)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_12;
++	else if (steps == 16)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_16;
++	else if (steps == 20)
++		ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_20;
++	return ret;
++}
++
++static int rp1_sdio_clk_init(struct rp1_sdio_clkgen *clkgen)
++{
++	unsigned long src_rate = clk_get_rate(clkgen->src_clk);
++	unsigned long base_rate = clk_get_rate(clkgen->base_clk);
++	unsigned int steps = src_rate / base_rate;
++	u32 reg = 0;
++	int steps_value = 0;
++
++	dev_dbg(clkgen->dev, "init: src_rate %lu, base_rate %lu, steps %d\n",
++		src_rate, base_rate, steps);
++
++	/* Assert reset while we set up clkgen */
++	clkgen_write(clkgen, CS, CS_RESET_BITS);
++
++	/* Pick clock source */
++	if (src_rate == FPGA_SRC_RATE) {
++		/* Using ALT SRC */
++		reg |= MODE_SRC_SEL_VALUE_CLK_ALT_SRC << MODE_SRC_SEL_LSB;
++	} else {
++		/* Assume we are using PLL SYS VCO */
++		reg |= MODE_SRC_SEL_VALUE_PLL_SYS_VCO << MODE_SRC_SEL_LSB;
++	}
++
++	/* How many delay steps are available in one cycle for this source */
++	steps_value = get_steps(steps);
++	if (steps_value < 0) {
++		dev_err(clkgen->dev, "Invalid step value: %d\n", steps);
++		return -EINVAL;
++	}
++	reg |= steps_value << MODE_STEPS_PER_CYCLE_LSB;
++
++	/* Mode register is done now*/
++	clkgen_write(clkgen, MODE, reg);
++
++	/* Now set delay mode */
++	/* Clamp value if out of range rx delay is used */
++	reg = RX_DELAY_OVERFLOW_VALUE_CLAMP << RX_DELAY_OVERFLOW_LSB;
++	/* SD tuning bus goes from 0x0 to 0xf but we don't necessarily have that
++	 * many steps available depending on the source so map 0x0 -> 0xf to one
++	 * cycle of rx delay
++	 */
++	reg |= RX_DELAY_MAP_VALUE_STRETCH << RX_DELAY_MAP_LSB;
++
++	/* Default RX delay */
++	dev_dbg(clkgen->dev, "default rx delay %d\n", DEFAULT_RX_DELAY);
++	reg |= (DEFAULT_RX_DELAY & RX_DELAY_FIXED_BITS) << RX_DELAY_FIXED_LSB;
++	clkgen_write(clkgen, RX_DELAY, reg);
++
++	/* Default SD delay */
++	dev_dbg(clkgen->dev, "default sd delay %d\n", DEFAULT_SD_DELAY);
++	reg = (DEFAULT_SD_DELAY & SD_DELAY_STEPS_BITS) << SD_DELAY_STEPS_LSB;
++	clkgen_write(clkgen, SD_DELAY, reg);
++
++	/* We select freq, we turn on tx clock, we turn on sd clk,
++	 * we pick clock generator mode
++	 */
++	reg = USE_LOCAL_FREQ_SEL_BITS | USE_LOCAL_CARD_CLK_EN_BITS |
++	      USE_LOCAL_CLK2CARD_ON_BITS | USE_LOCAL_CLK_GEN_SEL_BITS;
++	clkgen_write(clkgen, USE_LOCAL, reg);
++
++	/* Deassert reset. Reset bit is only writable bit of CS
++	 * reg so fine to write a 0.
++	 */
++	clkgen_write(clkgen, CS, 0);
++
++	return 0;
++}
++
++#define RUNNING	\
++	(CS_TX_CLK_RUNNING_BITS | CS_RX_CLK_RUNNING_BITS | \
++	 CS_SD_CLK_RUNNING_BITS)
++static int rp1_sdio_clk_is_prepared(struct clk_hw *hw)
++{
++	struct rp1_sdio_clkgen *clkgen =
++		container_of(hw, struct rp1_sdio_clkgen, hw);
++	u32 status;
++
++	dev_dbg(clkgen->dev, "is_prepared\n");
++	status = clkgen_read(clkgen, CS);
++	return ((status & RUNNING) == RUNNING);
++}
++
++/* Can define an additional divider if an sd card isn't working at full speed */
++/* #define SLOWDOWN 3 */
++
++static unsigned long rp1_sdio_clk_get_rate(struct clk_hw *hw,
++					   unsigned long parent_rate)
++{
++	/* Get the current rate */
++	struct rp1_sdio_clkgen *clkgen =
++		container_of(hw, struct rp1_sdio_clkgen, hw);
++	unsigned long actual_rate = 0;
++	u32 ndiv_diva;
++	u32 ndiv_divb;
++	u32 tmp;
++	u32 div;
++
++	tmp = clkgen_read(clkgen, LOCAL);
++	if ((tmp & LOCAL_CLK2CARD_ON_BITS) == 0) {
++		dev_dbg(clkgen->dev, "get_rate 0\n");
++		return 0;
++	}
++
++	tmp = clkgen_read(clkgen, NDIV);
++	ndiv_diva = (tmp & NDIV_DIVA_BITS) >> NDIV_DIVA_LSB;
++	ndiv_divb = (tmp & NDIV_DIVB_BITS) >> NDIV_DIVB_LSB;
++	div = ndiv_diva * ndiv_divb;
++	actual_rate = (clk_get_rate(clkgen->base_clk) / div);
++
++#ifdef SLOWDOWN
++	actual_rate *= SLOWDOWN;
++#endif
++
++	dev_dbg(clkgen->dev, "get_rate. ndiv_diva %d, ndiv_divb %d = %lu\n",
++		ndiv_diva, ndiv_divb, actual_rate);
++
++	return actual_rate;
++}
++
++static int rp1_sdio_clk_set_rate(struct clk_hw *hw, unsigned long rate,
++				 unsigned long parent_rate)
++{
++	struct rp1_sdio_clkgen *clkgen =
++		container_of(hw, struct rp1_sdio_clkgen, hw);
++	u32 div;
++	u32 reg;
++
++	dev_dbg(clkgen->dev, "set_rate %lu\n", rate);
++
++	if (rate == 0) {
++		/* Keep tx clock running */
++		clkgen_write(clkgen, LOCAL, LOCAL_CARD_CLK_EN_BITS);
++		return 0;
++	}
++
++#ifdef SLOWDOWN
++	rate /= SLOWDOWN;
++#endif
++
++	div = (clk_get_rate(clkgen->base_clk) / rate) - 1;
++	reg = LOCAL_CLK_GEN_SEL_BITS | LOCAL_CARD_CLK_EN_BITS |
++	      LOCAL_CLK2CARD_ON_BITS | (div << LOCAL_FREQ_SEL_LSB);
++	clkgen_write(clkgen, LOCAL, reg);
++
++	return 0;
++}
++
++#define MAX_NDIV (256 * 8)
++static int rp1_sdio_clk_determine_rate(struct clk_hw *hw,
++				       struct clk_rate_request *req)
++{
++	unsigned long rate;
++	struct rp1_sdio_clkgen *clkgen =
++		container_of(hw, struct rp1_sdio_clkgen, hw);
++	unsigned long base_rate = clk_get_rate(clkgen->base_clk);
++	u32 div;
++
++	/* What is the actual rate I can get if I request xyz */
++	if (req->rate) {
++		div = min((u32)(base_rate / req->rate), (u32)MAX_NDIV);
++		rate = base_rate / div;
++		req->rate = rate;
++		dev_dbg(clkgen->dev, "determine_rate %lu: %lu / %d = %lu\n",
++			req->rate, base_rate, div, rate);
++	} else {
++		rate = 0;
++		dev_dbg(clkgen->dev, "determine_rate %lu: %lu\n", req->rate,
++			rate);
++	}
++
++	return 0;
++}
++
++static const struct clk_ops rp1_sdio_clk_ops = {
++	.is_prepared    = rp1_sdio_clk_is_prepared,
++	.recalc_rate    = rp1_sdio_clk_get_rate,
++	.set_rate       = rp1_sdio_clk_set_rate,
++	.determine_rate = rp1_sdio_clk_determine_rate,
++};
++
++static int rp1_sdio_clk_probe(struct platform_device *pdev)
++{
++	struct device_node *node = pdev->dev.of_node;
++	struct rp1_sdio_clkgen *clkgen;
++	void __iomem *regs;
++	struct clk_init_data init = {};
++	int ret;
++
++	clkgen = devm_kzalloc(&pdev->dev, sizeof(*clkgen), GFP_KERNEL);
++	if (!clkgen)
++		return -ENOMEM;
++	platform_set_drvdata(pdev, clkgen);
++
++	clkgen->dev = &pdev->dev;
++
++	/* Source freq */
++	clkgen->src_clk = devm_clk_get(&pdev->dev, "src");
++	if (IS_ERR(clkgen->src_clk)) {
++		int err = PTR_ERR(clkgen->src_clk);
++
++		dev_err(&pdev->dev, "failed to get src clk: %d\n", err);
++		return err;
++	}
++
++	/* Desired maximum output freq (i.e. base freq) */
++	clkgen->base_clk = devm_clk_get(&pdev->dev, "base");
++	if (IS_ERR(clkgen->base_clk)) {
++		int err = PTR_ERR(clkgen->base_clk);
++
++		dev_err(&pdev->dev, "failed to get base clk: %d\n", err);
++		return err;
++	}
++
++	regs = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(regs))
++		return PTR_ERR(regs);
++
++	init.name = node->name;
++	init.ops = &rp1_sdio_clk_ops;
++	init.flags = CLK_GET_RATE_NOCACHE;
++
++	clkgen->hw.init = &init;
++	clkgen->regs = regs;
++
++	dev_info(&pdev->dev, "loaded %s\n", init.name);
++
++	ret = devm_clk_hw_register(&pdev->dev, &clkgen->hw);
++	if (ret)
++		return ret;
++
++	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clkgen->hw);
++	if (ret)
++		return ret;
++
++	ret = rp1_sdio_clk_init(clkgen);
++	return ret;
++}
++
++static int rp1_sdio_clk_remove(struct platform_device *pdev)
++{
++	return 0;
++}
++
++static const struct of_device_id rp1_sdio_clk_dt_ids[] = {
++	{ .compatible = "raspberrypi,rp1-sdio-clk", },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rp1_sdio_clk_dt_ids);
++
++static struct platform_driver rp1_sdio_clk_driver = {
++	.probe	= rp1_sdio_clk_probe,
++	.remove	= rp1_sdio_clk_remove,
++	.driver	= {
++		.name		= "rp1-sdio-clk",
++		.of_match_table	= rp1_sdio_clk_dt_ids,
++	},
++};
++module_platform_driver(rp1_sdio_clk_driver);
++
++MODULE_AUTHOR("Liam Fraser <[email protected]>");
++MODULE_DESCRIPTION("RP1 SDIO clock driver");
++MODULE_LICENSE("GPL");

+ 76 - 0
target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch

@@ -0,0 +1,76 @@
+From 50adadfaf324ed5cbb59ce2b85eda59de4e3801a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 4 Dec 2020 15:20:36 +0000
+Subject: [PATCH] i2c: designware: Add SMBUS quick command support
+
+The SMBUS emulation code turns an SMBUS quick command into a zero-
+length read. This controller can't do zero length accesses, but it
+can do quick commands, so reverse the emulation. The alternative
+would be to properly implement the SMBUS support but that is a lot
+more work, and unnecessary just to get i2cdetect working.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/i2c/busses/i2c-designware-core.h   |  2 ++
+ drivers/i2c/busses/i2c-designware-master.c | 17 +++++++++++++++--
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-designware-core.h
++++ b/drivers/i2c/busses/i2c-designware-core.h
+@@ -117,7 +117,9 @@
+ 
+ #define DW_IC_ERR_TX_ABRT	0x1
+ 
++#define DW_IC_TAR_SPECIAL		BIT(11)
+ #define DW_IC_TAR_10BITADDR_MASTER	BIT(12)
++#define DW_IC_TAR_SMBUS_QUICK_CMD	BIT(16)
+ 
+ #define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH	(BIT(2) | BIT(3))
+ #define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK	GENMASK(3, 2)
+--- a/drivers/i2c/busses/i2c-designware-master.c
++++ b/drivers/i2c/busses/i2c-designware-master.c
+@@ -228,6 +228,10 @@ static void i2c_dw_xfer_init(struct dw_i
+ 		ic_tar = DW_IC_TAR_10BITADDR_MASTER;
+ 	}
+ 
++	/* Convert a zero-length read into an SMBUS quick command */
++	if (!msgs[dev->msg_write_idx].len)
++		ic_tar = DW_IC_TAR_SPECIAL | DW_IC_TAR_SMBUS_QUICK_CMD;
++
+ 	regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
+ 			   ic_con);
+ 
+@@ -409,6 +413,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
+ 		regmap_read(dev->map, DW_IC_RXFLR, &flr);
+ 		rx_limit = dev->rx_fifo_depth - flr;
+ 
++		/* Handle SMBUS quick commands */
++		if (!buf_len) {
++			if (msgs[dev->msg_write_idx].flags & I2C_M_RD)
++				regmap_write(dev->map, DW_IC_DATA_CMD, 0x300);
++			else
++				regmap_write(dev->map, DW_IC_DATA_CMD, 0x200);
++		}
++
+ 		while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
+ 			u32 cmd = 0;
+ 
+@@ -673,7 +685,7 @@ static const struct i2c_algorithm i2c_dw
+ };
+ 
+ static const struct i2c_adapter_quirks i2c_dw_quirks = {
+-	.flags = I2C_AQ_NO_ZERO_LEN,
++	.flags = 0,
+ };
+ 
+ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+@@ -813,7 +825,8 @@ void i2c_dw_configure_master(struct dw_i
+ {
+ 	struct i2c_timings *t = &dev->timings;
+ 
+-	dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
++	dev->functionality = I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_QUICK |
++			     DW_IC_DEFAULT_FUNCTIONALITY;
+ 
+ 	dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ 			  DW_IC_CON_RESTART_EN;

+ 355 - 0
target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch

@@ -0,0 +1,355 @@
+From 0a1cd70189daec3baf4b4a233dd8e25ffbb9d512 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 28 Apr 2021 17:46:01 +0100
+Subject: [PATCH] dmaengine: dw-axi-dmac: Fixes for RP1
+
+Don't assume that DMA addresses of devices are the same as their
+physical addresses - convert correctly.
+
+The CFG2 register layout is used when there are more than 8 channels,
+but also when configured for more than 16 target peripheral devices
+because the index of the handshake signal has to be made wider.
+
+Reset the DMAC on probe
+
+The driver goes to the trouble of tracking when transfers have been
+paused, but then doesn't report that state when queried.
+
+Not having APB registers is not an error - for most use cases it's
+not even of interest, it's expected. Demote the message to debug level,
+which is disabled by default.
+
+Each channel has a descriptor pool, which is shared between transfers.
+It is unsafe to treat the total number of descriptors allocated from a
+pool as the number allocated to a specific transfer; doing so leads
+to releasing buffers that shouldn't be released and walking off the
+ends of descriptor lists. Instead, give each transfer descriptor its
+own count.
+
+Support partial transfers:
+Some use cases involve streaming from a device where the transfer only
+proceeds when the device's FIFO occupancy exceeds a certain threshold.
+In such cases (e.g. when pulling data from a UART) it is important to
+know how much data has been transferred so far, in order that remaining
+bytes can be read from the FIFO directly by software.
+
+Add the necessary code to provide this "residue" value with a finer,
+sub-transfer granularity.
+
+In order to prevent the occasional byte getting stuck in the DMA
+controller's internal buffers, restrict the destination memory width
+to the source register width.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ .../dma/dw-axi-dmac/dw-axi-dmac-platform.c    | 136 +++++++++++++++---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h         |   3 +
+ 2 files changed, 118 insertions(+), 21 deletions(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -12,6 +12,7 @@
+ #include <linux/device.h>
+ #include <linux/dmaengine.h>
+ #include <linux/dmapool.h>
++#include <linux/dma-direct.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
+@@ -79,6 +80,17 @@ axi_chan_iowrite64(struct axi_dma_chan *
+ 	iowrite32(upper_32_bits(val), chan->chan_regs + reg + 4);
+ }
+ 
++static inline u64
++axi_chan_ioread64(struct axi_dma_chan *chan, u32 reg)
++{
++	/*
++	 * We split one 64 bit read into two 32 bit reads as some HW doesn't
++	 * support 64 bit access.
++	 */
++	return ((u64)ioread32(chan->chan_regs + reg + 4) << 32) +
++		ioread32(chan->chan_regs + reg);
++}
++
+ static inline void axi_chan_config_write(struct axi_dma_chan *chan,
+ 					 struct axi_dma_chan_config *config)
+ {
+@@ -86,7 +98,7 @@ static inline void axi_chan_config_write
+ 
+ 	cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS |
+ 		  config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
+-	if (chan->chip->dw->hdata->reg_map_8_channels) {
++	if (!chan->chip->dw->hdata->reg_map_cfg2) {
+ 		cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS |
+ 			 config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS |
+ 			 config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS |
+@@ -214,7 +226,18 @@ static void axi_dma_hw_init(struct axi_d
+ {
+ 	int ret;
+ 	u32 i;
++	int retries = 1000;
+ 
++	axi_dma_iowrite32(chip, DMAC_RESET, 1);
++	while (axi_dma_ioread32(chip, DMAC_RESET)) {
++		retries--;
++		if (!retries) {
++			dev_err(chip->dev, "%s: DMAC failed to reset\n",
++				__func__);
++			return;
++		}
++		cpu_relax();
++	}
+ 	for (i = 0; i < chip->dw->hdata->nr_channels; i++) {
+ 		axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ 		axi_chan_disable(&chip->dw->chan[i]);
+@@ -276,7 +299,7 @@ static struct axi_dma_lli *axi_desc_get(
+ static void axi_desc_put(struct axi_dma_desc *desc)
+ {
+ 	struct axi_dma_chan *chan = desc->chan;
+-	int count = atomic_read(&chan->descs_allocated);
++	u32 count = desc->hw_desc_count;
+ 	struct axi_dma_hw_desc *hw_desc;
+ 	int descs_put;
+ 
+@@ -298,6 +321,48 @@ static void vchan_desc_put(struct virt_d
+ 	axi_desc_put(vd_to_axi_desc(vdesc));
+ }
+ 
++static u32 axi_dma_desc_src_pos(struct axi_dma_desc *desc, dma_addr_t addr)
++{
++	unsigned int idx = 0;
++	u32 pos = 0;
++
++	while (pos < desc->length) {
++		struct axi_dma_hw_desc *hw_desc = &desc->hw_desc[idx++];
++		u32 len = hw_desc->len;
++		dma_addr_t start = le64_to_cpu(hw_desc->lli->sar);
++
++		if (addr >= start && addr <= (start + len)) {
++			pos += addr - start;
++			break;
++		}
++
++		pos += len;
++	}
++
++	return pos;
++}
++
++static u32 axi_dma_desc_dst_pos(struct axi_dma_desc *desc, dma_addr_t addr)
++{
++	unsigned int idx = 0;
++	u32 pos = 0;
++
++	while (pos < desc->length) {
++		struct axi_dma_hw_desc *hw_desc = &desc->hw_desc[idx++];
++		u32 len = hw_desc->len;
++		dma_addr_t start = le64_to_cpu(hw_desc->lli->dar);
++
++		if (addr >= start && addr <= (start + len)) {
++			pos += addr - start;
++			break;
++		}
++
++		pos += len;
++	}
++
++	return pos;
++}
++
+ static enum dma_status
+ dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+ 		  struct dma_tx_state *txstate)
+@@ -307,10 +372,7 @@ dma_chan_tx_status(struct dma_chan *dcha
+ 	enum dma_status status;
+ 	u32 completed_length;
+ 	unsigned long flags;
+-	u32 completed_blocks;
+ 	size_t bytes = 0;
+-	u32 length;
+-	u32 len;
+ 
+ 	status = dma_cookie_status(dchan, cookie, txstate);
+ 	if (status == DMA_COMPLETE || !txstate)
+@@ -319,16 +381,31 @@ dma_chan_tx_status(struct dma_chan *dcha
+ 	spin_lock_irqsave(&chan->vc.lock, flags);
+ 
+ 	vdesc = vchan_find_desc(&chan->vc, cookie);
+-	if (vdesc) {
+-		length = vd_to_axi_desc(vdesc)->length;
+-		completed_blocks = vd_to_axi_desc(vdesc)->completed_blocks;
+-		len = vd_to_axi_desc(vdesc)->hw_desc[0].len;
+-		completed_length = completed_blocks * len;
+-		bytes = length - completed_length;
++	if (vdesc && vdesc == vchan_next_desc(&chan->vc)) {
++		/* This descriptor is in-progress */
++		struct axi_dma_desc *desc = vd_to_axi_desc(vdesc);
++		dma_addr_t addr;
++
++		if (chan->direction == DMA_MEM_TO_DEV) {
++			addr = axi_chan_ioread64(chan, CH_SAR);
++			completed_length = axi_dma_desc_src_pos(desc, addr);
++		} else if (chan->direction == DMA_DEV_TO_MEM) {
++			addr = axi_chan_ioread64(chan, CH_DAR);
++			completed_length = axi_dma_desc_dst_pos(desc, addr);
++		} else {
++			completed_length = 0;
++		}
++		bytes = desc->length - completed_length;
++	} else if (vdesc) {
++		/* Still in the queue so not started */
++		bytes = vd_to_axi_desc(vdesc)->length;
+ 	}
+ 
+-	spin_unlock_irqrestore(&chan->vc.lock, flags);
++	if (chan->is_paused && status == DMA_IN_PROGRESS)
++		status = DMA_PAUSED;
++
+ 	dma_set_residue(txstate, bytes);
++	spin_unlock_irqrestore(&chan->vc.lock, flags);
+ 
+ 	return status;
+ }
+@@ -516,7 +593,7 @@ static void dw_axi_dma_set_hw_channel(st
+ 	unsigned long reg_value, val;
+ 
+ 	if (!chip->apb_regs) {
+-		dev_err(chip->dev, "apb_regs not initialized\n");
++		dev_dbg(chip->dev, "apb_regs not initialized\n");
+ 		return;
+ 	}
+ 
+@@ -620,18 +697,25 @@ static int dw_axi_dma_set_hw_desc(struct
+ 	switch (chan->direction) {
+ 	case DMA_MEM_TO_DEV:
+ 		reg_width = __ffs(chan->config.dst_addr_width);
+-		device_addr = chan->config.dst_addr;
++		device_addr = phys_to_dma(chan->chip->dev, chan->config.dst_addr);
+ 		ctllo = reg_width << CH_CTL_L_DST_WIDTH_POS |
+ 			mem_width << CH_CTL_L_SRC_WIDTH_POS |
++			DWAXIDMAC_BURST_TRANS_LEN_1 << CH_CTL_L_DST_MSIZE_POS |
++			DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS |
+ 			DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_DST_INC_POS |
+ 			DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS;
+ 		block_ts = len >> mem_width;
+ 		break;
+ 	case DMA_DEV_TO_MEM:
+ 		reg_width = __ffs(chan->config.src_addr_width);
+-		device_addr = chan->config.src_addr;
++		/* Prevent partial access units getting lost */
++		if (mem_width > reg_width)
++			mem_width = reg_width;
++		device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr);
+ 		ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS |
+ 			mem_width << CH_CTL_L_DST_WIDTH_POS |
++			DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
++			DWAXIDMAC_BURST_TRANS_LEN_1 << CH_CTL_L_SRC_MSIZE_POS |
+ 			DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS |
+ 			DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_SRC_INC_POS;
+ 		block_ts = len >> reg_width;
+@@ -667,9 +751,6 @@ static int dw_axi_dma_set_hw_desc(struct
+ 	}
+ 
+ 	hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1);
+-
+-	ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+-		 DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS;
+ 	hw_desc->lli->ctl_lo = cpu_to_le32(ctllo);
+ 
+ 	set_desc_src_master(hw_desc);
+@@ -764,6 +845,8 @@ dw_axi_dma_chan_prep_cyclic(struct dma_c
+ 		src_addr += segment_len;
+ 	}
+ 
++	desc->hw_desc_count = total_segments;
++
+ 	llp = desc->hw_desc[0].llp;
+ 
+ 	/* Managed transfer list */
+@@ -843,6 +926,8 @@ dw_axi_dma_chan_prep_slave_sg(struct dma
+ 		} while (len >= segment_len);
+ 	}
+ 
++	desc->hw_desc_count = loop;
++
+ 	/* Set end-of-link to the last link descriptor of list */
+ 	set_desc_last(&desc->hw_desc[num_sgs - 1]);
+ 
+@@ -950,6 +1035,8 @@ dma_chan_prep_dma_memcpy(struct dma_chan
+ 		num++;
+ 	}
+ 
++	desc->hw_desc_count = num;
++
+ 	/* Set end-of-link to the last link descriptor of list */
+ 	set_desc_last(&desc->hw_desc[num - 1]);
+ 	/* Managed transfer list */
+@@ -998,7 +1085,7 @@ static void axi_chan_dump_lli(struct axi
+ static void axi_chan_list_dump_lli(struct axi_dma_chan *chan,
+ 				   struct axi_dma_desc *desc_head)
+ {
+-	int count = atomic_read(&chan->descs_allocated);
++	u32 count = desc_head->hw_desc_count;
+ 	int i;
+ 
+ 	for (i = 0; i < count; i++)
+@@ -1041,11 +1128,11 @@ out:
+ 
+ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
+ {
+-	int count = atomic_read(&chan->descs_allocated);
+ 	struct axi_dma_hw_desc *hw_desc;
+ 	struct axi_dma_desc *desc;
+ 	struct virt_dma_desc *vd;
+ 	unsigned long flags;
++	u32 count;
+ 	u64 llp;
+ 	int i;
+ 
+@@ -1067,6 +1154,7 @@ static void axi_chan_block_xfer_complete
+ 	if (chan->cyclic) {
+ 		desc = vd_to_axi_desc(vd);
+ 		if (desc) {
++			count = desc->hw_desc_count;
+ 			llp = lo_hi_readq(chan->chan_regs + CH_LLP);
+ 			for (i = 0; i < count; i++) {
+ 				hw_desc = &desc->hw_desc[i];
+@@ -1310,6 +1398,8 @@ static int parse_device_properties(struc
+ 	chip->dw->hdata->nr_channels = tmp;
+ 	if (tmp <= DMA_REG_MAP_CH_REF)
+ 		chip->dw->hdata->reg_map_8_channels = true;
++	else
++		chip->dw->hdata->reg_map_cfg2 = true;
+ 
+ 	ret = device_property_read_u32(dev, "snps,dma-masters", &tmp);
+ 	if (ret)
+@@ -1319,6 +1409,10 @@ static int parse_device_properties(struc
+ 
+ 	chip->dw->hdata->nr_masters = tmp;
+ 
++	ret = device_property_read_u32(dev, "snps,dma-targets", &tmp);
++	if (!ret && tmp > 16)
++		chip->dw->hdata->reg_map_cfg2 = true;
++
+ 	ret = device_property_read_u32(dev, "snps,data-width", &tmp);
+ 	if (ret)
+ 		return ret;
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -32,6 +32,8 @@ struct dw_axi_dma_hcfg {
+ 	u32	axi_rw_burst_len;
+ 	/* Register map for DMAX_NUM_CHANNELS <= 8 */
+ 	bool	reg_map_8_channels;
++	/* Register map for DMAX_NUM_CHANNELS > 8 || DMAX_NUM_HS_IF > 16*/
++	bool	reg_map_cfg2;
+ 	bool	restrict_axi_burst_len;
+ };
+ 
+@@ -100,6 +102,7 @@ struct axi_dma_desc {
+ 
+ 	struct virt_dma_desc		vd;
+ 	struct axi_dma_chan		*chan;
++	u32				hw_desc_count;
+ 	u32				completed_blocks;
+ 	u32				length;
+ 	u32				period_len;

+ 64 - 0
target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch

@@ -0,0 +1,64 @@
+From 8a9c0607ce0daa91c48faefd70ea73bda54ed0ae Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 29 Nov 2022 10:09:54 +0000
+Subject: [PATCH] spi: dw: Handle combined tx and rx messages
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/spi/spi-dw-core.c | 12 +++++++++---
+ drivers/spi/spi-dw-mmio.c |  8 ++++++--
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-dw-core.c
++++ b/drivers/spi/spi-dw-core.c
+@@ -244,8 +244,11 @@ static irqreturn_t dw_spi_transfer_handl
+ 	 */
+ 	if (irq_status & DW_SPI_INT_TXEI) {
+ 		dw_writer(dws);
+-		if (!dws->tx_len)
++		if (!dws->tx_len) {
+ 			dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
++			if (!dws->rx_len)
++				spi_finalize_current_transfer(dws->master);
++		}
+ 	}
+ 
+ 	return IRQ_HANDLED;
+@@ -372,8 +375,11 @@ static void dw_spi_irq_setup(struct dw_s
+ 
+ 	dws->transfer_handler = dw_spi_transfer_handler;
+ 
+-	imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI |
+-		DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
++	imask = 0;
++	if (dws->tx_len)
++		imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI;
++	if (dws->rx_len)
++		imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
+ 	dw_spi_umask_intr(dws, imask);
+ }
+ 
+--- a/drivers/spi/spi-dw-mmio.c
++++ b/drivers/spi/spi-dw-mmio.c
+@@ -20,6 +20,7 @@
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+ #include <linux/reset.h>
++#include <linux/interrupt.h>
+ 
+ #include "spi-dw.h"
+ 
+@@ -280,8 +281,11 @@ static int dw_spi_mmio_probe(struct plat
+ 	dws->paddr = mem->start;
+ 
+ 	dws->irq = platform_get_irq(pdev, 0);
+-	if (dws->irq < 0)
+-		return dws->irq; /* -ENXIO */
++	if (dws->irq < 0) {
++		if (dws->irq != -ENXIO)
++			return dws->irq; /* -ENXIO */
++		dws->irq = IRQ_NOTCONNECTED;
++	}
+ 
+ 	dwsmmio->clk = devm_clk_get(&pdev->dev, NULL);
+ 	if (IS_ERR(dwsmmio->clk))

+ 292 - 0
target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch

@@ -0,0 +1,292 @@
+From 824f18efc8ad59e2783570ae2df83e2cd16b9f04 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 14 Feb 2023 14:03:54 +0000
+Subject: [PATCH] pwm: Add support for RP1 PWM
+
+Add a driver for the RP1 PWM block.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ .../devicetree/bindings/pwm/pwm-rp1.yaml      |  38 ++++
+ drivers/pwm/Kconfig                           |   9 +
+ drivers/pwm/Makefile                          |   1 +
+ drivers/pwm/pwm-rp1.c                         | 203 ++++++++++++++++++
+ 4 files changed, 251 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/pwm-rp1.yaml
+ create mode 100644 drivers/pwm/pwm-rp1.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/pwm-rp1.yaml
+@@ -0,0 +1,38 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pwm/pwm-rp1.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi RP1 PWM controller
++
++maintainers:
++  - Naushir Patuck <[email protected]>
++
++properties:
++  compatible:
++    enum:
++      - raspberrypi,rp1-pwm
++
++  reg:
++    maxItems: 1
++
++  "#pwm-cells":
++    const: 3
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - "#pwm-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    pwm0: pwm@98000 {
++      compatible = "raspberrypi,rp1-pwm";
++      reg = <0x0 0x98000  0x0 0x100>;
++      clocks = <&rp1_sys>;
++      #pwm-cells = <3>;
++    };
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -451,6 +451,15 @@ config PWM_RASPBERRYPI_POE
+ 	  Enable Raspberry Pi firmware controller PWM bus used to control the
+ 	  official RPI PoE hat
+ 
++config PWM_RP1
++	tristate "RP1 PWM support"
++	depends on ARCH_BCM2835 || COMPILE_TEST
++	help
++	  PWM framework driver for Raspberry Pi RP1 controller
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called pwm-rp1.
++
+ config PWM_RCAR
+ 	tristate "Renesas R-Car PWM support"
+ 	depends on ARCH_RENESAS || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER)	+= pwm-om
+ obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o
+ obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
+ obj-$(CONFIG_PWM_RASPBERRYPI_POE)	+= pwm-raspberrypi-poe.o
++obj-$(CONFIG_PWM_RP1)		+= pwm-rp1.o
+ obj-$(CONFIG_PWM_RCAR)		+= pwm-rcar.o
+ obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
+ obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
+--- /dev/null
++++ b/drivers/pwm/pwm-rp1.c
+@@ -0,0 +1,203 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * pwm-rp1.c
++ *
++ * Raspberry Pi RP1 PWM.
++ *
++ * Copyright © 2023 Raspberry Pi Ltd.
++ *
++ * Author: Naushir Patuck ([email protected])
++ *
++ * Based on the pwm-bcm2835 driver by:
++ * Bart Tanghe <[email protected]>
++ */
++
++#include <linux/bitops.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++
++#define PWM_GLOBAL_CTRL		0x000
++#define PWM_CHANNEL_CTRL(x)	(0x014 + ((x) * 16))
++#define PWM_RANGE(x)		(0x018 + ((x) * 16))
++#define PWM_DUTY(x)		(0x020 + ((x) * 16))
++
++/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
++#define PWM_CHANNEL_DEFAULT	(BIT(8) + BIT(0))
++#define PWM_CHANNEL_ENABLE(x)	BIT(x)
++#define PWM_POLARITY		BIT(3)
++#define SET_UPDATE		BIT(31)
++#define PWM_MODE_MASK		GENMASK(1, 0)
++
++struct rp1_pwm {
++	struct pwm_chip chip;
++	struct device *dev;
++	void __iomem *base;
++	struct clk *clk;
++};
++
++static inline struct rp1_pwm *to_rp1_pwm(struct pwm_chip *chip)
++{
++	return container_of(chip, struct rp1_pwm, chip);
++}
++
++static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++	struct rp1_pwm *pc = to_rp1_pwm(chip);
++	u32 value;
++
++	value = readl(pc->base + PWM_GLOBAL_CTRL);
++	value |= SET_UPDATE;
++	writel(value, pc->base + PWM_GLOBAL_CTRL);
++}
++
++static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++	struct rp1_pwm *pc = to_rp1_pwm(chip);
++
++	writel(PWM_CHANNEL_DEFAULT, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++	return 0;
++}
++
++static void rp1_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++	struct rp1_pwm *pc = to_rp1_pwm(chip);
++	u32 value;
++
++	value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++	value &= ~PWM_MODE_MASK;
++	writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++	rp1_pwm_apply_config(chip, pwm);
++}
++
++static int rp1_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++			 const struct pwm_state *state)
++{
++	struct rp1_pwm *pc = to_rp1_pwm(chip);
++	unsigned long clk_rate = clk_get_rate(pc->clk);
++	unsigned long clk_period;
++	u32 value;
++
++	if (!clk_rate) {
++		dev_err(pc->dev, "failed to get clock rate\n");
++		return -EINVAL;
++	}
++
++	/* set period */
++	clk_period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate);
++
++	writel(DIV_ROUND_CLOSEST(state->duty_cycle, clk_period),
++	       pc->base + PWM_DUTY(pwm->hwpwm));
++
++	/* set duty cycle */
++	writel(DIV_ROUND_CLOSEST(state->period, clk_period),
++	       pc->base + PWM_RANGE(pwm->hwpwm));
++
++	/* set polarity */
++	value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++	if (state->polarity == PWM_POLARITY_NORMAL)
++		value &= ~PWM_POLARITY;
++	else
++		value |= PWM_POLARITY;
++	writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++
++	/* enable/disable */
++	value = readl(pc->base + PWM_GLOBAL_CTRL);
++	if (state->enabled)
++		value |= PWM_CHANNEL_ENABLE(pwm->hwpwm);
++	else
++		value &= ~PWM_CHANNEL_ENABLE(pwm->hwpwm);
++	writel(value, pc->base + PWM_GLOBAL_CTRL);
++
++	rp1_pwm_apply_config(chip, pwm);
++
++	return 0;
++}
++
++static const struct pwm_ops rp1_pwm_ops = {
++	.request = rp1_pwm_request,
++	.free = rp1_pwm_free,
++	.apply = rp1_pwm_apply,
++	.owner = THIS_MODULE,
++};
++
++static int rp1_pwm_probe(struct platform_device *pdev)
++{
++	struct rp1_pwm *pc;
++	struct resource *res;
++	int ret;
++
++	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
++	if (!pc)
++		return -ENOMEM;
++
++	pc->dev = &pdev->dev;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	pc->base = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(pc->base))
++		return PTR_ERR(pc->base);
++
++	pc->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(pc->clk))
++		return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
++				     "clock not found\n");
++
++	ret = clk_prepare_enable(pc->clk);
++	if (ret)
++		return ret;
++
++	pc->chip.dev = &pdev->dev;
++	pc->chip.ops = &rp1_pwm_ops;
++	pc->chip.base = -1;
++	pc->chip.npwm = 4;
++	pc->chip.of_xlate = of_pwm_xlate_with_flags;
++	pc->chip.of_pwm_n_cells = 3;
++
++	platform_set_drvdata(pdev, pc);
++
++	ret = pwmchip_add(&pc->chip);
++	if (ret < 0)
++		goto add_fail;
++
++	return 0;
++
++add_fail:
++	clk_disable_unprepare(pc->clk);
++	return ret;
++}
++
++static int rp1_pwm_remove(struct platform_device *pdev)
++{
++	struct rp1_pwm *pc = platform_get_drvdata(pdev);
++
++	clk_disable_unprepare(pc->clk);
++
++	pwmchip_remove(&pc->chip);
++
++	return 0;
++}
++
++static const struct of_device_id rp1_pwm_of_match[] = {
++	{ .compatible = "raspberrypi,rp1-pwm" },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
++
++static struct platform_driver rp1_pwm_driver = {
++	.driver = {
++		.name = "rpi-pwm",
++		.of_match_table = rp1_pwm_of_match,
++	},
++	.probe = rp1_pwm_probe,
++	.remove = rp1_pwm_remove,
++};
++module_platform_driver(rp1_pwm_driver);
++
++MODULE_AUTHOR("Naushir Patuck <[email protected]");
++MODULE_DESCRIPTION("RP1 PWM driver");
++MODULE_LICENSE("GPL");

+ 2678 - 0
target/linux/bcm27xx/patches-6.1/950-0885-drm-Add-RP1-DSI-driver.patch

@@ -0,0 +1,2678 @@
+From f93caa69a9af6476cd4d93944a83acd227e68fd4 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Tue, 14 Feb 2023 14:58:33 +0000
+Subject: [PATCH] drm: Add RP1 DSI driver
+
+Add support for the RP1 DSI hardware.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/gpu/drm/Kconfig                   |    2 +
+ drivers/gpu/drm/Makefile                  |    1 +
+ drivers/gpu/drm/rp1/Kconfig               |    5 +
+ drivers/gpu/drm/rp1/Makefile              |    4 +
+ drivers/gpu/drm/rp1/rp1-dsi/Kconfig       |   15 +
+ drivers/gpu/drm/rp1/rp1-dsi/Makefile      |    5 +
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c     |  537 ++++++++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h     |   94 ++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c |  443 ++++++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c | 1504 +++++++++++++++++++++
+ 10 files changed, 2610 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
+
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -384,6 +384,8 @@ source "drivers/gpu/drm/v3d/Kconfig"
+ 
+ source "drivers/gpu/drm/vc4/Kconfig"
+ 
++source "drivers/gpu/drm/rp1/Kconfig"
++
+ source "drivers/gpu/drm/etnaviv/Kconfig"
+ 
+ source "drivers/gpu/drm/hisilicon/Kconfig"
+--- a/drivers/gpu/drm/Makefile
++++ b/drivers/gpu/drm/Makefile
+@@ -148,3 +148,4 @@ obj-y			+= gud/
+ obj-$(CONFIG_DRM_HYPERV) += hyperv/
+ obj-y			+= solomon/
+ obj-$(CONFIG_DRM_SPRD) += sprd/
++obj-y += rp1/
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/Kconfig
+@@ -0,0 +1,5 @@
++source "drivers/gpu/drm/rp1/rp1-dsi/Kconfig"
++
++source "drivers/gpu/drm/rp1/rp1-dpi/Kconfig"
++
++source "drivers/gpu/drm/rp1/rp1-vec/Kconfig"
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/Makefile
+@@ -0,0 +1,4 @@
++obj-$(CONFIG_DRM_RP1_DSI) += rp1-dsi/
++obj-$(CONFIG_DRM_RP1_DPI) += rp1-dpi/
++obj-$(CONFIG_DRM_RP1_VEC) += rp1-vec/
++
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/Kconfig
+@@ -0,0 +1,15 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_DSI
++	tristate "DRM Support for RP1 DSI"
++	depends on DRM
++	select MFD_RP1
++	select DRM_GEM_DMA_HELPER
++	select DRM_KMS_HELPER
++	select DRM_MIPI_DSI
++	select DRM_VRAM_HELPER
++	select DRM_TTM
++	select DRM_TTM_HELPER
++	select GENERIC_PHY
++	select GENERIC_PHY_MIPI_DPHY
++	help
++	  Choose this option to enable DSI display on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-dsi-y := rp1_dsi.o rp1_dsi_dma.o rp1_dsi_dsi.o
++
++obj-$(CONFIG_DRM_RP1_DSI) += drm-rp1-dsi.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+@@ -0,0 +1,537 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/clk.h>
++#include <linux/component.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/phy/phy-mipi-dphy.h>
++#include <linux/string.h>
++
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_of.h>
++#include <drm/drm_print.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dsi.h"
++
++static inline struct rp1_dsi *
++bridge_to_rp1_dsi(struct drm_bridge *bridge)
++{
++	return container_of(bridge, struct rp1_dsi, bridge);
++}
++
++static void rp1_dsi_bridge_pre_enable(struct drm_bridge *bridge,
++				      struct drm_bridge_state *old_state)
++{
++	struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++	rp1dsi_dsi_setup(dsi, &dsi->pipe.crtc.state->adjusted_mode);
++}
++
++static void rp1_dsi_bridge_enable(struct drm_bridge *bridge,
++				  struct drm_bridge_state *old_state)
++{
++}
++
++static void rp1_dsi_bridge_disable(struct drm_bridge *bridge,
++				   struct drm_bridge_state *state)
++{
++}
++
++static void rp1_dsi_bridge_post_disable(struct drm_bridge *bridge,
++					struct drm_bridge_state *state)
++{
++	struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++	if (dsi->dsi_running) {
++		rp1dsi_dsi_stop(dsi);
++		dsi->dsi_running = false;
++	}
++}
++
++static int rp1_dsi_bridge_attach(struct drm_bridge *bridge,
++				 enum drm_bridge_attach_flags flags)
++{
++	struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++	/* Attach the panel or bridge to the dsi bridge */
++	return drm_bridge_attach(bridge->encoder, dsi->out_bridge,
++				 &dsi->bridge, flags);
++	return 0;
++}
++
++static const struct drm_bridge_funcs rp1_dsi_bridge_funcs = {
++	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
++	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
++	.atomic_reset = drm_atomic_helper_bridge_reset,
++	.atomic_pre_enable = rp1_dsi_bridge_pre_enable,
++	.atomic_enable = rp1_dsi_bridge_enable,
++	.atomic_disable = rp1_dsi_bridge_disable,
++	.atomic_post_disable = rp1_dsi_bridge_post_disable,
++	.attach = rp1_dsi_bridge_attach,
++};
++
++static void rp1dsi_pipe_update(struct drm_simple_display_pipe *pipe,
++			       struct drm_plane_state *old_state)
++{
++	struct drm_pending_vblank_event *event;
++	unsigned long flags;
++	struct drm_framebuffer *fb = pipe->plane.state->fb;
++	struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++	struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++	struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++	bool can_update = fb && dma_obj && dsi && dsi->pipe_enabled;
++
++	/* (Re-)start DSI,DMA where required; and update FB address */
++	if (can_update) {
++		if (!dsi->dma_running || fb->format->format != dsi->cur_fmt) {
++			if (dsi->dma_running && fb->format->format != dsi->cur_fmt) {
++				rp1dsi_dma_stop(dsi);
++				dsi->dma_running = false;
++			}
++			if (!dsi->dma_running) {
++				rp1dsi_dma_setup(dsi,
++						 fb->format->format, dsi->display_format,
++						&pipe->crtc.state->adjusted_mode);
++				dsi->dma_running = true;
++			}
++			dsi->cur_fmt  = fb->format->format;
++			drm_crtc_vblank_on(&pipe->crtc);
++		}
++		rp1dsi_dma_update(dsi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++	}
++
++	/* Arm VBLANK event (or call it immediately in some error cases) */
++	spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++	event = pipe->crtc.state->event;
++	if (event) {
++		pipe->crtc.state->event = NULL;
++		if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++			drm_crtc_arm_vblank_event(&pipe->crtc, event);
++		else
++			drm_crtc_send_vblank_event(&pipe->crtc, event);
++	}
++	spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static inline struct rp1_dsi *
++encoder_to_rp1_dsi(struct drm_encoder *encoder)
++{
++	struct drm_simple_display_pipe *pipe =
++		container_of(encoder, struct drm_simple_display_pipe, encoder);
++	return container_of(pipe, struct rp1_dsi, pipe);
++}
++
++static void rp1dsi_encoder_enable(struct drm_encoder *encoder)
++{
++	struct rp1_dsi *dsi = encoder_to_rp1_dsi(encoder);
++
++	/* Put DSI into video mode before starting video */
++	rp1dsi_dsi_set_cmdmode(dsi, 0);
++
++	/* Start DMA -> DPI */
++	dsi->pipe_enabled = true;
++	dsi->cur_fmt = 0xdeadbeef;
++	rp1dsi_pipe_update(&dsi->pipe, 0);
++}
++
++static void rp1dsi_encoder_disable(struct drm_encoder *encoder)
++{
++	struct rp1_dsi *dsi = encoder_to_rp1_dsi(encoder);
++
++	drm_crtc_vblank_off(&dsi->pipe.crtc);
++	if (dsi->dma_running) {
++		rp1dsi_dma_stop(dsi);
++		dsi->dma_running = false;
++	}
++	dsi->pipe_enabled = false;
++
++	/* Return to command mode after stopping video */
++	rp1dsi_dsi_set_cmdmode(dsi, 1);
++}
++
++static const struct drm_encoder_helper_funcs rp1_dsi_encoder_funcs = {
++	.enable = rp1dsi_encoder_enable,
++	.disable = rp1dsi_encoder_disable,
++};
++
++static void rp1dsi_pipe_enable(struct drm_simple_display_pipe *pipe,
++			       struct drm_crtc_state *crtc_state,
++			       struct drm_plane_state *plane_state)
++{
++}
++
++static void rp1dsi_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++}
++
++static int rp1dsi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++
++	if (dsi)
++		rp1dsi_dma_vblank_ctrl(dsi, 1);
++
++	return 0;
++}
++
++static void rp1dsi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++
++	if (dsi)
++		rp1dsi_dma_vblank_ctrl(dsi, 0);
++}
++
++static const struct drm_simple_display_pipe_funcs rp1dsi_pipe_funcs = {
++	.enable	    = rp1dsi_pipe_enable,
++	.update	    = rp1dsi_pipe_update,
++	.disable    = rp1dsi_pipe_disable,
++	.prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++	.enable_vblank  = rp1dsi_pipe_enable_vblank,
++	.disable_vblank = rp1dsi_pipe_disable_vblank,
++};
++
++static const struct drm_mode_config_funcs rp1dsi_mode_funcs = {
++	.fb_create = drm_gem_fb_create,
++	.atomic_check = drm_atomic_helper_check,
++	.atomic_commit = drm_atomic_helper_commit,
++};
++
++static const u32 rp1dsi_formats[] = {
++	DRM_FORMAT_XRGB8888,
++	DRM_FORMAT_XBGR8888,
++	DRM_FORMAT_RGB888,
++	DRM_FORMAT_BGR888,
++	DRM_FORMAT_RGB565
++};
++
++static void rp1dsi_stopall(struct drm_device *drm)
++{
++	if (drm->dev_private) {
++		struct rp1_dsi *dsi = drm->dev_private;
++
++		if (dsi->dma_running || rp1dsi_dma_busy(dsi)) {
++			rp1dsi_dma_stop(dsi);
++			dsi->dma_running = false;
++		}
++		if (dsi->dsi_running) {
++			rp1dsi_dsi_stop(dsi);
++			dsi->dsi_running = false;
++		}
++		if (dsi->clocks[RP1DSI_CLOCK_CFG])
++			clk_disable_unprepare(dsi->clocks[RP1DSI_CLOCK_CFG]);
++	}
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1dsi_fops);
++
++static struct drm_driver rp1dsi_driver = {
++	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++	.fops			= &rp1dsi_fops,
++	.name			= "drm-rp1-dsi",
++	.desc			= "drm-rp1-dsi",
++	.date			= "0",
++	.major			= 1,
++	.minor			= 0,
++	DRM_GEM_DMA_DRIVER_OPS,
++	.release		= rp1dsi_stopall,
++};
++
++static int rp1dsi_bind(struct rp1_dsi *dsi)
++{
++	struct platform_device *pdev = dsi->pdev;
++	struct drm_device *drm = dsi->drm;
++	int ret;
++
++	dsi->out_bridge = drmm_of_get_bridge(drm, pdev->dev.of_node, 0, 0);
++	if (IS_ERR(dsi->out_bridge))
++		return PTR_ERR(dsi->out_bridge);
++
++	ret = drmm_mode_config_init(drm);
++	if (ret)
++		goto rtn;
++
++	drm->mode_config.max_width  = 4096;
++	drm->mode_config.max_height = 4096;
++	drm->mode_config.fb_base    = 0;
++	drm->mode_config.preferred_depth = 32;
++	drm->mode_config.prefer_shadow	 = 0;
++	drm->mode_config.prefer_shadow_fbdev = 1;
++	drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++	drm->mode_config.funcs = &rp1dsi_mode_funcs;
++	drm_vblank_init(drm, 1);
++
++	ret = drm_simple_display_pipe_init(drm,
++					   &dsi->pipe,
++					   &rp1dsi_pipe_funcs,
++					   rp1dsi_formats,
++					   ARRAY_SIZE(rp1dsi_formats),
++					   NULL, NULL);
++	if (ret)
++		goto rtn;
++
++	/* We need slightly more complex encoder handling (enabling/disabling
++	 * video mode), so add encoder helper functions.
++	 */
++	drm_encoder_helper_add(&dsi->pipe.encoder, &rp1_dsi_encoder_funcs);
++
++	ret = drm_simple_display_pipe_attach_bridge(&dsi->pipe, &dsi->bridge);
++	if (ret)
++		goto rtn;
++
++	drm_bridge_add(&dsi->bridge);
++
++	drm_mode_config_reset(drm);
++
++	if (dsi->clocks[RP1DSI_CLOCK_CFG])
++		clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_CFG]);
++
++	ret = drm_dev_register(drm, 0);
++
++	if (ret == 0)
++		drm_fbdev_generic_setup(drm, 32);
++
++rtn:
++	if (ret)
++		dev_err(&pdev->dev, "%s returned %d\n", __func__, ret);
++	else
++		dev_info(&pdev->dev, "%s succeeded", __func__);
++
++	return ret;
++}
++
++static void rp1dsi_unbind(struct rp1_dsi *dsi)
++{
++	struct drm_device *drm = dsi->drm;
++
++	rp1dsi_stopall(drm);
++	drm_dev_unregister(drm);
++	drm_atomic_helper_shutdown(drm);
++}
++
++int rp1dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *dsi_dev)
++{
++	struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++
++	dev_info(&dsi->pdev->dev, "%s: Attach DSI device name=%s channel=%d lanes=%d format=%d flags=0x%lx hs_rate=%lu lp_rate=%lu",
++		 __func__, dsi_dev->name, dsi_dev->channel, dsi_dev->lanes,
++		 dsi_dev->format, dsi_dev->mode_flags, dsi_dev->hs_rate,
++		 dsi_dev->lp_rate);
++	dsi->vc              = dsi_dev->channel & 3;
++	dsi->lanes           = dsi_dev->lanes;
++
++	switch (dsi_dev->format) {
++	case MIPI_DSI_FMT_RGB666:
++	case MIPI_DSI_FMT_RGB666_PACKED:
++	case MIPI_DSI_FMT_RGB565:
++	case MIPI_DSI_FMT_RGB888:
++		break;
++	default:
++		return -EINVAL;
++	}
++	dsi->display_format  = dsi_dev->format;
++	dsi->display_flags   = dsi_dev->mode_flags;
++	dsi->display_hs_rate = dsi_dev->hs_rate;
++	dsi->display_lp_rate = dsi_dev->lp_rate;
++
++	/*
++	 * Previously, we added a separate component to handle panel/bridge
++	 * discovery and DRM registration, but now it's just a function call.
++	 * The downstream/attaching device should deal with -EPROBE_DEFER
++	 */
++	return rp1dsi_bind(dsi);
++}
++
++int rp1dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *dsi_dev)
++{
++	struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++
++	/*
++	 * Unregister the DRM driver.
++	 * TODO: Check we are cleaning up correctly and not doing things multiple times!
++	 */
++	rp1dsi_unbind(dsi);
++	return 0;
++}
++
++ssize_t rp1dsi_host_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg)
++{
++	struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++	struct mipi_dsi_packet packet;
++	int ret = 0;
++
++	/* Write */
++	ret = mipi_dsi_create_packet(&packet, msg);
++	if (ret) {
++		dev_err(dsi->drm->dev, "RP1DSI: failed to create packet: %d\n", ret);
++		return ret;
++	}
++
++	rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header), packet.payload_length, packet.payload);
++
++	/* Optional read back */
++	if (msg->rx_len && msg->rx_buf)
++		ret = rp1dsi_dsi_recv(dsi, msg->rx_len, msg->rx_buf);
++
++	return (ssize_t)ret;
++}
++
++static const struct mipi_dsi_host_ops rp1dsi_mipi_dsi_host_ops = {
++	.attach = rp1dsi_host_attach,
++	.detach = rp1dsi_host_detach,
++	.transfer = rp1dsi_host_transfer
++};
++
++static int rp1dsi_platform_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct drm_device *drm;
++	struct rp1_dsi *dsi;
++	int i, ret;
++
++	drm = drm_dev_alloc(&rp1dsi_driver, dev);
++	if (IS_ERR(drm)) {
++		ret = PTR_ERR(drm);
++		return ret;
++	}
++	dsi = drmm_kzalloc(drm, sizeof(*dsi), GFP_KERNEL);
++	if (!dsi) {
++		ret = -ENOMEM;
++		goto err_free_drm;
++	}
++	init_completion(&dsi->finished);
++	dsi->drm = drm;
++	dsi->pdev = pdev;
++	drm->dev_private = dsi;
++	platform_set_drvdata(pdev, drm);
++
++	dsi->bridge.funcs = &rp1_dsi_bridge_funcs;
++	dsi->bridge.of_node = dev->of_node;
++	dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
++
++	/* Safe default values for DSI mode */
++	dsi->lanes = 1;
++	dsi->display_format = MIPI_DSI_FMT_RGB888;
++	dsi->display_flags  = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
++
++	/* Hardware resources */
++	for (i = 0; i < RP1DSI_NUM_CLOCKS; i++) {
++		static const char * const myclocknames[RP1DSI_NUM_CLOCKS] = {
++			"cfgclk", "dpiclk", "byteclk", "refclk"
++		};
++		dsi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
++		if (IS_ERR(dsi->clocks[i])) {
++			ret = PTR_ERR(dsi->clocks[i]);
++			dev_err(dev, "Error getting clocks[%d]\n", i);
++			goto err_free_drm;
++		}
++	}
++
++	for (i = 0; i < RP1DSI_NUM_HW_BLOCKS; i++) {
++		dsi->hw_base[i] =
++			devm_ioremap_resource(dev,
++					      platform_get_resource(dsi->pdev,
++								    IORESOURCE_MEM,
++								    i));
++		if (IS_ERR(dsi->hw_base[i])) {
++			ret = PTR_ERR(dsi->hw_base[i]);
++			dev_err(dev, "Error memory mapping regs[%d]\n", i);
++			goto err_free_drm;
++		}
++	}
++	ret = platform_get_irq(dsi->pdev, 0);
++	if (ret > 0)
++		ret = devm_request_irq(dev, ret, rp1dsi_dma_isr,
++				       IRQF_SHARED, "rp1-dsi", dsi);
++	if (ret) {
++		dev_err(dev, "Unable to request interrupt\n");
++		ret = -EINVAL;
++		goto err_free_drm;
++	}
++	rp1dsi_mipicfg_setup(dsi);
++	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++	/* Create the MIPI DSI Host and wait for the panel/bridge to attach to it */
++	dsi->dsi_host.ops = &rp1dsi_mipi_dsi_host_ops;
++	dsi->dsi_host.dev = dev;
++	ret = mipi_dsi_host_register(&dsi->dsi_host);
++	if (ret)
++		goto err_free_drm;
++
++	return ret;
++
++err_free_drm:
++	dev_err(dev, "%s fail %d\n", __func__, ret);
++	drm_dev_put(drm);
++	return ret;
++}
++
++static int rp1dsi_platform_remove(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++	struct rp1_dsi *dsi = drm->dev_private;
++
++	mipi_dsi_host_unregister(&dsi->dsi_host);
++	return 0;
++}
++
++static void rp1dsi_platform_shutdown(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++
++	rp1dsi_stopall(drm);
++}
++
++static const struct of_device_id rp1dsi_of_match[] = {
++	{
++		.compatible = "raspberrypi,rp1dsi",
++	},
++	{ /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1dsi_of_match);
++
++static struct platform_driver rp1dsi_platform_driver = {
++	.probe		= rp1dsi_platform_probe,
++	.remove		= rp1dsi_platform_remove,
++	.shutdown       = rp1dsi_platform_shutdown,
++	.driver		= {
++		.name	= DRIVER_NAME,
++		.owner  = THIS_MODULE,
++		.of_match_table = rp1dsi_of_match,
++	},
++};
++
++module_platform_driver(rp1dsi_platform_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("MIPI DSI driver for Raspberry Pi RP1");
++MODULE_AUTHOR("Nick Hollinghurst");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
+@@ -0,0 +1,94 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++#ifndef _RP1_DSI_H_
++#define _RP1_DSI_H_
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/types.h>
++
++#include <drm/drm_bridge.h>
++#include <drm/drm_device.h>
++#include <drm/drm_mipi_dsi.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-dsi"
++#define DRIVER_NAME "drm-rp1-dsi"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1DSI_HW_BLOCK_DMA   0
++#define RP1DSI_HW_BLOCK_DSI   1
++#define RP1DSI_HW_BLOCK_CFG   2
++#define RP1DSI_NUM_HW_BLOCKS  3
++
++#define RP1DSI_CLOCK_CFG     0
++#define RP1DSI_CLOCK_DPI     1
++#define RP1DSI_CLOCK_BYTE    2
++#define RP1DSI_CLOCK_REF     3
++#define RP1DSI_NUM_CLOCKS    4
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_dsi {
++	/* DRM and platform device pointers */
++	struct drm_device *drm;
++	struct platform_device *pdev;
++
++	/* Framework and helper objects */
++	struct drm_simple_display_pipe pipe;
++	struct drm_bridge bridge;
++	struct drm_bridge *out_bridge;
++	struct mipi_dsi_host dsi_host;
++
++	/* Clocks. We need DPI clock; the others are frequency references */
++	struct clk *clocks[RP1DSI_NUM_CLOCKS];
++
++	/* Block (DSI DMA, DSI Host) base addresses, and current state */
++	void __iomem *hw_base[RP1DSI_NUM_HW_BLOCKS];
++	u32 cur_fmt;
++	bool dsi_running, dma_running, pipe_enabled;
++	struct completion finished;
++
++	/* Attached display parameters (from mipi_dsi_device) */
++	unsigned long display_flags, display_hs_rate, display_lp_rate;
++	enum mipi_dsi_pixel_format display_format;
++	u8 vc;
++	u8 lanes;
++
++	/* DPHY */
++	u8 hsfreq_index;
++};
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the DSI/DPI/DMA block				  */
++
++void rp1dsi_dma_setup(struct rp1_dsi *dsi,
++		      u32 in_format, enum mipi_dsi_pixel_format out_format,
++		      struct drm_display_mode const *mode);
++void rp1dsi_dma_update(struct rp1_dsi *dsi, dma_addr_t addr, u32 offset, u32 stride);
++void rp1dsi_dma_stop(struct rp1_dsi *dsi);
++int rp1dsi_dma_busy(struct rp1_dsi *dsi);
++irqreturn_t rp1dsi_dma_isr(int irq, void *dev);
++void rp1dsi_dma_vblank_ctrl(struct rp1_dsi *dsi, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the MIPICFG block and check RP1 platform		  */
++
++void rp1dsi_mipicfg_setup(struct rp1_dsi *dsi);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the SNPS D-PHY and DSI block setup		  */
++
++void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode);
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf);
++int  rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf);
++void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int cmd_mode);
++void rp1dsi_dsi_stop(struct rp1_dsi *dsi);
++
++#endif
++
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
+@@ -0,0 +1,443 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dsi.h"
++
++// --- DPI DMA REGISTERS (derived from Argon firmware, via RP1 drivers/mipi, with corrections) ---
++
++// Control
++#define DPI_DMA_CONTROL				      0x0
++#define DPI_DMA_CONTROL_ARM_SHIFT		      0
++#define DPI_DMA_CONTROL_ARM_MASK		      BIT(DPI_DMA_CONTROL_ARM_SHIFT)
++#define DPI_DMA_CONTROL_ALIGN16_SHIFT		      2
++#define DPI_DMA_CONTROL_ALIGN16_MASK		      BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
++#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT	      1
++#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK	      BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
++#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT	      3
++#define DPI_DMA_CONTROL_HIGH_WATER_MASK		      (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
++#define DPI_DMA_CONTROL_DEN_POL_SHIFT		      12
++#define DPI_DMA_CONTROL_DEN_POL_MASK		      BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT		      13
++#define DPI_DMA_CONTROL_HSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT		      14
++#define DPI_DMA_CONTROL_VSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_COLORM_SHIFT		      15
++#define DPI_DMA_CONTROL_COLORM_MASK		      BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
++#define DPI_DMA_CONTROL_SHUTDN_SHIFT		      16
++#define DPI_DMA_CONTROL_SHUTDN_MASK		      BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
++#define DPI_DMA_CONTROL_HBP_EN_SHIFT		      17
++#define DPI_DMA_CONTROL_HBP_EN_MASK		      BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HFP_EN_SHIFT		      18
++#define DPI_DMA_CONTROL_HFP_EN_MASK		      BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VBP_EN_SHIFT		      19
++#define DPI_DMA_CONTROL_VBP_EN_MASK		      BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VFP_EN_SHIFT		      20
++#define DPI_DMA_CONTROL_VFP_EN_MASK		      BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT		      21
++#define DPI_DMA_CONTROL_HSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT		      22
++#define DPI_DMA_CONTROL_VSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT	      23
++#define DPI_DMA_CONTROL_FORCE_IMMED_MASK	      BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT	      24
++#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK	      BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT	      25
++#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK	      BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
++
++// IRQ_ENABLES
++#define DPI_DMA_IRQ_EN				      0x04
++#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT		      0
++#define DPI_DMA_IRQ_EN_DMA_READY_MASK		      BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT		      1
++#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK		      BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT	      2
++#define DPI_DMA_IRQ_EN_FRAME_START_MASK		      BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT	      3
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK		      BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_EN_TE_SHIFT			      4
++#define DPI_DMA_IRQ_EN_TE_MASK			      BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
++#define DPI_DMA_IRQ_EN_ERROR_SHIFT		      5
++#define DPI_DMA_IRQ_EN_ERROR_MASK		      BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_SHIFT		      6
++#define DPI_DMA_IRQ_EN_MATCH_MASK		      BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT		      16
++#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK		      (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
++
++// IRQ_FLAGS
++#define DPI_DMA_IRQ_FLAGS			      0x08
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT	      0
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT	      1
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK	      BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT	      2
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK	      BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT	      3
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_TE_SHIFT		      4
++#define DPI_DMA_IRQ_FLAGS_TE_MASK		      BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT		      5
++#define DPI_DMA_IRQ_FLAGS_ERROR_MASK		      BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT		      6
++#define DPI_DMA_IRQ_FLAGS_MATCH_MASK		      BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
++
++// QOS
++#define DPI_DMA_QOS				      0xC
++#define DPI_DMA_QOS_DQOS_SHIFT			      0
++#define DPI_DMA_QOS_DQOS_MASK			      (0xF << DPI_DMA_QOS_DQOS_SHIFT)
++#define DPI_DMA_QOS_ULEV_SHIFT			      4
++#define DPI_DMA_QOS_ULEV_MASK			      (0xF << DPI_DMA_QOS_ULEV_SHIFT)
++#define DPI_DMA_QOS_UQOS_SHIFT			      8
++#define DPI_DMA_QOS_UQOS_MASK			      (0xF << DPI_DMA_QOS_UQOS_SHIFT)
++#define DPI_DMA_QOS_LLEV_SHIFT			      12
++#define DPI_DMA_QOS_LLEV_MASK			      (0xF << DPI_DMA_QOS_LLEV_SHIFT)
++#define DPI_DMA_QOS_LQOS_SHIFT			      16
++#define DPI_DMA_QOS_LQOS_MASK			      (0xF << DPI_DMA_QOS_LQOS_SHIFT)
++
++// Panics
++#define DPI_DMA_PANICS				     0x38
++#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT	     0
++#define DPI_DMA_PANICS_UPPER_COUNT_MASK		     \
++				(0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
++#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT	     16
++#define DPI_DMA_PANICS_LOWER_COUNT_MASK		     \
++				(0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
++
++// DMA Address Lower:
++#define DPI_DMA_DMA_ADDR_L			     0x10
++
++// DMA Address Upper:
++#define DPI_DMA_DMA_ADDR_H			     0x40
++
++// DMA stride
++#define DPI_DMA_DMA_STRIDE			     0x14
++
++// Visible Area
++#define DPI_DMA_VISIBLE_AREA			     0x18
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT     0
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
++#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT    16
++#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
++
++// Sync width
++#define DPI_DMA_SYNC_WIDTH   0x1C
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT	 0
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
++#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT	 16
++#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
++
++// Back porch
++#define DPI_DMA_BACK_PORCH   0x20
++#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT	 0
++#define DPI_DMA_BACK_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT	 16
++#define DPI_DMA_BACK_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
++
++// Front porch
++#define DPI_DMA_FRONT_PORCH  0x24
++#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT     0
++#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT     16
++#define DPI_DMA_FRONT_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
++
++// Input masks
++#define DPI_DMA_IMASK	 0x2C
++#define DPI_DMA_IMASK_R_SHIFT	 0
++#define DPI_DMA_IMASK_R_MASK	 (0x3FF << DPI_DMA_IMASK_R_SHIFT)
++#define DPI_DMA_IMASK_G_SHIFT	 10
++#define DPI_DMA_IMASK_G_MASK	 (0x3FF << DPI_DMA_IMASK_G_SHIFT)
++#define DPI_DMA_IMASK_B_SHIFT	 20
++#define DPI_DMA_IMASK_B_MASK	 (0x3FF << DPI_DMA_IMASK_B_SHIFT)
++
++// Output Masks
++#define DPI_DMA_OMASK	 0x30
++#define DPI_DMA_OMASK_R_SHIFT	 0
++#define DPI_DMA_OMASK_R_MASK	 (0x3FF << DPI_DMA_OMASK_R_SHIFT)
++#define DPI_DMA_OMASK_G_SHIFT	 10
++#define DPI_DMA_OMASK_G_MASK	 (0x3FF << DPI_DMA_OMASK_G_SHIFT)
++#define DPI_DMA_OMASK_B_SHIFT	 20
++#define DPI_DMA_OMASK_B_MASK	 (0x3FF << DPI_DMA_OMASK_B_SHIFT)
++
++// Shifts
++#define DPI_DMA_SHIFT	 0x28
++#define DPI_DMA_SHIFT_IR_SHIFT	 0
++#define DPI_DMA_SHIFT_IR_MASK	 (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
++#define DPI_DMA_SHIFT_IG_SHIFT	 5
++#define DPI_DMA_SHIFT_IG_MASK	 (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
++#define DPI_DMA_SHIFT_IB_SHIFT	 10
++#define DPI_DMA_SHIFT_IB_MASK	 (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
++#define DPI_DMA_SHIFT_OR_SHIFT	 15
++#define DPI_DMA_SHIFT_OR_MASK	 (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
++#define DPI_DMA_SHIFT_OG_SHIFT	 20
++#define DPI_DMA_SHIFT_OG_MASK	 (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
++#define DPI_DMA_SHIFT_OB_SHIFT	 25
++#define DPI_DMA_SHIFT_OB_MASK	 (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
++
++// Scaling
++#define DPI_DMA_RGBSZ	 0x34
++#define DPI_DMA_RGBSZ_BPP_SHIFT	 16
++#define DPI_DMA_RGBSZ_BPP_MASK	 (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
++#define DPI_DMA_RGBSZ_R_SHIFT	 0
++#define DPI_DMA_RGBSZ_R_MASK	 (0xF << DPI_DMA_RGBSZ_R_SHIFT)
++#define DPI_DMA_RGBSZ_G_SHIFT	 4
++#define DPI_DMA_RGBSZ_G_MASK	 (0xF << DPI_DMA_RGBSZ_G_SHIFT)
++#define DPI_DMA_RGBSZ_B_SHIFT	 8
++#define DPI_DMA_RGBSZ_B_MASK	 (0xF << DPI_DMA_RGBSZ_B_SHIFT)
++
++// Status
++#define DPI_DMA_STATUS  0x3c
++
++#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
++
++static unsigned int rp1dsi_dma_read(struct rp1_dsi *dsi, unsigned int reg)
++{
++	void __iomem *addr = dsi->hw_base[RP1DSI_HW_BLOCK_DMA] + reg;
++
++	return readl(addr);
++}
++
++static void rp1dsi_dma_write(struct rp1_dsi *dsi, unsigned int reg, unsigned int val)
++{
++	void __iomem *addr = dsi->hw_base[RP1DSI_HW_BLOCK_DMA] + reg;
++
++	writel(val, addr);
++}
++
++int rp1dsi_dma_busy(struct rp1_dsi *dsi)
++{
++	return (rp1dsi_dma_read(dsi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1dsi_ipixfmt {
++	u32 format; /* DRM format code                           */
++	u32 mask;   /* RGB masks (10 bits each, left justified)  */
++	u32 shift;  /* RGB MSB positions in the memory word      */
++	u32 rgbsz;  /* Shifts used for scaling; also (BPP/8-1)   */
++};
++
++#define IMASK_RGB(r, g, b)	(BITS(DPI_DMA_IMASK_R, r) | \
++				 BITS(DPI_DMA_IMASK_G, g) |  \
++				 BITS(DPI_DMA_IMASK_B, b))
++#define ISHIFT_RGB(r, g, b)	(BITS(DPI_DMA_SHIFT_IR, r) | \
++				 BITS(DPI_DMA_SHIFT_IG, g) | \
++				 BITS(DPI_DMA_SHIFT_IB, b))
++
++static const struct rp1dsi_ipixfmt my_formats[] = {
++	{
++		.format = DRM_FORMAT_XRGB8888,
++		.mask   = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = ISHIFT_RGB(23, 15, 7),
++		.rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
++	},
++	{
++		.format = DRM_FORMAT_XBGR8888,
++		.mask   = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = ISHIFT_RGB(7, 15, 23),
++		.rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
++	},
++	{
++		.format = DRM_FORMAT_RGB888,
++		.mask   = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = ISHIFT_RGB(23, 15, 7),
++		.rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
++	},
++	{
++		.format = DRM_FORMAT_BGR888,
++		.mask   = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = ISHIFT_RGB(7, 15, 23),
++		.rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
++	},
++	{
++		.format = DRM_FORMAT_RGB565,
++		.mask   = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++		.shift  = ISHIFT_RGB(15, 10, 4),
++		.rgbsz  = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++			  BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++	}
++};
++
++/* Choose the internal on-the-bus DPI format as expected by DSI Host. */
++static u32 get_omask_oshift(enum mipi_dsi_pixel_format fmt, u32 *oshift)
++{
++	switch (fmt) {
++	case MIPI_DSI_FMT_RGB565:
++		*oshift = BITS(DPI_DMA_SHIFT_OR, 15) |
++			  BITS(DPI_DMA_SHIFT_OG, 10) |
++			  BITS(DPI_DMA_SHIFT_OB, 4);
++		return BITS(DPI_DMA_OMASK_R, 0x3e0) |
++		       BITS(DPI_DMA_OMASK_G, 0x3f0) |
++		       BITS(DPI_DMA_OMASK_B, 0x3e0);
++	case MIPI_DSI_FMT_RGB666_PACKED:
++		*oshift = BITS(DPI_DMA_SHIFT_OR, 17) |
++			  BITS(DPI_DMA_SHIFT_OG, 11) |
++			  BITS(DPI_DMA_SHIFT_OB, 5);
++		return BITS(DPI_DMA_OMASK_R, 0x3f0) |
++		       BITS(DPI_DMA_OMASK_G, 0x3f0) |
++		       BITS(DPI_DMA_OMASK_B, 0x3f0);
++	case MIPI_DSI_FMT_RGB666:
++		*oshift = BITS(DPI_DMA_SHIFT_OR, 21) |
++			  BITS(DPI_DMA_SHIFT_OG, 13) |
++			  BITS(DPI_DMA_SHIFT_OB, 5);
++		return BITS(DPI_DMA_OMASK_R, 0x3f0) |
++		       BITS(DPI_DMA_OMASK_G, 0x3f0) |
++		       BITS(DPI_DMA_OMASK_B, 0x3f0);
++	default:
++		*oshift = BITS(DPI_DMA_SHIFT_OR, 23) |
++			  BITS(DPI_DMA_SHIFT_OG, 15) |
++			  BITS(DPI_DMA_SHIFT_OB, 7);
++		return BITS(DPI_DMA_OMASK_R, 0x3fc) |
++		       BITS(DPI_DMA_OMASK_G, 0x3fc) |
++		       BITS(DPI_DMA_OMASK_B, 0x3fc);
++	}
++}
++
++void rp1dsi_dma_setup(struct rp1_dsi *dsi,
++		      u32 in_format, enum mipi_dsi_pixel_format out_format,
++		     struct drm_display_mode const *mode)
++{
++	u32 oshift;
++	int i;
++
++	/*
++	 * Configure all DSI/DPI/DMA block registers, except base address.
++	 * DMA will not actually start until a FB base address is specified
++	 * using rp1dsi_dma_update().
++	 */
++
++	rp1dsi_dma_write(dsi, DPI_DMA_VISIBLE_AREA,
++			 BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
++			 BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
++
++	rp1dsi_dma_write(dsi, DPI_DMA_SYNC_WIDTH,
++			 BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
++			 BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
++
++	/* In the DPIDMA registers, "back porch" time includes sync width */
++	rp1dsi_dma_write(dsi, DPI_DMA_BACK_PORCH,
++			 BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
++			 BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
++
++	rp1dsi_dma_write(dsi, DPI_DMA_FRONT_PORCH,
++			 BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
++			 BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
++
++	/* Input to output pixel format conversion */
++	for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++		if (my_formats[i].format == in_format)
++			break;
++	}
++	if (i >= ARRAY_SIZE(my_formats)) {
++		drm_err(dsi->drm, "%s: bad input format\n", __func__);
++		i = 0;
++	}
++	rp1dsi_dma_write(dsi, DPI_DMA_IMASK, my_formats[i].mask);
++	rp1dsi_dma_write(dsi, DPI_DMA_OMASK, get_omask_oshift(out_format, &oshift));
++	rp1dsi_dma_write(dsi, DPI_DMA_SHIFT, my_formats[i].shift | oshift);
++	if (out_format == MIPI_DSI_FMT_RGB888)
++		rp1dsi_dma_write(dsi, DPI_DMA_RGBSZ, my_formats[i].rgbsz);
++	else
++		rp1dsi_dma_write(dsi, DPI_DMA_RGBSZ, my_formats[i].rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++
++	rp1dsi_dma_write(dsi, DPI_DMA_QOS,
++			 BITS(DPI_DMA_QOS_DQOS, 0x0) |
++			 BITS(DPI_DMA_QOS_ULEV, 0xb) |
++			 BITS(DPI_DMA_QOS_UQOS, 0x2) |
++			 BITS(DPI_DMA_QOS_LLEV, 0x8) |
++			 BITS(DPI_DMA_QOS_LQOS, 0x7));
++
++	rp1dsi_dma_write(dsi, DPI_DMA_IRQ_FLAGS, -1);
++	rp1dsi_dma_vblank_ctrl(dsi, 1);
++
++	i = rp1dsi_dma_busy(dsi);
++	if (i)
++		drm_err(dsi->drm, "RP1DSI: Unexpectedly busy at start!");
++
++	rp1dsi_dma_write(dsi, DPI_DMA_CONTROL,
++			 BITS(DPI_DMA_CONTROL_ARM, (i == 0)) |
++			 BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) |
++			 BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) |
++			 BITS(DPI_DMA_CONTROL_DEN_POL, 0) |
++			 BITS(DPI_DMA_CONTROL_HSYNC_POL, 0) |
++			 BITS(DPI_DMA_CONTROL_VSYNC_POL, 0) |
++			 BITS(DPI_DMA_CONTROL_COLORM, 0) |
++			 BITS(DPI_DMA_CONTROL_SHUTDN, 0) |
++			 BITS(DPI_DMA_CONTROL_HBP_EN, 1) |
++			 BITS(DPI_DMA_CONTROL_HFP_EN, 1) |
++			 BITS(DPI_DMA_CONTROL_VBP_EN, 1) |
++			 BITS(DPI_DMA_CONTROL_VFP_EN, 1) |
++			 BITS(DPI_DMA_CONTROL_HSYNC_EN, 1) |
++			 BITS(DPI_DMA_CONTROL_VSYNC_EN, 1));
++}
++
++void rp1dsi_dma_update(struct rp1_dsi *dsi, dma_addr_t addr, u32 offset, u32 stride)
++{
++	/*
++	 * Update STRIDE, DMAH and DMAL only. When called after rp1dsi_dma_setup(),
++	 * DMA starts immediately; if already running, the buffer will flip at
++	 * the next vertical sync event.
++	 */
++	u64 a = addr + offset;
++
++	rp1dsi_dma_write(dsi, DPI_DMA_DMA_STRIDE, stride);
++	rp1dsi_dma_write(dsi, DPI_DMA_DMA_ADDR_H, a >> 32);
++	rp1dsi_dma_write(dsi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1dsi_dma_stop(struct rp1_dsi *dsi)
++{
++	/*
++	 * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++	 * the current and any queued frame to end. "Force drain" flags are not used,
++	 * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++	 */
++	u32 ctrl;
++
++	reinit_completion(&dsi->finished);
++	ctrl = rp1dsi_dma_read(dsi, DPI_DMA_CONTROL);
++	ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
++	rp1dsi_dma_write(dsi, DPI_DMA_CONTROL, ctrl);
++	if (!wait_for_completion_timeout(&dsi->finished, HZ / 10))
++		drm_err(dsi->drm, "%s: timed out waiting for idle\n", __func__);
++	rp1dsi_dma_write(dsi, DPI_DMA_IRQ_EN, 0);
++}
++
++void rp1dsi_dma_vblank_ctrl(struct rp1_dsi *dsi, int enable)
++{
++	rp1dsi_dma_write(dsi, DPI_DMA_IRQ_EN,
++			 BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1)      |
++			 BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1)        |
++			 BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
++			 BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
++}
++
++irqreturn_t rp1dsi_dma_isr(int irq, void *dev)
++{
++	struct rp1_dsi *dsi = dev;
++	u32 u = rp1dsi_dma_read(dsi, DPI_DMA_IRQ_FLAGS);
++
++	if (u) {
++		rp1dsi_dma_write(dsi, DPI_DMA_IRQ_FLAGS, u);
++		if (dsi) {
++			if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
++				drm_err_ratelimited(dsi->drm,
++						    "Underflow! (panics=0x%08x)\n",
++						    rp1dsi_dma_read(dsi, DPI_DMA_PANICS));
++			if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
++				drm_crtc_handle_vblank(&dsi->pipe.crtc);
++			if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
++				complete(&dsi->finished);
++		}
++	}
++	return u ? IRQ_HANDLED : IRQ_NONE;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
+@@ -0,0 +1,1504 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include "drm/drm_print.h"
++
++#include "rp1_dsi.h"
++
++/* ------------------------------- Synopsis DSI ------------------------ */
++#define     DSI_VERSION_CFG                       0x000
++#define     DSI_PWR_UP                            0x004
++#define     DSI_CLKMGR_CFG                        0x008
++#define     DSI_DPI_VCID                          0x00C
++#define     DSI_DPI_COLOR_CODING                  0x010
++#define     DSI_DPI_CFG_POL                       0x014
++#define     DSI_DPI_LP_CMD_TIM                    0x018
++#define     DSI_DBI_VCID                          0x01C
++#define     DSI_DBI_CFG                           0x020
++#define     DSI_DBI_PARTITIONING_EN               0x024
++#define     DSI_DBI_CMDSIZE                       0x028
++#define     DSI_PCKHDL_CFG                        0x02C
++#define     DSI_GEN_VCID                          0x030
++#define     DSI_MODE_CFG                          0x034
++#define     DSI_VID_MODE_CFG                      0x038
++#define     DSI_VID_PKT_SIZE                      0x03C
++#define     DSI_VID_NUM_CHUNKS                    0x040
++#define     DSI_VID_NULL_SIZE                     0x044
++#define     DSI_VID_HSA_TIME                      0x048
++#define     DSI_VID_HBP_TIME                      0x04C
++#define     DSI_VID_HLINE_TIME                    0x050
++#define     DSI_VID_VSA_LINES                     0x054
++#define     DSI_VID_VBP_LINES                     0x058
++#define     DSI_VID_VFP_LINES                     0x05C
++#define     DSI_VID_VACTIVE_LINES                 0x060
++#define     DSI_EDPI_CMD_SIZE                     0x064
++#define     DSI_CMD_MODE_CFG                      0x068
++#define     DSI_GEN_HDR                           0x06C
++#define     DSI_GEN_PLD_DATA                      0x070
++#define     DSI_CMD_PKT_STATUS                    0x074
++#define     DSI_TO_CNT_CFG                        0x078
++#define     DSI_HS_RD_TO_CNT                      0x07C
++#define     DSI_LP_RD_TO_CNT                      0x080
++#define     DSI_HS_WR_TO_CNT                      0x084
++#define     DSI_LP_WR_TO_CNT                      0x088
++#define     DSI_BTA_TO_CNT                        0x08C
++#define     DSI_SDF_3D                            0x090
++#define     DSI_LPCLK_CTRL                        0x094
++#define     DSI_PHY_TMR_LPCLK_CFG                 0x098
++#define     DSI_PHY_TMR_HS2LP_LSB       16
++#define     DSI_PHY_TMR_LP2HS_LSB       0
++#define     DSI_PHY_TMR_CFG                       0x09C
++#define     DSI_PHY_TMR_RD_CFG                    0x0F4
++#define     DSI_PHYRSTZ                           0x0A0
++#define     DSI_PHY_IF_CFG                        0x0A4
++#define     DSI_PHY_ULPS_CTRL                     0x0A8
++#define     DSI_PHY_TX_TRIGGERS                   0x0AC
++#define     DSI_PHY_STATUS                        0x0B0
++
++#define     DSI_PHY_TST_CTRL0                     0x0B4
++#define     DSI_PHY_TST_CTRL1                     0x0B8
++#define     DSI_INT_ST0                           0x0BC
++#define     DSI_INT_ST1                           0x0C0
++#define     DSI_INT_MASK0_CFG                     0x0C4
++#define     DSI_INT_MASK1_CFG                     0x0C8
++#define     DSI_PHY_CAL                           0x0CC
++#define     DSI_HEXP_NPKT_CLR                     0x104
++#define     DSI_HEXP_NPKT_SIZE                    0x108
++#define     DSI_VID_SHADOW_CTRL                   0x100
++
++#define     DSI_DPI_VCID_ACT                      0x10C
++#define     DSI_DPI_COLOR_CODING_ACT              0x110
++#define     DSI_DPI_LP_CMD_TIM_ACT                0x118
++#define     DSI_VID_MODE_CFG_ACT                  0x138
++#define     DSI_VID_PKT_SIZE_ACT                  0x13C
++#define     DSI_VID_NUM_CHUNKS_ACT                0x140
++#define     DSI_VID_NULL_SIZE_ACT                 0x144
++#define     DSI_VID_HSA_TIME_ACT                  0x148
++#define     DSI_VID_HBP_TIME_ACT                  0x14C
++#define     DSI_VID_HLINE_TIME_ACT                0x150
++#define     DSI_VID_VSA_LINES_ACT                 0x154
++#define     DSI_VID_VBP_LINES_ACT                 0x158
++#define     DSI_VID_VFP_LINES_ACT                 0x15C
++#define     DSI_VID_VACTIVE_LINES_ACT             0x160
++#define     DSI_SDF_3D_CFG_ACT                    0x190
++
++#define     DSI_INT_FORCE0                        0x0D8
++#define     DSI_INT_FORCE1                        0x0DC
++
++#define     DSI_AUTO_ULPS_MODE                    0x0E0
++#define     DSI_AUTO_ULPS_ENTRY_DELAY             0x0E4
++#define     DSI_AUTO_ULPS_WAKEUP_TIME             0x0E8
++#define     DSI_EDPI_ADV_FEATURES                 0x0EC
++
++#define     DSI_DSC_PARAMETER                     0x0F0
++
++/* And some bitfield definitions */
++
++#define DPHY_PWR_UP_SHUTDOWNZ_LSB 0
++#define DPHY_PWR_UP_SHUTDOWNZ_BITS BIT(DPHY_PWR_UP_SHUTDOWNZ_LSB)
++
++#define DPHY_CTRL0_PHY_TESTCLK_LSB 1
++#define DPHY_CTRL0_PHY_TESTCLK_BITS BIT(DPHY_CTRL0_PHY_TESTCLK_LSB)
++#define DPHY_CTRL0_PHY_TESTCLR_LSB 0
++#define DPHY_CTRL0_PHY_TESTCLR_BITS BIT(DPHY_CTRL0_PHY_TESTCLR_LSB)
++
++#define DPHY_CTRL1_PHY_TESTDIN_LSB  0
++#define DPHY_CTRL1_PHY_TESTDIN_BITS  (0xff << DPHY_CTRL1_PHY_TESTDIN_LSB)
++#define DPHY_CTRL1_PHY_TESTDOUT_LSB 8
++#define DPHY_CTRL1_PHY_TESTDOUT_BITS (0xff << DPHY_CTRL1_PHY_TESTDOUT_LSB)
++#define DPHY_CTRL1_PHY_TESTEN_LSB 16
++#define DPHY_CTRL1_PHY_TESTEN_BITS BIT(DPHY_CTRL1_PHY_TESTEN_LSB)
++
++#define DSI_PHYRSTZ_SHUTDOWNZ_LSB  0
++#define DSI_PHYRSTZ_SHUTDOWNZ_BITS BIT(DSI_PHYRSTZ_SHUTDOWNZ_LSB)
++#define DSI_PHYRSTZ_RSTZ_LSB  1
++#define DSI_PHYRSTZ_RSTZ_BITS BIT(DSI_PHYRSTZ_RSTZ_LSB)
++#define DSI_PHYRSTZ_ENABLECLK_LSB 2
++#define DSI_PHYRSTZ_ENABLECLK_BITS BIT(DSI_PHYRSTZ_ENABLECLK_LSB)
++#define DSI_PHYRSTZ_FORCEPLL_LSB 3
++#define DSI_PHYRSTZ_FORCEPLL_BITS  BIT(DSI_PHYRSTZ_FORCEPLL_LSB)
++
++#define DPHY_HS_RX_CTRL_LANE0_OFFSET  0x44
++#define DPHY_PLL_INPUT_DIV_OFFSET 0x17
++#define DPHY_PLL_LOOP_DIV_OFFSET 0x18
++#define DPHY_PLL_DIV_CTRL_OFFSET 0x19
++
++#define DPHY_PLL_BIAS_OFFSET 0x10
++#define DPHY_PLL_BIAS_VCO_RANGE_LSB 3
++#define DPHY_PLL_BIAS_USE_PROGRAMMED_VCO_RANGE BIT(7)
++
++#define DPHY_PLL_CHARGE_PUMP_OFFSET 0x11
++#define DPHY_PLL_LPF_OFFSET 0x12
++
++#define DSI_WRITE(reg, val)  writel((val),  dsi->hw_base[RP1DSI_HW_BLOCK_DSI] + (reg))
++#define DSI_READ(reg)        readl(dsi->hw_base[RP1DSI_HW_BLOCK_DSI] + (reg))
++
++// ================================================================================
++// Register block : RPI_MIPICFG
++// Version        : 1
++// Bus type       : apb
++// Description    : Register block to control mipi DPHY
++// ================================================================================
++#define RPI_MIPICFG_REGS_RWTYPE_MSB 13
++#define RPI_MIPICFG_REGS_RWTYPE_LSB 12
++// ================================================================================
++// Register    : RPI_MIPICFG_CLK2FC
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_CLK2FC_OFFSET 0x00000000
++#define RPI_MIPICFG_CLK2FC_BITS   0x00000007
++#define RPI_MIPICFG_CLK2FC_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_CLK2FC_SEL
++// Description : select a clock to be sent to the frequency counter
++//               7 = none
++//               6 = none
++//               5 = none
++//               4 = rxbyteclkhs (187.5MHz)
++//               3 = rxclkesc0 (20MHz)
++//               2 = txbyteclkhs (187.5MHz)
++//               1 = txclkesc (125MHz)
++//               0 = none
++#define RPI_MIPICFG_CLK2FC_SEL_RESET  0x0
++#define RPI_MIPICFG_CLK2FC_SEL_BITS   0x00000007
++#define RPI_MIPICFG_CLK2FC_SEL_MSB    2
++#define RPI_MIPICFG_CLK2FC_SEL_LSB    0
++#define RPI_MIPICFG_CLK2FC_SEL_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_CFG
++// JTAG access : asynchronous
++// Description : Top level configuration
++#define RPI_MIPICFG_CFG_OFFSET 0x00000004
++#define RPI_MIPICFG_CFG_BITS   0x00000111
++#define RPI_MIPICFG_CFG_RESET  0x00000001
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_CFG_DPIUPDATE
++// Description : Indicate the DSI block that the next frame will have a new video configuration
++#define RPI_MIPICFG_CFG_DPIUPDATE_RESET  0x0
++#define RPI_MIPICFG_CFG_DPIUPDATE_BITS   0x00000100
++#define RPI_MIPICFG_CFG_DPIUPDATE_MSB    8
++#define RPI_MIPICFG_CFG_DPIUPDATE_LSB    8
++#define RPI_MIPICFG_CFG_DPIUPDATE_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_CFG_SEL_TE_EXT
++// Description : Select the TE source: 1 - ext, 0 - int
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_RESET  0x0
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_BITS   0x00000010
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_MSB    4
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_LSB    4
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_CFG_SEL_CSI_DSI_N
++// Description : Select PHY direction: input to CSI, output from DSI. CSI 1 DSI 0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_RESET  0x1
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_BITS   0x00000001
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_MSB    0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_LSB    0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_TE
++// JTAG access : synchronous
++// Description : Tearing effect processing
++#define RPI_MIPICFG_TE_OFFSET 0x00000008
++#define RPI_MIPICFG_TE_BITS   0x10ffffff
++#define RPI_MIPICFG_TE_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_TE_ARM
++// Description : Tearing effect arm
++#define RPI_MIPICFG_TE_ARM_RESET  0x0
++#define RPI_MIPICFG_TE_ARM_BITS   0x10000000
++#define RPI_MIPICFG_TE_ARM_MSB    28
++#define RPI_MIPICFG_TE_ARM_LSB    28
++#define RPI_MIPICFG_TE_ARM_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_TE_HALT_CYC
++// Description : When arm pulse has been seen, wait for te; then halt the dpi block
++//		 for this many clk_dpi cycles
++#define RPI_MIPICFG_TE_HALT_CYC_RESET  0x000000
++#define RPI_MIPICFG_TE_HALT_CYC_BITS   0x00ffffff
++#define RPI_MIPICFG_TE_HALT_CYC_MSB    23
++#define RPI_MIPICFG_TE_HALT_CYC_LSB    0
++#define RPI_MIPICFG_TE_HALT_CYC_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_MONITOR
++// JTAG access : asynchronous
++// Description : DPHY status monitors for analog DFT
++#define RPI_MIPICFG_DPHY_MONITOR_OFFSET 0x00000010
++#define RPI_MIPICFG_DPHY_MONITOR_BITS   0x00111fff
++#define RPI_MIPICFG_DPHY_MONITOR_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_MONITOR_LOCK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_RESET  0x0
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_BITS   0x00100000
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_MSB    20
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_LSB    20
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_MONITOR_BISTOK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_RESET  0x0
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_BITS   0x00010000
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_MSB    16
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_LSB    16
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_RESET  0x0
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_BITS   0x00001000
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_MSB    12
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_LSB    12
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_RESET  0x0
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_BITS   0x00000f00
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_MSB    11
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_LSB    8
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_MONITOR_TESTDOUT
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_RESET  0x00
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_BITS   0x000000ff
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_MSB    7
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_LSB    0
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_CTRL_0
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_0_OFFSET 0x00000014
++#define RPI_MIPICFG_DPHY_CTRL_0_BITS   0x0000003f
++#define RPI_MIPICFG_DPHY_CTRL_0_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE
++// Description : When set in lpmode, TXCLKESC is driven from clk_vec(driven from clocks block)
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_BITS   0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_MSB    5
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_LSB    5
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA
++// Description : When set, drive the DPHY from the test registers
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_BITS   0x00000010
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_MSB    4
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_LSB    4
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS
++// Description : When test_ena is set, disable cfg_clk
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_BITS   0x00000008
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_MSB    3
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_LSB    3
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS
++// Description : When test_ena is set, disable refclk
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_BITS   0x00000004
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_MSB    2
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_LSB    2
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS
++// Description : When test_ena is set, disable txclkesc
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_BITS   0x00000002
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_MSB    1
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_LSB    1
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS
++// Description : When test_ena is set, disable txbyteclkhs
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_BITS   0x00000001
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_MSB    0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_LSB    0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_CTRL_1
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_1_OFFSET 0x00000018
++#define RPI_MIPICFG_DPHY_CTRL_1_BITS   0x7fffffff
++#define RPI_MIPICFG_DPHY_CTRL_1_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_BITS   0x40000000
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_MSB    30
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_LSB    30
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_BITS   0x20000000
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_MSB    29
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_LSB    29
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_RSTZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_BITS   0x10000000
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_MSB    28
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_LSB    28
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_BITS   0x08000000
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_MSB    27
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_LSB    27
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_BISTON
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_BITS   0x04000000
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_MSB    26
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_LSB    26
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_BITS   0x02000000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_MSB    25
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_LSB    25
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_BITS   0x01000000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_MSB    24
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_LSB    24
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_BITS   0x00800000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_MSB    23
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_LSB    23
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_BITS   0x00400000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_MSB    22
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_LSB    22
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_BITS   0x00200000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_MSB    21
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_LSB    21
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_BITS   0x00100000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_MSB    20
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_LSB    20
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_BITS   0x00080000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_MSB    19
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_LSB    19
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_BITS   0x00040000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_MSB    18
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_LSB    18
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_BITS   0x00020000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_MSB    17
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_LSB    17
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_BITS   0x00010000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_MSB    16
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_LSB    16
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_BITS   0x00008000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_MSB    15
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_LSB    15
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_BITS   0x00004000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_MSB    14
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_LSB    14
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_BITS   0x00002000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_MSB    13
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_LSB    13
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_BITS   0x00001000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_MSB    12
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_LSB    12
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_BITS   0x00000800
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_MSB    11
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_LSB    11
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_BITS   0x00000400
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_MSB    10
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_LSB    10
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_BITS   0x00000200
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_MSB    9
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_LSB    9
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_BITS   0x00000100
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_MSB    8
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_LSB    8
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_BITS   0x00000080
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_MSB    7
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_LSB    7
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_BITS   0x00000040
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_MSB    6
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_LSB    6
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_BITS   0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_MSB    5
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_LSB    5
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_BITS   0x00000010
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_MSB    4
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_LSB    4
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_BITS   0x00000008
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_MSB    3
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_LSB    3
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_BITS   0x00000004
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_MSB    2
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_LSB    2
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_BITS   0x00000002
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_MSB    1
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_LSB    1
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_BITS   0x00000001
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_MSB    0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_LSB    0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_CTRL_2
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_2_OFFSET 0x0000001c
++#define RPI_MIPICFG_DPHY_CTRL_2_BITS   0x000007ff
++#define RPI_MIPICFG_DPHY_CTRL_2_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_2_TESTCLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_BITS   0x00000400
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_MSB    10
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_LSB    10
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_2_TESTEN
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_BITS   0x00000200
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_MSB    9
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_LSB    9
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_2_TESTCLR
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_RESET  0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_BITS   0x00000100
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_MSB    8
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_LSB    8
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_2_TESTDIN
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_BITS   0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_MSB    7
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_LSB    0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_CTRL_3
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_3_OFFSET 0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_3_BITS   0xffffffff
++#define RPI_MIPICFG_DPHY_CTRL_3_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_BITS   0xff000000
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_MSB    31
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_LSB    24
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_BITS   0x00ff0000
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_MSB    23
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_LSB    16
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_BITS   0x0000ff00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_MSB    15
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_LSB    8
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_BITS   0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_MSB    7
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_LSB    0
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_DPHY_CTRL_4
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_4_OFFSET 0x00000024
++#define RPI_MIPICFG_DPHY_CTRL_4_BITS   0xffffffff
++#define RPI_MIPICFG_DPHY_CTRL_4_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_BITS   0xff000000
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_MSB    31
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_LSB    24
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_BITS   0x00ff0000
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_MSB    23
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_LSB    16
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_BITS   0x0000ff00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_MSB    15
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_LSB    8
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_RESET  0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_BITS   0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_MSB    7
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_LSB    0
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define RPI_MIPICFG_INTR_OFFSET 0x00000028
++#define RPI_MIPICFG_INTR_BITS   0x0000000f
++#define RPI_MIPICFG_INTR_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTR_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTR_DSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTR_DSI_HOST_BITS   0x00000008
++#define RPI_MIPICFG_INTR_DSI_HOST_MSB    3
++#define RPI_MIPICFG_INTR_DSI_HOST_LSB    3
++#define RPI_MIPICFG_INTR_DSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTR_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTR_CSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTR_CSI_HOST_BITS   0x00000004
++#define RPI_MIPICFG_INTR_CSI_HOST_MSB    2
++#define RPI_MIPICFG_INTR_CSI_HOST_LSB    2
++#define RPI_MIPICFG_INTR_CSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTR_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTR_DSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTR_DSI_DMA_BITS   0x00000002
++#define RPI_MIPICFG_INTR_DSI_DMA_MSB    1
++#define RPI_MIPICFG_INTR_DSI_DMA_LSB    1
++#define RPI_MIPICFG_INTR_DSI_DMA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTR_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTR_CSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTR_CSI_DMA_BITS   0x00000001
++#define RPI_MIPICFG_INTR_CSI_DMA_MSB    0
++#define RPI_MIPICFG_INTR_CSI_DMA_LSB    0
++#define RPI_MIPICFG_INTR_CSI_DMA_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define RPI_MIPICFG_INTE_OFFSET 0x0000002c
++#define RPI_MIPICFG_INTE_BITS   0x0000000f
++#define RPI_MIPICFG_INTE_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTE_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTE_DSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTE_DSI_HOST_BITS   0x00000008
++#define RPI_MIPICFG_INTE_DSI_HOST_MSB    3
++#define RPI_MIPICFG_INTE_DSI_HOST_LSB    3
++#define RPI_MIPICFG_INTE_DSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTE_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTE_CSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTE_CSI_HOST_BITS   0x00000004
++#define RPI_MIPICFG_INTE_CSI_HOST_MSB    2
++#define RPI_MIPICFG_INTE_CSI_HOST_LSB    2
++#define RPI_MIPICFG_INTE_CSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTE_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTE_DSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTE_DSI_DMA_BITS   0x00000002
++#define RPI_MIPICFG_INTE_DSI_DMA_MSB    1
++#define RPI_MIPICFG_INTE_DSI_DMA_LSB    1
++#define RPI_MIPICFG_INTE_DSI_DMA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTE_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTE_CSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTE_CSI_DMA_BITS   0x00000001
++#define RPI_MIPICFG_INTE_CSI_DMA_MSB    0
++#define RPI_MIPICFG_INTE_CSI_DMA_LSB    0
++#define RPI_MIPICFG_INTE_CSI_DMA_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define RPI_MIPICFG_INTF_OFFSET 0x00000030
++#define RPI_MIPICFG_INTF_BITS   0x0000000f
++#define RPI_MIPICFG_INTF_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTF_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTF_DSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTF_DSI_HOST_BITS   0x00000008
++#define RPI_MIPICFG_INTF_DSI_HOST_MSB    3
++#define RPI_MIPICFG_INTF_DSI_HOST_LSB    3
++#define RPI_MIPICFG_INTF_DSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTF_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTF_CSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTF_CSI_HOST_BITS   0x00000004
++#define RPI_MIPICFG_INTF_CSI_HOST_MSB    2
++#define RPI_MIPICFG_INTF_CSI_HOST_LSB    2
++#define RPI_MIPICFG_INTF_CSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTF_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTF_DSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTF_DSI_DMA_BITS   0x00000002
++#define RPI_MIPICFG_INTF_DSI_DMA_MSB    1
++#define RPI_MIPICFG_INTF_DSI_DMA_LSB    1
++#define RPI_MIPICFG_INTF_DSI_DMA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTF_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTF_CSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTF_CSI_DMA_BITS   0x00000001
++#define RPI_MIPICFG_INTF_CSI_DMA_MSB    0
++#define RPI_MIPICFG_INTF_CSI_DMA_LSB    0
++#define RPI_MIPICFG_INTF_CSI_DMA_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define RPI_MIPICFG_INTS_OFFSET 0x00000034
++#define RPI_MIPICFG_INTS_BITS   0x0000000f
++#define RPI_MIPICFG_INTS_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTS_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTS_DSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTS_DSI_HOST_BITS   0x00000008
++#define RPI_MIPICFG_INTS_DSI_HOST_MSB    3
++#define RPI_MIPICFG_INTS_DSI_HOST_LSB    3
++#define RPI_MIPICFG_INTS_DSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTS_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTS_CSI_HOST_RESET  0x0
++#define RPI_MIPICFG_INTS_CSI_HOST_BITS   0x00000004
++#define RPI_MIPICFG_INTS_CSI_HOST_MSB    2
++#define RPI_MIPICFG_INTS_CSI_HOST_LSB    2
++#define RPI_MIPICFG_INTS_CSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTS_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTS_DSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTS_DSI_DMA_BITS   0x00000002
++#define RPI_MIPICFG_INTS_DSI_DMA_MSB    1
++#define RPI_MIPICFG_INTS_DSI_DMA_LSB    1
++#define RPI_MIPICFG_INTS_DSI_DMA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_INTS_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTS_CSI_DMA_RESET  0x0
++#define RPI_MIPICFG_INTS_CSI_DMA_BITS   0x00000001
++#define RPI_MIPICFG_INTS_CSI_DMA_MSB    0
++#define RPI_MIPICFG_INTS_CSI_DMA_LSB    0
++#define RPI_MIPICFG_INTS_CSI_DMA_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_BLOCK_ID
++// JTAG access : asynchronous
++// Description : Block Identifier
++#define RPI_MIPICFG_BLOCK_ID_OFFSET 0x00000038
++#define RPI_MIPICFG_BLOCK_ID_BITS   0xffffffff
++#define RPI_MIPICFG_BLOCK_ID_RESET  0x4d495049
++#define RPI_MIPICFG_BLOCK_ID_MSB    31
++#define RPI_MIPICFG_BLOCK_ID_LSB    0
++#define RPI_MIPICFG_BLOCK_ID_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_INSTANCE_ID
++// JTAG access : asynchronous
++// Description : Block Instance Identifier
++#define RPI_MIPICFG_INSTANCE_ID_OFFSET 0x0000003c
++#define RPI_MIPICFG_INSTANCE_ID_BITS   0x0000000f
++#define RPI_MIPICFG_INSTANCE_ID_RESET  0x00000000
++#define RPI_MIPICFG_INSTANCE_ID_MSB    3
++#define RPI_MIPICFG_INSTANCE_ID_LSB    0
++#define RPI_MIPICFG_INSTANCE_ID_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_AUTO_OFFSET 0x00000040
++#define RPI_MIPICFG_RSTSEQ_AUTO_BITS   0x00000007
++#define RPI_MIPICFG_RSTSEQ_AUTO_RESET  0x00000007
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_AUTO_CSI
++// Description : 1 = reset is controlled by the sequencer
++//               0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_RESET  0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_BITS   0x00000004
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_MSB    2
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_LSB    2
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++//               0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_RESET  0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_BITS   0x00000002
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_MSB    1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_LSB    1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++//               0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_RESET  0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_BITS   0x00000001
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_MSB    0
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_LSB    0
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_OFFSET 0x00000044
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BITS   0x00000007
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_RESET  0x00000006
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_PARALLEL_CSI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_RESET  0x1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_BITS   0x00000004
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_MSB    2
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_LSB    2
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_RESET  0x1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_BITS   0x00000002
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_MSB    1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_LSB    1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS   0x00000001
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB    0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB    0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_CTRL_OFFSET 0x00000048
++#define RPI_MIPICFG_RSTSEQ_CTRL_BITS   0x00000007
++#define RPI_MIPICFG_RSTSEQ_CTRL_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_CTRL_CSI
++// Description : 1 = keep the reset asserted
++//               0 = keep the reset deasserted
++//               This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_BITS   0x00000004
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_MSB    2
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_LSB    2
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++//               0 = keep the reset deasserted
++//               This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_BITS   0x00000002
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_MSB    1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_LSB    1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++//               0 = keep the reset deasserted
++//               This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_BITS   0x00000001
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_MSB    0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_LSB    0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// ================================================================================
++// Register    : RPI_MIPICFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_TRIG_OFFSET 0x0000004c
++#define RPI_MIPICFG_RSTSEQ_TRIG_BITS   0x00000007
++#define RPI_MIPICFG_RSTSEQ_TRIG_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_TRIG_CSI
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_BITS   0x00000004
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_MSB    2
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_LSB    2
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_ACCESS "SC"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_BITS   0x00000002
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_MSB    1
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_LSB    1
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_BITS   0x00000001
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_MSB    0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_LSB    0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// ================================================================================
++// Register    : RPI_MIPICFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_DONE_OFFSET 0x00000050
++#define RPI_MIPICFG_RSTSEQ_DONE_BITS   0x00000007
++#define RPI_MIPICFG_RSTSEQ_DONE_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_DONE_CSI
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_BITS   0x00000004
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_MSB    2
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_LSB    2
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_BITS   0x00000002
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_MSB    1
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_LSB    1
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_RESET  0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_BITS   0x00000001
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_MSB    0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_LSB    0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// ================================================================================
++// Register    : RPI_MIPICFG_DFTSS
++// JTAG access : asynchronous
++// Description : None
++#define RPI_MIPICFG_DFTSS_OFFSET 0x00000054
++#define RPI_MIPICFG_DFTSS_BITS   0x0000001f
++#define RPI_MIPICFG_DFTSS_RESET  0x00000000
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DFTSS_JTAG_COPY
++// Description : None
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_RESET  0x0
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_BITS   0x00000010
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_MSB    4
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_LSB    4
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY
++// Description : None
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_RESET  0x0
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_BITS   0x00000008
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_MSB    3
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_LSB    3
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_RESET  0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_BITS   0x00000004
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_MSB    2
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_LSB    2
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DFTSS_BYPASS_INSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_RESET  0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_BITS   0x00000002
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_MSB    1
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_LSB    1
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field       : RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_RESET  0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_BITS   0x00000001
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_MSB    0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_LSB    0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_ACCESS "RW"
++
++#define CFG_WRITE(reg, val)  writel((val),  dsi->hw_base[RP1DSI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg)        readl(dsi->hw_base[RP1DSI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++/* ------------------------------- DPHY setup stuff ------------------------ */
++
++static void dphy_transaction(struct rp1_dsi *dsi, uint8_t test_code, uint8_t test_data)
++{
++	/*
++	 * See pg 101 of mipi dphy bidir databook
++	 * Assume we start with testclk high.
++	 * Each APB write takes at least 10ns and we ignore TESTDOUT
++	 * so there is no need for extra delays between the transitions.
++	 */
++	u32 tmp;
++
++	DSI_WRITE(DSI_PHY_TST_CTRL1, test_code | DPHY_CTRL1_PHY_TESTEN_BITS);
++	DSI_WRITE(DSI_PHY_TST_CTRL0, 0);
++	tmp = (DSI_READ(DSI_PHY_TST_CTRL1) >> DPHY_CTRL1_PHY_TESTDOUT_LSB) & 0xFF;
++	DSI_WRITE(DSI_PHY_TST_CTRL1, test_data);
++	DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++}
++
++static uint8_t dphy_get_div(u32 refclk_khz, u32 vco_freq_khz, u32 *ptr_m, u32 *ptr_n)
++{
++	/*
++	 * See pg 77-78 of dphy databook
++	 * fvco = m/n * refclk
++	 * with the limit
++	 * 40MHz >= fREFCLK / N >= 5MHz
++	 * M (multiplier) must be an even number between 2 and 300
++	 * N (input divider) must be an integer between 1 and 100
++	 *
++	 * In practice, given a 50MHz reference clock, it can produce any
++	 * multiple of 10MHz, 11.1111MHz, 12.5MHz, 14.286MHz or 16.667MHz
++	 * with < 1% error for all frequencies above 495MHz.
++	 */
++
++	static const u32 REF_DIVN_MAX = 40000u;
++	static const u32 REF_DIVN_MIN =  5000u;
++	u32 best_n, best_m, best_err = 0x7fffffff;
++	unsigned int n;
++
++	for (n = 1 + refclk_khz / REF_DIVN_MAX; n * REF_DIVN_MIN <= refclk_khz && n < 100; ++n) {
++		u32 half_m = (n * vco_freq_khz + refclk_khz) / (2 * refclk_khz);
++
++		if (half_m < 150) {
++			u32 f = (2 * half_m * refclk_khz) / n;
++			u32 err = (f > vco_freq_khz) ? f - vco_freq_khz : vco_freq_khz - f;
++
++			if (err < best_err) {
++				best_n = n;
++				best_m = 2 * half_m;
++				best_err = err;
++				if (err == 0)
++					break;
++			}
++		}
++	}
++
++	if (64 * best_err < vco_freq_khz) { /* tolerate small error */
++		*ptr_n = best_n;
++		*ptr_m = best_m;
++		return 1;
++	}
++	return 0;
++}
++
++struct hsfreq_range {
++	u16 mhz_max;
++	u8  hsfreqrange;
++	u8  clk_lp2hs;
++	u8  clk_hs2lp;
++	u8  data_lp2hs; /* excluding clk lane entry */
++	u8  data_hs2lp;
++};
++
++/* See Table A-3 on page 258 of dphy databook */
++static const struct hsfreq_range hsfreq_table[] = {
++	{   89, 0b000000, 32, 20, 26, 13 },
++	{   99, 0b010000, 35, 23, 28, 14 },
++	{  109, 0b100000, 32, 22, 26, 13 },
++	{  129, 0b000001, 31, 20, 27, 13 },
++	{  139, 0b010001, 33, 22, 26, 14 },
++	{  149, 0b100001, 33, 21, 26, 14 },
++	{  169, 0b000010, 32, 20, 27, 13 },
++	{  179, 0b010010, 36, 23, 30, 15 },
++	{  199, 0b100010, 40, 22, 33, 15 },
++	{  219, 0b000011, 40, 22, 33, 15 },
++	{  239, 0b010011, 44, 24, 36, 16 },
++	{  249, 0b100011, 48, 24, 38, 17 },
++	{  269, 0b000100, 48, 24, 38, 17 },
++	{  299, 0b010100, 50, 27, 41, 18 },
++	{  329, 0b000101, 56, 28, 45, 18 },
++	{  359, 0b010101, 59, 28, 48, 19 },
++	{  399, 0b100101, 61, 30, 50, 20 },
++	{  449, 0b000110, 67, 31, 55, 21 },
++	{  499, 0b010110, 73, 31, 59, 22 },
++	{  549, 0b000111, 79, 36, 63, 24 },
++	{  599, 0b010111, 83, 37, 68, 25 },
++	{  649, 0b001000, 90, 38, 73, 27 },
++	{  699, 0b011000, 95, 40, 77, 28 },
++	{  749, 0b001001, 102, 40, 84, 28 },
++	{  799, 0b011001, 106, 42, 87, 30 },
++	{  849, 0b101001, 113, 44, 93, 31 },
++	{  899, 0b111001, 118, 47, 98, 32 },
++	{  949, 0b001010, 124, 47, 102, 34 },
++	{  999, 0b011010, 130, 49, 107, 35 },
++	{ 1049, 0b101010, 135, 51, 111, 37 },
++	{ 1099, 0b111010, 139, 51, 114, 38 },
++	{ 1149, 0b001011, 146, 54, 120, 40 },
++	{ 1199, 0b011011, 153, 57, 125, 41 },
++	{ 1249, 0b101011, 158, 58, 130, 42 },
++	{ 1299, 0b111011, 163, 58, 135, 44 },
++	{ 1349, 0b001100, 168, 60, 140, 45 },
++	{ 1399, 0b011100, 172, 64, 144, 47 },
++	{ 1449, 0b101100, 176, 65, 148, 48 },
++	{ 1500, 0b111100, 181, 66, 153, 50 },
++};
++
++static void dphy_set_hsfreqrange(struct rp1_dsi *dsi, u32 freq_mhz)
++{
++	unsigned int i;
++
++	if (freq_mhz < 80 || freq_mhz > 1500)
++		drm_err(dsi->drm, "DPHY: Frequency %u MHz out of range\n",
++			freq_mhz);
++
++	for (i = 0; i < ARRAY_SIZE(hsfreq_table) - 1; i++) {
++		if (freq_mhz <= hsfreq_table[i].mhz_max)
++			break;
++	}
++
++	dsi->hsfreq_index = i;
++	dphy_transaction(dsi, DPHY_HS_RX_CTRL_LANE0_OFFSET,
++			 hsfreq_table[i].hsfreqrange << 1);
++}
++
++static void dphy_configure_pll(struct rp1_dsi *dsi, u32 refclk_khz, u32 vco_freq_khz)
++{
++	u32 m = 0;
++	u32 n = 0;
++
++	if (dphy_get_div(refclk_khz, vco_freq_khz, &m, &n)) {
++		dphy_set_hsfreqrange(dsi, vco_freq_khz / 1000);
++		/* Program m,n from registers */
++		dphy_transaction(dsi, DPHY_PLL_DIV_CTRL_OFFSET, 0x30);
++		/* N (program N-1) */
++		dphy_transaction(dsi, DPHY_PLL_INPUT_DIV_OFFSET, n - 1);
++		/* M[8:5] ?? */
++		dphy_transaction(dsi, DPHY_PLL_LOOP_DIV_OFFSET, 0x80 | ((m - 1) >> 5));
++		/* M[4:0] (program M-1) */
++		dphy_transaction(dsi, DPHY_PLL_LOOP_DIV_OFFSET, ((m - 1) & 0x1F));
++		drm_dbg_driver(dsi->drm,
++			       "DPHY: vco freq want %dkHz got %dkHz = %d * (%dkHz / %d), hsfreqrange = 0x%02x\r\n",
++			       vco_freq_khz, refclk_khz * m / n, m, refclk_khz,
++			       n, hsfreq_table[dsi->hsfreq_index].hsfreqrange);
++	} else {
++		drm_info(dsi->drm,
++			 "rp1dsi: Error configuring DPHY PLL! %dkHz = %d * (%dkHz / %d)\r\n",
++			 vco_freq_khz, m, refclk_khz, n);
++	}
++}
++
++static void dphy_init_khz(struct rp1_dsi *dsi, u32 ref_freq, u32 vco_freq)
++{
++	/* Reset the PHY */
++	DSI_WRITE(DSI_PHYRSTZ, 0);
++	DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++	DSI_WRITE(DSI_PHY_TST_CTRL1, 0);
++	DSI_WRITE(DSI_PHY_TST_CTRL0, (DPHY_CTRL0_PHY_TESTCLK_BITS | DPHY_CTRL0_PHY_TESTCLR_BITS));
++	udelay(1);
++	DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++	udelay(1);
++	/* Since we are in DSI (not CSI2) mode here, start the PLL */
++	dphy_configure_pll(dsi, ref_freq, vco_freq);
++	udelay(1);
++	/* Unreset */
++	DSI_WRITE(DSI_PHYRSTZ, DSI_PHYRSTZ_SHUTDOWNZ_BITS);
++	udelay(1);
++	DSI_WRITE(DSI_PHYRSTZ, (DSI_PHYRSTZ_SHUTDOWNZ_BITS | DSI_PHYRSTZ_RSTZ_BITS));
++	udelay(1); /* so we can see PLL coming up? */
++}
++
++void rp1dsi_mipicfg_setup(struct rp1_dsi *dsi)
++{
++	/* Select DSI rather than CSI-2 */
++	CFG_WRITE(RPI_MIPICFG_CFG, 0);
++	/* Enable DSIDMA interrupt only */
++	CFG_WRITE(RPI_MIPICFG_INTE, RPI_MIPICFG_INTE_DSI_DMA_BITS);
++}
++
++static unsigned long rp1dsi_refclk_freq(struct rp1_dsi *dsi)
++{
++	unsigned long u;
++
++	u = (dsi->clocks[RP1DSI_CLOCK_REF]) ? clk_get_rate(dsi->clocks[RP1DSI_CLOCK_REF]) : 0;
++	if (u < 1 || u >= (1ul << 30))
++		u = 50000000ul; /* default XOSC frequency */
++	return u;
++}
++
++static void rp1dsi_dpiclk_start(struct rp1_dsi *dsi, unsigned int bpp, unsigned int lanes)
++{
++	unsigned long u;
++
++	if (dsi->clocks[RP1DSI_CLOCK_DPI]) {
++		u = (dsi->clocks[RP1DSI_CLOCK_BYTE]) ?
++				clk_get_rate(dsi->clocks[RP1DSI_CLOCK_BYTE]) : 0;
++		drm_info(dsi->drm,
++			 "rp1dsi: Nominal byte clock %lu; scale by %u/%u",
++			 u, 4 * lanes, (bpp >> 1));
++		if (u < 1 || u >= (1ul << 28))
++			u = 72000000ul; /* default DUMMY frequency for byteclock */
++
++		clk_set_parent(dsi->clocks[RP1DSI_CLOCK_DPI], dsi->clocks[RP1DSI_CLOCK_BYTE]);
++		clk_set_rate(dsi->clocks[RP1DSI_CLOCK_DPI], (4 * lanes * u) / (bpp >> 1));
++		clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_DPI]);
++	}
++}
++
++static void rp1dsi_dpiclk_stop(struct rp1_dsi *dsi)
++{
++	if (dsi->clocks[RP1DSI_CLOCK_DPI])
++		clk_disable_unprepare(dsi->clocks[RP1DSI_CLOCK_DPI]);
++}
++
++/* Choose the internal on-the-bus DPI format, and DSI packing flag. */
++static u32 get_colorcode(enum mipi_dsi_pixel_format fmt)
++{
++	switch (fmt) {
++	case MIPI_DSI_FMT_RGB666:
++		return 0x104;
++	case MIPI_DSI_FMT_RGB666_PACKED:
++		return 0x003;
++	case MIPI_DSI_FMT_RGB565:
++		return 0x000;
++	case MIPI_DSI_FMT_RGB888:
++		return 0x005;
++	}
++
++	/* This should be impossible as the format is validated in
++	 * rp1dsi_host_attach
++	 */
++	WARN_ONCE(1, "Invalid colour format configured for DSI");
++	return 0x005;
++}
++
++void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode)
++{
++	u32 timeout, mask, vid_mode_cfg;
++	u32 freq_khz;
++	unsigned int bpp = mipi_dsi_pixel_format_to_bpp(dsi->display_format);
++
++	DSI_WRITE(DSI_PHY_IF_CFG, dsi->lanes - 1);
++	DSI_WRITE(DSI_DPI_CFG_POL, 0);
++	DSI_WRITE(DSI_GEN_VCID, dsi->vc);
++	DSI_WRITE(DSI_DPI_COLOR_CODING, get_colorcode(dsi->display_format));
++	/* a conservative guess (LP escape is slow!) */
++	DSI_WRITE(DSI_DPI_LP_CMD_TIM, 0x00100000);
++
++	/* Drop to LP where possible */
++	vid_mode_cfg = 0xbf00;
++	if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
++		vid_mode_cfg |= 0x01;
++	if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_BURST)
++		vid_mode_cfg |= 0x02;
++	DSI_WRITE(DSI_VID_MODE_CFG, vid_mode_cfg);
++
++	/* Use LP Escape Data signalling for all commands */
++	DSI_WRITE(DSI_CMD_MODE_CFG, 0x10F7F00);
++	/* Select Command Mode */
++	DSI_WRITE(DSI_MODE_CFG, 1);
++	/* XXX magic number */
++	DSI_WRITE(DSI_TO_CNT_CFG, 0x02000200);
++	/* XXX magic number */
++	DSI_WRITE(DSI_BTA_TO_CNT, 0x800);
++
++	DSI_WRITE(DSI_VID_PKT_SIZE, mode->hdisplay);
++	DSI_WRITE(DSI_VID_NUM_CHUNKS, 0);
++	DSI_WRITE(DSI_VID_NULL_SIZE, 0);
++
++	/* Note, unlike Argon firmware, here we DON'T consider sync to be concurrent with porch */
++	DSI_WRITE(DSI_VID_HSA_TIME,
++		  (bpp * (mode->hsync_end - mode->hsync_start)) / (8 * dsi->lanes));
++	DSI_WRITE(DSI_VID_HBP_TIME,
++		  (bpp * (mode->htotal - mode->hsync_end)) / (8 * dsi->lanes));
++	DSI_WRITE(DSI_VID_HLINE_TIME, (bpp * mode->htotal) / (8 * dsi->lanes));
++	DSI_WRITE(DSI_VID_VSA_LINES, (mode->vsync_end - mode->vsync_start));
++	DSI_WRITE(DSI_VID_VBP_LINES, (mode->vtotal - mode->vsync_end));
++	DSI_WRITE(DSI_VID_VFP_LINES, (mode->vsync_start - mode->vdisplay));
++	DSI_WRITE(DSI_VID_VACTIVE_LINES, mode->vdisplay);
++
++	freq_khz = (bpp *  mode->clock) / dsi->lanes;
++
++	dphy_init_khz(dsi, rp1dsi_refclk_freq(dsi) / 1000, freq_khz);
++
++	DSI_WRITE(DSI_PHY_TMR_LPCLK_CFG,
++		  (hsfreq_table[dsi->hsfreq_index].clk_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
++		  (hsfreq_table[dsi->hsfreq_index].clk_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
++	DSI_WRITE(DSI_PHY_TMR_CFG,
++		  (hsfreq_table[dsi->hsfreq_index].data_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
++		  (hsfreq_table[dsi->hsfreq_index].data_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
++
++	DSI_WRITE(DSI_CLKMGR_CFG, 0x00000505);
++
++	/* Wait for PLL lock */
++	for (timeout = (1 << 14); timeout != 0; --timeout) {
++		usleep_range(10, 50);
++		if (DSI_READ(DSI_PHY_STATUS) & (1 << 0))
++			break;
++	}
++	if (timeout == 0)
++		drm_err(dsi->drm, "RP1DSI: Time out waiting for PLL\n");
++
++	DSI_WRITE(DSI_LPCLK_CTRL, 0x1);		/* configure the requesthsclk */
++	DSI_WRITE(DSI_PHY_TST_CTRL0, 0x2);
++	DSI_WRITE(DSI_PCKHDL_CFG, 1 << 2);	/* allow bus turnaround */
++	DSI_WRITE(DSI_PWR_UP, 0x1);		/* power up */
++
++	/* Now it should be safe to start the external DPI clock divider */
++	rp1dsi_dpiclk_start(dsi, bpp, dsi->lanes);
++
++	/* Wait for all lane(s) to be in Stopstate */
++	mask = (1 << 4);
++	if (dsi->lanes >= 2)
++		mask |= (1 << 7);
++	if (dsi->lanes >= 3)
++		mask |= (1 << 9);
++	if (dsi->lanes >= 4)
++		mask |= (1 << 11);
++	for (timeout = (1 << 10); timeout != 0; --timeout) {
++		usleep_range(10, 50);
++		if ((DSI_READ(DSI_PHY_STATUS) & mask) == mask)
++			break;
++	}
++	if (timeout == 0)
++		drm_err(dsi->drm, "RP1DSI: Time out waiting for lanes (%x %x)\n",
++			mask, DSI_READ(DSI_PHY_STATUS));
++}
++
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf)
++{
++	u32 val;
++
++	/* Wait for both FIFOs empty */
++	for (val = 256; val > 0; --val) {
++		if ((DSI_READ(DSI_CMD_PKT_STATUS) & 0xF) == 0x5)
++			break;
++		usleep_range(100, 150);
++	}
++
++	/* Write payload (in 32-bit words) and header */
++	for (; len > 0; len -= 4) {
++		val = *buf++;
++		if (len > 1)
++			val |= (*buf++) << 8;
++		if (len > 2)
++			val |= (*buf++) << 16;
++		if (len > 3)
++			val |= (*buf++) << 24;
++		DSI_WRITE(DSI_GEN_PLD_DATA, val);
++	}
++	DSI_WRITE(DSI_GEN_HDR, hdr);
++
++	/* Wait for both FIFOs empty */
++	for (val = 256; val > 0; --val) {
++		if ((DSI_READ(DSI_CMD_PKT_STATUS) & 0xF) == 0x5)
++			break;
++		usleep_range(100, 150);
++	}
++}
++
++int rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf)
++{
++	int i, j;
++	u32 val;
++
++	/* Wait until not busy and FIFO not empty */
++	for (i = 1024; i > 0; --i) {
++		val = DSI_READ(DSI_CMD_PKT_STATUS);
++		if ((val & ((1 << 6) | (1 << 4))) == 0)
++			break;
++		usleep_range(100, 150);
++	}
++	if (i == 0)
++		return -EIO;
++
++	for (i = 0; i < len; i += 4) {
++		/* Read fifo must not be empty before all bytes are read */
++		if (DSI_READ(DSI_CMD_PKT_STATUS) & (1 << 4))
++			break;
++
++		val = DSI_READ(DSI_GEN_PLD_DATA);
++		for (j = 0; j < 4 && j + i < len; j++)
++			*buf++ = val >> (8 * j);
++	}
++
++	return (i >= len) ? len : (i > 0) ? i : -EIO;
++}
++
++void rp1dsi_dsi_stop(struct rp1_dsi *dsi)
++{
++	DSI_WRITE(DSI_MODE_CFG, 1);	/* Return to Command Mode */
++	DSI_WRITE(DSI_LPCLK_CTRL, 2);	/* Stop the HS clock */
++	DSI_WRITE(DSI_PWR_UP, 0x0);     /* Power down host controller */
++	DSI_WRITE(DSI_PHYRSTZ, 0);      /* PHY into reset. */
++	rp1dsi_dpiclk_stop(dsi);
++}
++
++void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int mode)
++{
++	DSI_WRITE(DSI_MODE_CFG, mode);
++}

+ 1552 - 0
target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch

@@ -0,0 +1,1552 @@
+From 61c3065f89d4447c7e4cf61a466ebc3c4a834ad2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Tue, 19 Sep 2023 17:51:49 +0100
+Subject: [PATCH] drm: Add RP1 DPI driver
+
+Add support for the RP1 DPI hardware.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/gpu/drm/rp1/rp1-dpi/Kconfig       |  12 +
+ drivers/gpu/drm/rp1/rp1-dpi/Makefile      |   5 +
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c     | 429 ++++++++++++++++++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h     |  69 +++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c | 510 ++++++++++++++++++++++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c  | 486 +++++++++++++++++++++
+ 6 files changed, 1511 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
+
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_DPI
++	tristate "DRM Support for RP1 DPI"
++	depends on DRM
++	select MFD_RP1
++	select DRM_GEM_DMA_HELPER
++	select DRM_KMS_HELPER
++	select DRM_VRAM_HELPER
++	select DRM_TTM
++	select DRM_TTM_HELPER
++	help
++	  Choose this option to enable Video Out on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o
++
++obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
+@@ -0,0 +1,429 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/printk.h>
++#include <linux/console.h>
++#include <linux/debugfs.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/cred.h>
++#include <linux/media-bus-format.h>
++#include <linux/pinctrl/consumer.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_mm.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_vblank.h>
++#include <drm/drm_of.h>
++
++#include "rp1_dpi.h"
++
++/*
++ * Default bus format, where not specified by a connector/bridge
++ * and not overridden by the OF property "default_bus_fmt".
++ * This value is for compatibility with vc4 and VGA666-style boards,
++ * even though RP1 hardware cannot achieve the full 18-bit depth
++ * with that pinout (MEDIA_BUS_FMT_RGB666_1X24_CPADHI is preferred).
++ */
++static unsigned int default_bus_fmt = MEDIA_BUS_FMT_RGB666_1X18;
++module_param(default_bus_fmt, uint, 0644);
++
++/* -------------------------------------------------------------- */
++
++static void rp1dpi_pipe_update(struct drm_simple_display_pipe *pipe,
++			       struct drm_plane_state *old_state)
++{
++	struct drm_pending_vblank_event *event;
++	unsigned long flags;
++	struct drm_framebuffer *fb = pipe->plane.state->fb;
++	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++	struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++	struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++	bool can_update = fb && dma_obj && dpi && dpi->pipe_enabled;
++
++	/* (Re-)start DPI-DMA where required; and update FB address */
++	if (can_update) {
++		if (!dpi->dpi_running || fb->format->format != dpi->cur_fmt) {
++			if (dpi->dpi_running &&
++			    fb->format->format != dpi->cur_fmt) {
++				rp1dpi_hw_stop(dpi);
++				dpi->dpi_running = false;
++			}
++			if (!dpi->dpi_running) {
++				rp1dpi_hw_setup(dpi,
++						fb->format->format,
++						dpi->bus_fmt,
++						dpi->de_inv,
++						&pipe->crtc.state->mode);
++				dpi->dpi_running = true;
++			}
++			dpi->cur_fmt = fb->format->format;
++			drm_crtc_vblank_on(&pipe->crtc);
++		}
++		rp1dpi_hw_update(dpi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++	}
++
++	/* Arm VBLANK event (or call it immediately in some error cases) */
++	spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++	event = pipe->crtc.state->event;
++	if (event) {
++		pipe->crtc.state->event = NULL;
++		if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++			drm_crtc_arm_vblank_event(&pipe->crtc, event);
++		else
++			drm_crtc_send_vblank_event(&pipe->crtc, event);
++	}
++	spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static void rp1dpi_pipe_enable(struct drm_simple_display_pipe *pipe,
++			       struct drm_crtc_state *crtc_state,
++			      struct drm_plane_state *plane_state)
++{
++	static const unsigned int M = 1000000;
++	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++	struct drm_connector *conn;
++	struct drm_connector_list_iter conn_iter;
++	unsigned int fpix, fdiv, fvco;
++	int ret;
++
++	/* Look up the connector attached to DPI so we can get the
++	 * bus_format.  Ideally the bridge would tell us the
++	 * bus_format we want, but it doesn't yet, so assume that it's
++	 * uniform throughout the bridge chain.
++	 */
++	dev_info(&dpi->pdev->dev, __func__);
++	drm_connector_list_iter_begin(pipe->encoder.dev, &conn_iter);
++	drm_for_each_connector_iter(conn, &conn_iter) {
++		if (conn->encoder == &pipe->encoder) {
++			dpi->de_inv = !!(conn->display_info.bus_flags &
++							DRM_BUS_FLAG_DE_LOW);
++			dpi->clk_inv = !!(conn->display_info.bus_flags &
++						DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
++			if (conn->display_info.num_bus_formats)
++				dpi->bus_fmt = conn->display_info.bus_formats[0];
++			break;
++		}
++	}
++	drm_connector_list_iter_end(&conn_iter);
++
++	/* Set DPI clock to desired frequency. Currently (experimentally)
++	 * we take control of the VideoPLL, to ensure we can generate it
++	 * accurately. NB: this prevents concurrent use of DPI and VEC!
++	 * Magic numbers ensure the parent clock is within [100MHz, 200MHz]
++	 * with VCO in [1GHz, 1.33GHz]. The initial divide is by 6, 8 or 10.
++	 */
++	fpix = 1000 * pipe->crtc.state->mode.clock;
++	fpix = clamp(fpix, 1 * M, 200 * M);
++	fdiv = fpix;
++	while (fdiv < 100 * M)
++		fdiv *= 2;
++	fvco = fdiv * 2 * DIV_ROUND_UP(500 * M, fdiv);
++	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLCORE], fvco);
++	if (ret)
++		dev_err(&dpi->pdev->dev, "Failed to set PLL VCO to %u (%d)", fvco, ret);
++	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLDIV], fdiv);
++	if (ret)
++		dev_err(&dpi->pdev->dev, "Failed to set PLL output to %u (%d)", fdiv, ret);
++	ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_DPI], fpix);
++	if (ret)
++		dev_err(&dpi->pdev->dev, "Failed to set DPI clock to %u (%d)", fpix, ret);
++
++	rp1dpi_vidout_setup(dpi, dpi->clk_inv);
++	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLCORE]);
++	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLDIV]);
++	pinctrl_pm_select_default_state(&dpi->pdev->dev);
++	clk_prepare_enable(dpi->clocks[RP1DPI_CLK_DPI]);
++	dev_info(&dpi->pdev->dev, "Want %u /%u %u /%u %u; got VCO=%lu DIV=%lu DPI=%lu",
++		 fvco, fvco / fdiv, fdiv, fdiv / fpix, fpix,
++		 clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLCORE]),
++		 clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLDIV]),
++		 clk_get_rate(dpi->clocks[RP1DPI_CLK_DPI]));
++
++	/* Start DPI-DMA. pipe already has the new crtc and plane state. */
++	dpi->pipe_enabled = true;
++	dpi->cur_fmt = 0xdeadbeef;
++	rp1dpi_pipe_update(pipe, 0);
++}
++
++static void rp1dpi_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++	dev_info(&dpi->pdev->dev, __func__);
++	drm_crtc_vblank_off(&pipe->crtc);
++	if (dpi->dpi_running) {
++		rp1dpi_hw_stop(dpi);
++		dpi->dpi_running = false;
++	}
++	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
++	pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
++	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLDIV]);
++	clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLCORE]);
++	dpi->pipe_enabled = false;
++}
++
++static int rp1dpi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++	if (dpi)
++		rp1dpi_hw_vblank_ctrl(dpi, 1);
++
++	return 0;
++}
++
++static void rp1dpi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++	if (dpi)
++		rp1dpi_hw_vblank_ctrl(dpi, 0);
++}
++
++static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = {
++	.enable	    = rp1dpi_pipe_enable,
++	.update	    = rp1dpi_pipe_update,
++	.disable    = rp1dpi_pipe_disable,
++	.prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++	.enable_vblank	= rp1dpi_pipe_enable_vblank,
++	.disable_vblank = rp1dpi_pipe_disable_vblank,
++};
++
++static const struct drm_mode_config_funcs rp1dpi_mode_funcs = {
++	.fb_create = drm_gem_fb_create,
++	.atomic_check = drm_atomic_helper_check,
++	.atomic_commit = drm_atomic_helper_commit,
++};
++
++static void rp1dpi_stopall(struct drm_device *drm)
++{
++	if (drm->dev_private) {
++		struct rp1_dpi *dpi = drm->dev_private;
++
++		if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) {
++			rp1dpi_hw_stop(dpi);
++			clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
++			dpi->dpi_running = false;
++		}
++		rp1dpi_vidout_poweroff(dpi);
++		pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
++	}
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1dpi_fops);
++
++static struct drm_driver rp1dpi_driver = {
++	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++	.fops			= &rp1dpi_fops,
++	.name			= "drm-rp1-dpi",
++	.desc			= "drm-rp1-dpi",
++	.date			= "0",
++	.major			= 1,
++	.minor			= 0,
++	DRM_GEM_DMA_DRIVER_OPS,
++	.release		= rp1dpi_stopall,
++};
++
++static const u32 rp1dpi_formats[] = {
++	DRM_FORMAT_XRGB8888,
++	DRM_FORMAT_XBGR8888,
++	DRM_FORMAT_RGB888,
++	DRM_FORMAT_BGR888,
++	DRM_FORMAT_RGB565
++};
++
++static int rp1dpi_platform_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct drm_device *drm;
++	struct rp1_dpi *dpi;
++	struct drm_bridge *bridge = NULL;
++	struct drm_panel *panel;
++	int i, ret;
++
++	dev_info(dev, __func__);
++	ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0,
++					  &panel, &bridge);
++	if (ret) {
++		dev_info(dev, "%s: bridge not found\n", __func__);
++		return -EPROBE_DEFER;
++	}
++	if (panel) {
++		bridge = devm_drm_panel_bridge_add(dev, panel);
++		if (IS_ERR(bridge))
++			return PTR_ERR(bridge);
++	}
++
++	drm = drm_dev_alloc(&rp1dpi_driver, dev);
++	if (IS_ERR(drm)) {
++		dev_info(dev, "%s %d", __func__, (int)__LINE__);
++		ret = PTR_ERR(drm);
++		return ret;
++	}
++	dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);
++	if (!dpi) {
++		dev_info(dev, "%s %d", __func__, (int)__LINE__);
++		drm_dev_put(drm);
++		return -ENOMEM;
++	}
++
++	init_completion(&dpi->finished);
++	dpi->drm = drm;
++	dpi->pdev = pdev;
++	drm->dev_private = dpi;
++	platform_set_drvdata(pdev, drm);
++
++	dpi->bus_fmt = default_bus_fmt;
++	ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt);
++
++	for (i = 0; i < RP1DPI_NUM_HW_BLOCKS; i++) {
++		dpi->hw_base[i] =
++			devm_ioremap_resource(dev,
++					      platform_get_resource(dpi->pdev, IORESOURCE_MEM, i));
++		if (IS_ERR(dpi->hw_base[i])) {
++			ret = PTR_ERR(dpi->hw_base[i]);
++			dev_err(dev, "Error memory mapping regs[%d]\n", i);
++			goto err_free_drm;
++		}
++	}
++	ret = platform_get_irq(dpi->pdev, 0);
++	if (ret > 0)
++		ret = devm_request_irq(dev, ret, rp1dpi_hw_isr,
++				       IRQF_SHARED, "rp1-dpi", dpi);
++	if (ret) {
++		dev_err(dev, "Unable to request interrupt\n");
++		ret = -EINVAL;
++		goto err_free_drm;
++	}
++	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++	for (i = 0; i < RP1DPI_NUM_CLOCKS; i++) {
++		static const char * const myclocknames[RP1DPI_NUM_CLOCKS] = {
++			"dpiclk", "plldiv", "pllcore"
++		};
++		dpi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
++		if (IS_ERR(dpi->clocks[i])) {
++			ret = PTR_ERR(dpi->clocks[i]);
++			goto err_free_drm;
++		}
++	}
++
++	ret = drmm_mode_config_init(drm);
++	if (ret)
++		goto err_free_drm;
++
++	drm->mode_config.max_width  = 4096;
++	drm->mode_config.max_height = 4096;
++	drm->mode_config.fb_base    = 0;
++	drm->mode_config.preferred_depth = 32;
++	drm->mode_config.prefer_shadow	 = 0;
++	drm->mode_config.prefer_shadow_fbdev = 1;
++	drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++	drm->mode_config.funcs = &rp1dpi_mode_funcs;
++	drm_vblank_init(drm, 1);
++
++	ret = drm_simple_display_pipe_init(drm,
++					   &dpi->pipe,
++					   &rp1dpi_pipe_funcs,
++					   rp1dpi_formats,
++					   ARRAY_SIZE(rp1dpi_formats),
++					   NULL, NULL);
++	if (!ret)
++		ret = drm_simple_display_pipe_attach_bridge(&dpi->pipe, bridge);
++	if (ret)
++		goto err_free_drm;
++
++	drm_mode_config_reset(drm);
++
++	ret = drm_dev_register(drm, 0);
++	if (ret)
++		goto err_free_drm;
++
++	drm_fbdev_generic_setup(drm, 32);
++
++	dev_info(dev, "%s success\n", __func__);
++	return ret;
++
++err_free_drm:
++	dev_err(dev, "%s fail %d\n", __func__, ret);
++	drm_dev_put(drm);
++	return ret;
++}
++
++static int rp1dpi_platform_remove(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++
++	rp1dpi_stopall(drm);
++	drm_dev_unregister(drm);
++	drm_atomic_helper_shutdown(drm);
++	drm_dev_put(drm);
++
++	return 0;
++}
++
++static void rp1dpi_platform_shutdown(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++
++	rp1dpi_stopall(drm);
++}
++
++static const struct of_device_id rp1dpi_of_match[] = {
++	{
++		.compatible = "raspberrypi,rp1dpi",
++	},
++	{ /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1dpi_of_match);
++
++static struct platform_driver rp1dpi_platform_driver = {
++	.probe		= rp1dpi_platform_probe,
++	.remove		= rp1dpi_platform_remove,
++	.shutdown	= rp1dpi_platform_shutdown,
++	.driver		= {
++		.name	= DRIVER_NAME,
++		.owner	= THIS_MODULE,
++		.of_match_table = rp1dpi_of_match,
++	},
++};
++
++module_platform_driver(rp1dpi_platform_driver);
++
++MODULE_AUTHOR("Nick Hollinghurst");
++MODULE_DESCRIPTION("DRM driver for DPI output on Raspberry Pi RP1");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
+@@ -0,0 +1,69 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/types.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <drm/drm_device.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-dpi"
++#define DRIVER_NAME "drm-rp1-dpi"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1DPI_HW_BLOCK_DPI   0
++#define RP1DPI_HW_BLOCK_CFG   1
++#define RP1DPI_NUM_HW_BLOCKS  2
++
++#define RP1DPI_CLK_DPI      0
++#define RP1DPI_CLK_PLLDIV   1
++#define RP1DPI_CLK_PLLCORE  2
++#define RP1DPI_NUM_CLOCKS   3
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_dpi {
++	/* DRM and platform device pointers */
++	struct drm_device *drm;
++	struct platform_device *pdev;
++
++	/* Framework and helper objects */
++	struct drm_simple_display_pipe pipe;
++	struct drm_connector connector;
++
++	/* Clocks: Video PLL, its primary divider, and DPI clock. */
++	struct clk *clocks[RP1DPI_NUM_CLOCKS];
++
++	/* Block (DPI, VOCFG) base addresses, and current state */
++	void __iomem *hw_base[RP1DPI_NUM_HW_BLOCKS];
++	u32 cur_fmt;
++	u32 bus_fmt;
++	bool de_inv, clk_inv;
++	bool dpi_running, pipe_enabled;
++	struct completion finished;
++};
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the DPI/DMA block				  */
++
++void rp1dpi_hw_setup(struct rp1_dpi *dpi,
++		     u32 in_format,
++		     u32 bus_format,
++		     bool de_inv,
++		     struct drm_display_mode const *mode);
++void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride);
++void rp1dpi_hw_stop(struct rp1_dpi *dpi);
++int rp1dpi_hw_busy(struct rp1_dpi *dpi);
++irqreturn_t rp1dpi_hw_isr(int irq, void *dev);
++void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VIDEO OUT CFG block and check RP1 platform	  */
++
++void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge);
++void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi);
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
+@@ -0,0 +1,510 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/rp1_platform.h>
++
++#include "rp1_dpi.h"
++
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_SEL
++// JTAG access : synchronous
++// Description : Selects source: VEC or DPI
++#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
++#define VIDEO_OUT_CFG_SEL_BITS	 0x00000013
++#define VIDEO_OUT_CFG_SEL_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_PCLK_INV
++// Description : Select dpi_pclk output port polarity inversion.
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET  0x0
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS	  0x00000010
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB	  4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB	  4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_PAD_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET	 0x0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS	 0x00000002
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB	 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB	 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_VDAC_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET  0x0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS	  0x00000001
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB	  0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB	  0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_VDAC_CFG
++// JTAG access : synchronous
++// Description : Configure SNPS VDAC
++#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
++#define VIDEO_OUT_CFG_VDAC_CFG_BITS   0x1fffffff
++#define VIDEO_OUT_CFG_VDAC_CFG_RESET  0x0003ffff
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS   0x1c000000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB    28
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB    26
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENSC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS   0x03800000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB	   25
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB	   23
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS   0x00700000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB    22
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB    20
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS   0x00080000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB    19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB    19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS   0x00040000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB    18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB    18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
++// Description : dac2 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS   0x0003f000
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB    17
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB    12
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
++// Description : dac1 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS   0x00000fc0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB    11
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB    6
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
++// Description : dac0 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS   0x0000003f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB    5
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB    0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_VDAC_STATUS
++// JTAG access : synchronous
++// Description : Read VDAC status
++#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
++#define VIDEO_OUT_CFG_VDAC_STATUS_BITS	 0x00000017
++#define VIDEO_OUT_CFG_VDAC_STATUS_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET	0x0
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS	0x00000010
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB	4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB	4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET  "-"
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS	  0x00000007
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB	  2
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB	  0
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_MEM_PD
++// JTAG access : synchronous
++// Description : Control memory power down
++#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
++#define VIDEO_OUT_CFG_MEM_PD_BITS   0x00000003
++#define VIDEO_OUT_CFG_MEM_PD_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_MEM_PD_VEC
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET	0x0
++#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS	0x00000002
++#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB	1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB	1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_MEM_PD_DPI
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET	0x0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS	0x00000001
++#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB	0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB	0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_TEST_OVERRIDE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS   0xffffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET  0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS   0x80000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB    31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB    31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET	0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS	0x40000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB	30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB	30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET  0x00000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS	  0x3fffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB	  29
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB	  0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
++#define VIDEO_OUT_CFG_INTR_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTR_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTR_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTR_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTR_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTR_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTR_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTR_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTR_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTR_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTR_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTR_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
++#define VIDEO_OUT_CFG_INTE_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTE_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTE_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTE_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTE_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTE_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTE_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTE_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTE_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTE_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTE_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTE_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
++#define VIDEO_OUT_CFG_INTF_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTF_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTF_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTF_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTF_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTF_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTF_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTF_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTF_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTF_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTF_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTF_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
++#define VIDEO_OUT_CFG_INTS_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTS_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTS_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTS_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTS_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTS_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTS_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTS_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTS_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTS_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTS_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTS_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_BLOCK_ID
++// JTAG access : synchronous
++// Description : Block Identifier
++//		 Hexadecimal representation of "VOCF"
++#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
++#define VIDEO_OUT_CFG_BLOCK_ID_BITS   0xffffffff
++#define VIDEO_OUT_CFG_BLOCK_ID_RESET  0x564f4346
++#define VIDEO_OUT_CFG_BLOCK_ID_MSB    31
++#define VIDEO_OUT_CFG_BLOCK_ID_LSB    0
++#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INSTANCE_ID
++// JTAG access : synchronous
++// Description : Block Instance Identifier
++#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
++#define VIDEO_OUT_CFG_INSTANCE_ID_BITS	 0x0000000f
++#define VIDEO_OUT_CFG_INSTANCE_ID_RESET	 0x00000000
++#define VIDEO_OUT_CFG_INSTANCE_ID_MSB	 3
++#define VIDEO_OUT_CFG_INSTANCE_ID_LSB	 0
++#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET	 0x00000007
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS   0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET  0x00000006
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET	 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS	 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB	 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB	 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET	 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS	 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB	 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB	 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET	0x0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS	0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB	0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB	0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++
++#define CFG_WRITE(reg, val)  writel((val),  dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg)	     readl(dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge)
++{
++	/*
++	 * We assume DPI and VEC can't be used at the same time (due to
++	 * clashing requirements for PLL_VIDEO, and potentially for VDAC).
++	 * We therefore leave VEC memories powered down.
++	 */
++	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_VEC_BITS);
++	CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE,
++		  VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS);
++
++	/* DPI->Pads; DPI->VDAC; optionally flip PCLK polarity */
++	CFG_WRITE(VIDEO_OUT_CFG_SEL,
++		  drive_negedge ? VIDEO_OUT_CFG_SEL_PCLK_INV_BITS : 0);
++
++	/* configure VDAC for 3 channels, bandgap on, 710mV swing */
++	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++
++	/* enable DPI interrupt */
++	CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_DPI_BITS);
++}
++
++void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi)
++{
++	/* disable DPI interrupt */
++	CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
++
++	/* Ensure VDAC is turned off; power down DPI,VEC memories */
++	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
+@@ -0,0 +1,486 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/media-bus-format.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dpi.h"
++
++// --- DPI DMA REGISTERS ---
++
++// Control
++#define DPI_DMA_CONTROL				      0x0
++#define DPI_DMA_CONTROL_ARM_SHIFT		      0
++#define DPI_DMA_CONTROL_ARM_MASK		      BIT(DPI_DMA_CONTROL_ARM_SHIFT)
++#define DPI_DMA_CONTROL_ALIGN16_SHIFT		      2
++#define DPI_DMA_CONTROL_ALIGN16_MASK		      BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
++#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT	      1
++#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK	      BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
++#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT	      3
++#define DPI_DMA_CONTROL_HIGH_WATER_MASK		      (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
++#define DPI_DMA_CONTROL_DEN_POL_SHIFT		      12
++#define DPI_DMA_CONTROL_DEN_POL_MASK		      BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT		      13
++#define DPI_DMA_CONTROL_HSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT		      14
++#define DPI_DMA_CONTROL_VSYNC_POL_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_COLORM_SHIFT		      15
++#define DPI_DMA_CONTROL_COLORM_MASK		      BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
++#define DPI_DMA_CONTROL_SHUTDN_SHIFT		      16
++#define DPI_DMA_CONTROL_SHUTDN_MASK		      BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
++#define DPI_DMA_CONTROL_HBP_EN_SHIFT		      17
++#define DPI_DMA_CONTROL_HBP_EN_MASK		      BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HFP_EN_SHIFT		      18
++#define DPI_DMA_CONTROL_HFP_EN_MASK		      BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VBP_EN_SHIFT		      19
++#define DPI_DMA_CONTROL_VBP_EN_MASK		      BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VFP_EN_SHIFT		      20
++#define DPI_DMA_CONTROL_VFP_EN_MASK		      BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT		      21
++#define DPI_DMA_CONTROL_HSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT		      22
++#define DPI_DMA_CONTROL_VSYNC_EN_MASK		      BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT	      23
++#define DPI_DMA_CONTROL_FORCE_IMMED_MASK	      BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT	      24
++#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK	      BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT	      25
++#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK	      BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
++
++// IRQ_ENABLES
++#define DPI_DMA_IRQ_EN				      0x04
++#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT		      0
++#define DPI_DMA_IRQ_EN_DMA_READY_MASK		      BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT		      1
++#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK		      BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT	      2
++#define DPI_DMA_IRQ_EN_FRAME_START_MASK		      BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT	      3
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK		      BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_EN_TE_SHIFT			      4
++#define DPI_DMA_IRQ_EN_TE_MASK			      BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
++#define DPI_DMA_IRQ_EN_ERROR_SHIFT		      5
++#define DPI_DMA_IRQ_EN_ERROR_MASK		      BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_SHIFT		      6
++#define DPI_DMA_IRQ_EN_MATCH_MASK		      BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT		      16
++#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK		      (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
++
++// IRQ_FLAGS
++#define DPI_DMA_IRQ_FLAGS			      0x08
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT	      0
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT	      1
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK	      BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT	      2
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK	      BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT	      3
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK	      BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_TE_SHIFT		      4
++#define DPI_DMA_IRQ_FLAGS_TE_MASK		      BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT		      5
++#define DPI_DMA_IRQ_FLAGS_ERROR_MASK		      BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT		      6
++#define DPI_DMA_IRQ_FLAGS_MATCH_MASK		      BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
++
++// QOS
++#define DPI_DMA_QOS				      0xC
++#define DPI_DMA_QOS_DQOS_SHIFT			      0
++#define DPI_DMA_QOS_DQOS_MASK			      (0xF << DPI_DMA_QOS_DQOS_SHIFT)
++#define DPI_DMA_QOS_ULEV_SHIFT			      4
++#define DPI_DMA_QOS_ULEV_MASK			      (0xF << DPI_DMA_QOS_ULEV_SHIFT)
++#define DPI_DMA_QOS_UQOS_SHIFT			      8
++#define DPI_DMA_QOS_UQOS_MASK			      (0xF << DPI_DMA_QOS_UQOS_SHIFT)
++#define DPI_DMA_QOS_LLEV_SHIFT			      12
++#define DPI_DMA_QOS_LLEV_MASK			      (0xF << DPI_DMA_QOS_LLEV_SHIFT)
++#define DPI_DMA_QOS_LQOS_SHIFT			      16
++#define DPI_DMA_QOS_LQOS_MASK			      (0xF << DPI_DMA_QOS_LQOS_SHIFT)
++
++// Panics
++#define DPI_DMA_PANICS				     0x38
++#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT	     0
++#define DPI_DMA_PANICS_UPPER_COUNT_MASK		     \
++				(0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
++#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT	     16
++#define DPI_DMA_PANICS_LOWER_COUNT_MASK		     \
++				(0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
++
++// DMA Address Lower:
++#define DPI_DMA_DMA_ADDR_L			     0x10
++
++// DMA Address Upper:
++#define DPI_DMA_DMA_ADDR_H			     0x40
++
++// DMA stride
++#define DPI_DMA_DMA_STRIDE			     0x14
++
++// Visible Area
++#define DPI_DMA_VISIBLE_AREA			     0x18
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT     0
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
++#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT    16
++#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK     (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
++
++// Sync width
++#define DPI_DMA_SYNC_WIDTH   0x1C
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT	 0
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
++#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT	 16
++#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK	 (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
++
++// Back porch
++#define DPI_DMA_BACK_PORCH   0x20
++#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT	 0
++#define DPI_DMA_BACK_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT	 16
++#define DPI_DMA_BACK_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
++
++// Front porch
++#define DPI_DMA_FRONT_PORCH  0x24
++#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT     0
++#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT     16
++#define DPI_DMA_FRONT_PORCH_COLSM1_MASK	 (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
++
++// Input masks
++#define DPI_DMA_IMASK	 0x2C
++#define DPI_DMA_IMASK_R_SHIFT	 0
++#define DPI_DMA_IMASK_R_MASK	 (0x3FF << DPI_DMA_IMASK_R_SHIFT)
++#define DPI_DMA_IMASK_G_SHIFT	 10
++#define DPI_DMA_IMASK_G_MASK	 (0x3FF << DPI_DMA_IMASK_G_SHIFT)
++#define DPI_DMA_IMASK_B_SHIFT	 20
++#define DPI_DMA_IMASK_B_MASK	 (0x3FF << DPI_DMA_IMASK_B_SHIFT)
++
++// Output Masks
++#define DPI_DMA_OMASK	 0x30
++#define DPI_DMA_OMASK_R_SHIFT	 0
++#define DPI_DMA_OMASK_R_MASK	 (0x3FF << DPI_DMA_OMASK_R_SHIFT)
++#define DPI_DMA_OMASK_G_SHIFT	 10
++#define DPI_DMA_OMASK_G_MASK	 (0x3FF << DPI_DMA_OMASK_G_SHIFT)
++#define DPI_DMA_OMASK_B_SHIFT	 20
++#define DPI_DMA_OMASK_B_MASK	 (0x3FF << DPI_DMA_OMASK_B_SHIFT)
++
++// Shifts
++#define DPI_DMA_SHIFT	 0x28
++#define DPI_DMA_SHIFT_IR_SHIFT	 0
++#define DPI_DMA_SHIFT_IR_MASK	 (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
++#define DPI_DMA_SHIFT_IG_SHIFT	 5
++#define DPI_DMA_SHIFT_IG_MASK	 (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
++#define DPI_DMA_SHIFT_IB_SHIFT	 10
++#define DPI_DMA_SHIFT_IB_MASK	 (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
++#define DPI_DMA_SHIFT_OR_SHIFT	 15
++#define DPI_DMA_SHIFT_OR_MASK	 (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
++#define DPI_DMA_SHIFT_OG_SHIFT	 20
++#define DPI_DMA_SHIFT_OG_MASK	 (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
++#define DPI_DMA_SHIFT_OB_SHIFT	 25
++#define DPI_DMA_SHIFT_OB_MASK	 (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
++
++// Scaling
++#define DPI_DMA_RGBSZ	 0x34
++#define DPI_DMA_RGBSZ_BPP_SHIFT	 16
++#define DPI_DMA_RGBSZ_BPP_MASK	 (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
++#define DPI_DMA_RGBSZ_R_SHIFT	 0
++#define DPI_DMA_RGBSZ_R_MASK	 (0xF << DPI_DMA_RGBSZ_R_SHIFT)
++#define DPI_DMA_RGBSZ_G_SHIFT	 4
++#define DPI_DMA_RGBSZ_G_MASK	 (0xF << DPI_DMA_RGBSZ_G_SHIFT)
++#define DPI_DMA_RGBSZ_B_SHIFT	 8
++#define DPI_DMA_RGBSZ_B_MASK	 (0xF << DPI_DMA_RGBSZ_B_SHIFT)
++
++// Status
++#define DPI_DMA_STATUS  0x3c
++
++#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
++
++static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg)
++{
++	void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
++
++	return readl(addr);
++}
++
++static void rp1dpi_hw_write(struct rp1_dpi *dpi, unsigned int reg, unsigned int val)
++{
++	void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
++
++	writel(val, addr);
++}
++
++int rp1dpi_hw_busy(struct rp1_dpi *dpi)
++{
++	return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1dpi_ipixfmt {
++	u32 format; /* DRM format code                           */
++	u32 mask;   /* RGB masks (10 bits each, left justified)  */
++	u32 shift;  /* RGB MSB positions in the memory word      */
++	u32 rgbsz;  /* Shifts used for scaling; also (BPP/8-1)   */
++};
++
++#define IMASK_RGB(r, g, b)	(BITS(DPI_DMA_IMASK_R, r)  | \
++				 BITS(DPI_DMA_IMASK_G, g)  | \
++				 BITS(DPI_DMA_IMASK_B, b))
++#define OMASK_RGB(r, g, b)	(BITS(DPI_DMA_OMASK_R, r)  | \
++				 BITS(DPI_DMA_OMASK_G, g)  | \
++				 BITS(DPI_DMA_OMASK_B, b))
++#define ISHIFT_RGB(r, g, b)	(BITS(DPI_DMA_SHIFT_IR, r) | \
++				 BITS(DPI_DMA_SHIFT_IG, g) | \
++				 BITS(DPI_DMA_SHIFT_IB, b))
++#define OSHIFT_RGB(r, g, b)	(BITS(DPI_DMA_SHIFT_OR, r) | \
++				 BITS(DPI_DMA_SHIFT_OG, g) | \
++				 BITS(DPI_DMA_SHIFT_OB, b))
++
++static const struct rp1dpi_ipixfmt my_formats[] = {
++	{
++	  .format = DRM_FORMAT_XRGB8888,
++	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++	  .shift  = ISHIFT_RGB(23, 15, 7),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
++	},
++	{
++	  .format = DRM_FORMAT_XBGR8888,
++	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++	  .shift  = ISHIFT_RGB(7, 15, 23),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 3),
++	},
++	{
++	  .format = DRM_FORMAT_RGB888,
++	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++	  .shift  = ISHIFT_RGB(23, 15, 7),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
++	},
++	{
++	  .format = DRM_FORMAT_BGR888,
++	  .mask	  = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++	  .shift  = ISHIFT_RGB(7, 15, 23),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_BPP, 2),
++	},
++	{
++	  .format = DRM_FORMAT_RGB565,
++	  .mask	  = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++	  .shift  = ISHIFT_RGB(15, 10, 4),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++		    BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++	},
++	{
++	  .format = DRM_FORMAT_BGR565,
++	  .mask	  = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++	  .shift  = ISHIFT_RGB(4, 10, 15),
++	  .rgbsz  = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++		    BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++	}
++};
++
++static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz)
++{
++	switch (bus_format) {
++	case MEDIA_BUS_FMT_RGB565_1X16:
++		if (*shift == ISHIFT_RGB(15, 10, 4)) {
++			/* When framebuffer is RGB565, we can output RGB565 */
++			*shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0);
++			*rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
++			return OMASK_RGB(0x3fc, 0x3fc, 0);
++		}
++
++		/* due to a HW limitation, bit-depth is effectively RGB535 */
++		*shift |= OSHIFT_RGB(19, 14, 6);
++		*imask &= IMASK_RGB(0x3e0, 0x380, 0x3e0);
++		*rgbsz = BITS(DPI_DMA_RGBSZ_G, 5) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++		return OMASK_RGB(0x3e0, 0x39c, 0x3e0);
++
++	case MEDIA_BUS_FMT_RGB666_1X18:
++	case MEDIA_BUS_FMT_BGR666_1X18:
++		/* due to a HW limitation, bit-depth is effectively RGB444 */
++		*shift |= OSHIFT_RGB(23, 15, 7);
++		*imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0);
++		*rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++		return OMASK_RGB(0x330, 0x3c0, 0x3c0);
++
++	case MEDIA_BUS_FMT_RGB888_1X24:
++	case MEDIA_BUS_FMT_BGR888_1X24:
++	case MEDIA_BUS_FMT_RGB101010_1X30:
++		/* The full 24 bits can be output. Note that RP1's internal wiring means
++		 * that 8.8.8 to GPIO pads can share with 10.10.10 to the onboard VDAC.
++		 */
++		*shift |= OSHIFT_RGB(29, 19, 9);
++		return OMASK_RGB(0x3fc, 0x3fc, 0x3fc);
++
++	default:
++		/* RGB666_1x24_CPADHI, BGR666_1X24_CPADHI and "RGB565_666" formats */
++		*shift |= OSHIFT_RGB(27, 17, 7);
++		*rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
++		return OMASK_RGB(0x3f0, 0x3f0, 0x3f0);
++	}
++}
++
++#define BUS_FMT_IS_BGR(fmt) (				       \
++		((fmt) == MEDIA_BUS_FMT_BGR666_1X18)        || \
++		((fmt) == MEDIA_BUS_FMT_BGR666_1X24_CPADHI) || \
++		((fmt) == MEDIA_BUS_FMT_BGR888_1X24))
++
++void rp1dpi_hw_setup(struct rp1_dpi *dpi,
++		     u32 in_format, u32 bus_format, bool de_inv,
++		    struct drm_display_mode const *mode)
++{
++	u32 shift, imask, omask, rgbsz;
++	int i;
++
++	pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC",
++		__func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
++		mode->hdisplay, mode->vdisplay,
++		mode->htotal, mode->vtotal,
++		mode->clock,
++		(mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
++		(mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
++		de_inv ? '-' : '+',
++		dpi->clk_inv ? '-' : '+');
++
++	/*
++	 * Configure all DPI/DMA block registers, except base address.
++	 * DMA will not actually start until a FB base address is specified
++	 * using rp1dpi_hw_update().
++	 */
++	rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
++			BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
++			BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
++
++	rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
++			BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
++			BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
++
++	/* In these registers, "back porch" time includes sync width */
++	rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
++			BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
++			BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
++
++	rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
++			BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
++			BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
++
++	/* Input to output pixel format conversion */
++	for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++		if (my_formats[i].format == in_format)
++			break;
++	}
++	if (i >= ARRAY_SIZE(my_formats)) {
++		pr_err("%s: bad input format\n", __func__);
++		i = 4;
++	}
++	if (BUS_FMT_IS_BGR(bus_format))
++		i ^= 1;
++	shift = my_formats[i].shift;
++	imask = my_formats[i].mask;
++	rgbsz = my_formats[i].rgbsz;
++	omask = set_output_format(bus_format, &shift, &imask, &rgbsz);
++
++	rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask);
++	rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask);
++	rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift);
++	rp1dpi_hw_write(dpi, DPI_DMA_RGBSZ, rgbsz);
++
++	rp1dpi_hw_write(dpi, DPI_DMA_QOS,
++			BITS(DPI_DMA_QOS_DQOS, 0x0) |
++			BITS(DPI_DMA_QOS_ULEV, 0xb) |
++			BITS(DPI_DMA_QOS_UQOS, 0x2) |
++			BITS(DPI_DMA_QOS_LLEV, 0x8) |
++			BITS(DPI_DMA_QOS_LQOS, 0x7));
++
++	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1);
++	rp1dpi_hw_vblank_ctrl(dpi, 1);
++
++	i = rp1dpi_hw_busy(dpi);
++	if (i)
++		pr_warn("%s: Unexpectedly busy at start!", __func__);
++
++	rp1dpi_hw_write(dpi, DPI_DMA_CONTROL,
++			BITS(DPI_DMA_CONTROL_ARM,          !i) |
++			BITS(DPI_DMA_CONTROL_AUTO_REPEAT,   1) |
++			BITS(DPI_DMA_CONTROL_HIGH_WATER,  448) |
++			BITS(DPI_DMA_CONTROL_DEN_POL,  de_inv) |
++			BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) |
++			BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
++			BITS(DPI_DMA_CONTROL_COLORM,	   0) |
++			BITS(DPI_DMA_CONTROL_SHUTDN,	   0) |
++			BITS(DPI_DMA_CONTROL_HBP_EN,    (mode->htotal != mode->hsync_end))      |
++			BITS(DPI_DMA_CONTROL_HFP_EN,    (mode->hsync_start != mode->hdisplay))  |
++			BITS(DPI_DMA_CONTROL_VBP_EN,    (mode->vtotal != mode->vsync_end))      |
++			BITS(DPI_DMA_CONTROL_VFP_EN,    (mode->vsync_start != mode->vdisplay))  |
++			BITS(DPI_DMA_CONTROL_HSYNC_EN,  (mode->hsync_end != mode->hsync_start)) |
++			BITS(DPI_DMA_CONTROL_VSYNC_EN,  (mode->vsync_end != mode->vsync_start)));
++}
++
++void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride)
++{
++	u64 a = addr + offset;
++
++	/*
++	 * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(),
++	 * DMA starts immediately; if already running, the buffer will flip at
++	 * the next vertical sync event.
++	 */
++	rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride);
++	rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
++	rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1dpi_hw_stop(struct rp1_dpi *dpi)
++{
++	u32 ctrl;
++
++	/*
++	 * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++	 * the current and any queued frame to end. "Force drain" flags are not used,
++	 * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++	 */
++	reinit_completion(&dpi->finished);
++	ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL);
++	ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
++	rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl);
++	if (!wait_for_completion_timeout(&dpi->finished, HZ / 10))
++		drm_err(dpi->drm, "%s: timed out waiting for idle\n", __func__);
++	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0);
++}
++
++void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable)
++{
++	rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN,
++			BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1)      |
++			BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1)        |
++			BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
++			BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
++}
++
++irqreturn_t rp1dpi_hw_isr(int irq, void *dev)
++{
++	struct rp1_dpi *dpi = dev;
++	u32 u = rp1dpi_hw_read(dpi, DPI_DMA_IRQ_FLAGS);
++
++	if (u) {
++		rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, u);
++		if (dpi) {
++			if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
++				drm_err_ratelimited(dpi->drm,
++						    "Underflow! (panics=0x%08x)\n",
++						    rp1dpi_hw_read(dpi, DPI_DMA_PANICS));
++			if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
++				drm_crtc_handle_vblank(&dpi->pipe.crtc);
++			if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
++				complete(&dpi->finished);
++		}
++	}
++	return u ? IRQ_HANDLED : IRQ_NONE;
++}

+ 3078 - 0
target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch

@@ -0,0 +1,3078 @@
+From 09c2c6aad0fed44182defecd274579770feb0ae2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Tue, 19 Sep 2023 17:54:41 +0100
+Subject: [PATCH] drm: Add RP1 VEC driver
+
+Add support for the RP1 VEC hardware.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/gpu/drm/rp1/rp1-vec/Kconfig       |   12 +
+ drivers/gpu/drm/rp1/rp1-vec/Makefile      |    5 +
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c     |  539 ++++++++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h     |   79 ++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c |  508 ++++++++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c  |  469 +++++++
+ drivers/gpu/drm/rp1/rp1-vec/vec_regs.h    | 1420 +++++++++++++++++++++
+ 7 files changed, 3032 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/vec_regs.h
+
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_VEC
++	tristate "DRM Support for RP1 VEC"
++	depends on DRM
++	select MFD_RP1
++	select DRM_GEM_DMA_HELPER
++	select DRM_KMS_HELPER
++	select DRM_VRAM_HELPER
++	select DRM_TTM
++	select DRM_TTM_HELPER
++	help
++	  Choose this option to enable Video Out on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-vec-y := rp1_vec.o rp1_vec_hw.o rp1_vec_cfg.o
++
++obj-$(CONFIG_DRM_RP1_VEC) += drm-rp1-vec.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -0,0 +1,539 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for VEC output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/printk.h>
++#include <linux/console.h>
++#include <linux/debugfs.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/cred.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_mm.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_vblank.h>
++#include <drm/drm_of.h>
++
++#include "rp1_vec.h"
++
++/*
++ * Default TV standard parameter; it may be overridden by the OF
++ * property "tv_norm" (which should be one of the strings below).
++ *
++ * The default (empty string) supports various 60Hz and 50Hz modes,
++ * and will automatically select NTSC[-M] or PAL[-BDGHIKL]; the two
++ * "fake" 60Hz standards NTSC-443 and PAL60 also support 50Hz PAL.
++ * Other values will restrict the set of video modes offered.
++ *
++ * Finally, the DRM connector property "mode" (which is an integer)
++ * can be used to override this value, but it does not prevent the
++ * selection of an inapplicable video mode.
++ */
++
++static char *rp1vec_tv_norm_str;
++module_param_named(tv_norm, rp1vec_tv_norm_str, charp, 0600);
++MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
++		 "\t\tSupported: NTSC, NTSC-J, NTSC-443, PAL, PAL-M, PAL-N,\n"
++		 "\t\t\tPAL60.\n"
++		 "\t\tDefault: empty string: infer PAL for a 50 Hz mode,\n"
++		 "\t\t\tNTSC otherwise");
++
++const char * const rp1vec_tvstd_names[] = {
++	[RP1VEC_TVSTD_NTSC]     = "NTSC",
++	[RP1VEC_TVSTD_NTSC_J]   = "NTSC-J",
++	[RP1VEC_TVSTD_NTSC_443] = "NTSC-443",
++	[RP1VEC_TVSTD_PAL]      = "PAL",
++	[RP1VEC_TVSTD_PAL_M]    = "PAL-M",
++	[RP1VEC_TVSTD_PAL_N]    = "PAL-N",
++	[RP1VEC_TVSTD_PAL60]    = "PAL60",
++	[RP1VEC_TVSTD_DEFAULT]  = "",
++};
++
++static int rp1vec_parse_tv_norm(const char *str)
++{
++	int i;
++
++	if (str && *str) {
++		for (i = 0; i < ARRAY_SIZE(rp1vec_tvstd_names); ++i) {
++			if (strcasecmp(str, rp1vec_tvstd_names[i]) == 0)
++				return i;
++		}
++	}
++	return RP1VEC_TVSTD_DEFAULT;
++}
++
++static void rp1vec_pipe_update(struct drm_simple_display_pipe *pipe,
++			       struct drm_plane_state *old_state)
++{
++	struct drm_pending_vblank_event *event;
++	unsigned long flags;
++	struct drm_framebuffer *fb = pipe->plane.state->fb;
++	struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++	struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++	struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++	bool can_update = fb && dma_obj && vec && vec->pipe_enabled;
++
++	/* (Re-)start VEC where required; and update FB address */
++	if (can_update) {
++		if (!vec->vec_running || fb->format->format != vec->cur_fmt) {
++			if (vec->vec_running && fb->format->format != vec->cur_fmt) {
++				rp1vec_hw_stop(vec);
++				vec->vec_running = false;
++			}
++			if (!vec->vec_running) {
++				rp1vec_hw_setup(vec,
++						fb->format->format,
++						&pipe->crtc.state->mode,
++						vec->connector.state->tv.mode);
++				vec->vec_running = true;
++			}
++			vec->cur_fmt  = fb->format->format;
++			drm_crtc_vblank_on(&pipe->crtc);
++		}
++		rp1vec_hw_update(vec, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++	}
++
++	/* Check if VBLANK callback needs to be armed (or sent immediately in some error cases).
++	 * Note there is a tiny probability of a race between rp1vec_dma_update() and IRQ;
++	 * ordering it this way around is safe, but theoretically might delay an extra frame.
++	 */
++	spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++	event = pipe->crtc.state->event;
++	if (event) {
++		pipe->crtc.state->event = NULL;
++		if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++			drm_crtc_arm_vblank_event(&pipe->crtc, event);
++		else
++			drm_crtc_send_vblank_event(&pipe->crtc, event);
++	}
++	spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static void rp1vec_pipe_enable(struct drm_simple_display_pipe *pipe,
++			       struct drm_crtc_state *crtc_state,
++			      struct drm_plane_state *plane_state)
++{
++	struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++	dev_info(&vec->pdev->dev, __func__);
++	vec->pipe_enabled = true;
++	vec->cur_fmt = 0xdeadbeef;
++	rp1vec_vidout_setup(vec);
++	rp1vec_pipe_update(pipe, 0);
++}
++
++static void rp1vec_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++	struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++	dev_info(&vec->pdev->dev, __func__);
++	drm_crtc_vblank_off(&pipe->crtc);
++	if (vec) {
++		if (vec->vec_running) {
++			rp1vec_hw_stop(vec);
++			vec->vec_running = false;
++		}
++		vec->pipe_enabled = false;
++	}
++}
++
++static int rp1vec_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	if (pipe && pipe->crtc.dev) {
++		struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++		if (vec)
++			rp1vec_hw_vblank_ctrl(vec, 1);
++	}
++	return 0;
++}
++
++static void rp1vec_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++	if (pipe && pipe->crtc.dev) {
++		struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++		if (vec)
++			rp1vec_hw_vblank_ctrl(vec, 0);
++	}
++}
++
++static const struct drm_simple_display_pipe_funcs rp1vec_pipe_funcs = {
++	.enable	    = rp1vec_pipe_enable,
++	.update	    = rp1vec_pipe_update,
++	.disable    = rp1vec_pipe_disable,
++	.prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++	.enable_vblank	= rp1vec_pipe_enable_vblank,
++	.disable_vblank = rp1vec_pipe_disable_vblank,
++};
++
++static void rp1vec_connector_destroy(struct drm_connector *connector)
++{
++	drm_connector_unregister(connector);
++	drm_connector_cleanup(connector);
++}
++
++static const struct drm_display_mode rp1vec_modes[4] = {
++	{ /* Full size 525/60i with Rec.601 pixel rate */
++		DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
++			 720, 720 + 14, 720 + 14 + 64, 858, 0,
++			 480, 480 + 7, 480 + 7 + 6, 525, 0,
++			 DRM_MODE_FLAG_INTERLACE)
++	},
++	{ /* Cropped and horizontally squashed to be TV-safe */
++		DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
++			 704, 704 + 72, 704 + 72 + 72, 980, 0,
++			 432, 432 + 31, 432 + 31 + 6, 525, 0,
++			 DRM_MODE_FLAG_INTERLACE)
++	},
++	{ /* Full size 625/50i with Rec.601 pixel rate */
++		DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
++			 720, 720 + 20, 720 + 20 + 64, 864, 0,
++			 576, 576 + 4, 576 + 4 + 6, 625, 0,
++			 DRM_MODE_FLAG_INTERLACE)
++	},
++	{ /* Cropped and squashed, for square(ish) pixels */
++		DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
++			 704, 704 + 80, 704 + 80 + 72, 987, 0,
++			 512, 512 + 36, 512 + 36 + 6, 625, 0,
++			 DRM_MODE_FLAG_INTERLACE)
++	}
++};
++
++static int rp1vec_connector_get_modes(struct drm_connector *connector)
++{
++	struct rp1_vec *vec = container_of(connector, struct rp1_vec, connector);
++	bool ok525 = RP1VEC_TVSTD_SUPPORT_525(vec->tv_norm);
++	bool ok625 = RP1VEC_TVSTD_SUPPORT_625(vec->tv_norm);
++	int i, prog, n = 0;
++
++	for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
++		if ((rp1vec_modes[i].vtotal == 625) ? ok625 : ok525) {
++			for (prog = 0; prog < 2; prog++) {
++				struct drm_display_mode *mode =
++					drm_mode_duplicate(connector->dev,
++							   &rp1vec_modes[i]);
++
++				if (prog) {
++					mode->flags &= ~DRM_MODE_FLAG_INTERLACE;
++					mode->vdisplay	  >>= 1;
++					mode->vsync_start >>= 1;
++					mode->vsync_end	  >>= 1;
++					mode->vtotal	  >>= 1;
++				}
++
++				if (mode->hdisplay == 704 &&
++				    mode->vtotal == ((ok525) ? 525 : 625))
++					mode->type |= DRM_MODE_TYPE_PREFERRED;
++
++				drm_mode_set_name(mode);
++				drm_mode_probed_add(connector, mode);
++				n++;
++			}
++		}
++	}
++
++	return n;
++}
++
++static void rp1vec_connector_reset(struct drm_connector *connector)
++{
++	struct rp1_vec *vec = container_of(connector, struct rp1_vec, connector);
++
++	drm_atomic_helper_connector_reset(connector);
++	if (connector->state)
++		connector->state->tv.mode = vec->tv_norm;
++}
++
++static int rp1vec_connector_atomic_check(struct drm_connector *conn,
++					 struct drm_atomic_state *state)
++{	struct drm_connector_state *old_state =
++		drm_atomic_get_old_connector_state(state, conn);
++	struct drm_connector_state *new_state =
++		drm_atomic_get_new_connector_state(state, conn);
++
++	if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) {
++		struct drm_crtc_state *crtc_state =
++			drm_atomic_get_new_crtc_state(state, new_state->crtc);
++
++		crtc_state->mode_changed = true;
++	}
++
++	return 0;
++}
++
++static enum drm_mode_status rp1vec_mode_valid(struct drm_device *dev,
++					      const struct drm_display_mode *mode)
++{
++	/*
++	 * Check the mode roughly matches one of our standard modes
++	 * (optionally half-height and progressive). Ignore H/V sync
++	 * timings which for interlaced TV are approximate at best.
++	 */
++	int i, prog;
++
++	prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
++
++	for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
++		const struct drm_display_mode *ref = rp1vec_modes + i;
++
++		if (mode->hdisplay == ref->hdisplay           &&
++		    mode->vdisplay == (ref->vdisplay >> prog) &&
++		    mode->clock + 2 >= ref->clock             &&
++		    mode->clock <= ref->clock + 2             &&
++		    mode->htotal + 2 >= ref->htotal           &&
++		    mode->htotal <= ref->htotal + 2           &&
++		    mode->vtotal + 2 >= (ref->vtotal >> prog) &&
++		    mode->vtotal <= (ref->vtotal >> prog) + 2)
++			return MODE_OK;
++	}
++	return MODE_BAD;
++}
++
++static const struct drm_connector_helper_funcs rp1vec_connector_helper_funcs = {
++	.get_modes = rp1vec_connector_get_modes,
++	.atomic_check = rp1vec_connector_atomic_check,
++};
++
++static const struct drm_connector_funcs rp1vec_connector_funcs = {
++	.fill_modes = drm_helper_probe_single_connector_modes,
++	.destroy = rp1vec_connector_destroy,
++	.reset = rp1vec_connector_reset,
++	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
++	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
++};
++
++static const struct drm_mode_config_funcs rp1vec_mode_funcs = {
++	.fb_create = drm_gem_fb_create,
++	.atomic_check = drm_atomic_helper_check,
++	.atomic_commit = drm_atomic_helper_commit,
++	.mode_valid = rp1vec_mode_valid,
++};
++
++static const u32 rp1vec_formats[] = {
++	DRM_FORMAT_XRGB8888,
++	DRM_FORMAT_XBGR8888,
++	DRM_FORMAT_RGB888,
++	DRM_FORMAT_BGR888,
++	DRM_FORMAT_RGB565
++};
++
++static void rp1vec_stopall(struct drm_device *drm)
++{
++	if (drm->dev_private) {
++		struct rp1_vec *vec = drm->dev_private;
++
++		if (vec->vec_running || rp1vec_hw_busy(vec)) {
++			rp1vec_hw_stop(vec);
++			vec->vec_running = false;
++		}
++		rp1vec_vidout_poweroff(vec);
++	}
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1vec_fops);
++
++static struct drm_driver rp1vec_driver = {
++	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++	.fops			= &rp1vec_fops,
++	.name			= "drm-rp1-vec",
++	.desc			= "drm-rp1-vec",
++	.date			= "0",
++	.major			= 1,
++	.minor			= 0,
++	DRM_GEM_DMA_DRIVER_OPS,
++	.release		= rp1vec_stopall,
++};
++
++static int rp1vec_platform_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct drm_device *drm;
++	struct rp1_vec *vec;
++	const char *str;
++	int i, ret;
++
++	dev_info(dev, __func__);
++	drm = drm_dev_alloc(&rp1vec_driver, dev);
++	if (IS_ERR(drm)) {
++		ret = PTR_ERR(drm);
++		dev_err(dev, "%s drm_dev_alloc %d", __func__, ret);
++		return ret;
++	}
++
++	vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL);
++	if (!vec) {
++		dev_err(dev, "%s drmm_kzalloc failed", __func__);
++		ret = -ENOMEM;
++		goto err_free_drm;
++	}
++	init_completion(&vec->finished);
++	vec->drm = drm;
++	vec->pdev = pdev;
++	drm->dev_private = vec;
++	platform_set_drvdata(pdev, drm);
++
++	str = rp1vec_tv_norm_str;
++	of_property_read_string(dev->of_node, "tv_norm", &str);
++	vec->tv_norm = rp1vec_parse_tv_norm(str);
++
++	for (i = 0; i < RP1VEC_NUM_HW_BLOCKS; i++) {
++		vec->hw_base[i] =
++			devm_ioremap_resource(dev,
++					      platform_get_resource(vec->pdev, IORESOURCE_MEM, i));
++		if (IS_ERR(vec->hw_base[i])) {
++			ret = PTR_ERR(vec->hw_base[i]);
++			dev_err(dev, "Error memory mapping regs[%d]\n", i);
++			goto err_free_drm;
++		}
++	}
++	ret = platform_get_irq(vec->pdev, 0);
++	if (ret > 0)
++		ret = devm_request_irq(dev, ret, rp1vec_hw_isr,
++				       IRQF_SHARED, "rp1-vec", vec);
++	if (ret) {
++		dev_err(dev, "Unable to request interrupt\n");
++		ret = -EINVAL;
++		goto err_free_drm;
++	}
++	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++	vec->vec_clock = devm_clk_get(dev, NULL);
++	if (IS_ERR(vec->vec_clock)) {
++		ret = PTR_ERR(vec->vec_clock);
++		goto err_free_drm;
++	}
++	ret = clk_prepare_enable(vec->vec_clock);
++
++	ret = drmm_mode_config_init(drm);
++	if (ret)
++		goto err_free_drm;
++	drm->mode_config.max_width  = 768;
++	drm->mode_config.max_height = 576;
++	drm->mode_config.fb_base    = 0;
++	drm->mode_config.preferred_depth = 32;
++	drm->mode_config.prefer_shadow	 = 0;
++	drm->mode_config.prefer_shadow_fbdev = 1;
++	//drm->mode_config.fbdev_use_iomem = false;
++	drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++	drm->mode_config.funcs = &rp1vec_mode_funcs;
++	drm_vblank_init(drm, 1);
++
++	ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(rp1vec_tvstd_names),
++					    rp1vec_tvstd_names);
++	if (ret)
++		goto err_free_drm;
++
++	drm_connector_init(drm, &vec->connector, &rp1vec_connector_funcs,
++			   DRM_MODE_CONNECTOR_Composite);
++	if (ret)
++		goto err_free_drm;
++
++	vec->connector.interlace_allowed = true;
++	drm_connector_helper_add(&vec->connector, &rp1vec_connector_helper_funcs);
++
++	drm_object_attach_property(&vec->connector.base,
++				   drm->mode_config.tv_mode_property,
++				   vec->tv_norm);
++
++	ret = drm_simple_display_pipe_init(drm,
++					   &vec->pipe,
++					   &rp1vec_pipe_funcs,
++					   rp1vec_formats,
++					   ARRAY_SIZE(rp1vec_formats),
++					   NULL,
++					   &vec->connector);
++	if (ret)
++		goto err_free_drm;
++
++	drm_mode_config_reset(drm);
++
++	ret = drm_dev_register(drm, 0);
++	if (ret)
++		goto err_free_drm;
++
++	drm_fbdev_generic_setup(drm, 32); /* the "32" is preferred BPP */
++	return ret;
++
++err_free_drm:
++	dev_info(dev, "%s fail %d", __func__, ret);
++	drm_dev_put(drm);
++	return ret;
++}
++
++static int rp1vec_platform_remove(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++
++	rp1vec_stopall(drm);
++	drm_dev_unregister(drm);
++	drm_atomic_helper_shutdown(drm);
++	drm_dev_put(drm);
++
++	return 0;
++}
++
++static void rp1vec_platform_shutdown(struct platform_device *pdev)
++{
++	struct drm_device *drm = platform_get_drvdata(pdev);
++
++	rp1vec_stopall(drm);
++}
++
++static const struct of_device_id rp1vec_of_match[] = {
++	{
++		.compatible = "raspberrypi,rp1vec",
++	},
++	{ /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1vec_of_match);
++
++static struct platform_driver rp1vec_platform_driver = {
++	.probe		= rp1vec_platform_probe,
++	.remove		= rp1vec_platform_remove,
++	.shutdown	= rp1vec_platform_shutdown,
++	.driver		= {
++		.name	= DRIVER_NAME,
++		.owner	= THIS_MODULE,
++		.of_match_table = rp1vec_of_match,
++	},
++};
++
++module_platform_driver(rp1vec_platform_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("DRM driver for Composite Video on Raspberry Pi RP1");
++MODULE_AUTHOR("Nick Hollinghurst");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
+@@ -0,0 +1,79 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/types.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <drm/drm_device.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-vec"
++#define DRIVER_NAME "drm-rp1-vec"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1VEC_HW_BLOCK_VEC   0
++#define RP1VEC_HW_BLOCK_CFG   1
++#define RP1VEC_NUM_HW_BLOCKS  2
++
++enum {
++	RP1VEC_TVSTD_NTSC = 0,	/* +525 => NTSC       625 => PAL   */
++	RP1VEC_TVSTD_NTSC_J,	/* +525 => NTSC-J     625 => PAL   */
++	RP1VEC_TVSTD_NTSC_443,	/* +525 => NTSC-443  +625 => PAL   */
++	RP1VEC_TVSTD_PAL,	/*  525 => NTSC      +625 => PAL   */
++	RP1VEC_TVSTD_PAL_M,	/* +525 => PAL-M      625 => PAL   */
++	RP1VEC_TVSTD_PAL_N,	/*  525 => NTSC      +625 => PAL-N */
++	RP1VEC_TVSTD_PAL60,	/* +525 => PAL60     +625 => PAL   */
++	RP1VEC_TVSTD_DEFAULT,	/* +525 => NTSC      +625 => PAL   */
++};
++
++/* Which standards support which modes? Those marked with + above */
++#define RP1VEC_TVSTD_SUPPORT_525(n) ((0xD7 >> (n)) & 1)
++#define RP1VEC_TVSTD_SUPPORT_625(n) ((0xEC >> (n)) & 1)
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_vec {
++	/* DRM and platform device pointers */
++	struct drm_device *drm;
++	struct platform_device *pdev;
++
++	/* Framework and helper objects */
++	struct drm_simple_display_pipe pipe;
++	struct drm_connector connector;
++
++	/* Clock. We assume this is always at 108 MHz. */
++	struct clk *vec_clock;
++
++	/* Block (VCC, CFG) base addresses, and current state */
++	void __iomem *hw_base[RP1VEC_NUM_HW_BLOCKS];
++	u32 cur_fmt;
++	int tv_norm;
++	bool vec_running, pipe_enabled;
++	struct completion finished;
++};
++
++extern const char * const rp1vec_tvstd_names[];
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VEC/DMA block				  */
++
++void rp1vec_hw_setup(struct rp1_vec *vec,
++		     u32 in_format,
++		struct drm_display_mode const *mode,
++		int tvstd);
++void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride);
++void rp1vec_hw_stop(struct rp1_vec *vec);
++int rp1vec_hw_busy(struct rp1_vec *vec);
++irqreturn_t rp1vec_hw_isr(int irq, void *dev);
++void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VIDEO OUT CFG block and check RP1 platform	  */
++
++void rp1vec_vidout_setup(struct rp1_vec *vec);
++void rp1vec_vidout_poweroff(struct rp1_vec *vec);
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c
+@@ -0,0 +1,508 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/rp1_platform.h>
++
++#include "rp1_vec.h"
++
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_SEL
++// JTAG access : synchronous
++// Description : Selects source: VEC or DPI
++#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
++#define VIDEO_OUT_CFG_SEL_BITS	 0x00000013
++#define VIDEO_OUT_CFG_SEL_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_PCLK_INV
++// Description : Select dpi_pclk output port polarity inversion.
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET  0x0
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS	  0x00000010
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB	  4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB	  4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_PAD_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET	 0x0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS	 0x00000002
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB	 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB	 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_SEL_VDAC_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET  0x0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS	  0x00000001
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB	  0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB	  0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_VDAC_CFG
++// JTAG access : synchronous
++// Description : Configure SNPS VDAC
++#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
++#define VIDEO_OUT_CFG_VDAC_CFG_BITS   0x1fffffff
++#define VIDEO_OUT_CFG_VDAC_CFG_RESET  0x0003ffff
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS   0x1c000000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB    28
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB    26
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENSC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS   0x03800000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB	   25
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB	   23
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS   0x00700000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB    22
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB    20
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS   0x00080000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB    19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB    19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET  0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS   0x00040000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB    18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB    18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
++// Description : dac2 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS   0x0003f000
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB    17
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB    12
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
++// Description : dac1 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS   0x00000fc0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB    11
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB    6
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
++// Description : dac0 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET  0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS   0x0000003f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB    5
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB    0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_VDAC_STATUS
++// JTAG access : synchronous
++// Description : Read VDAC status
++#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
++#define VIDEO_OUT_CFG_VDAC_STATUS_BITS	 0x00000017
++#define VIDEO_OUT_CFG_VDAC_STATUS_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET	0x0
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS	0x00000010
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB	4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB	4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET  "-"
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS	  0x00000007
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB	  2
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB	  0
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_MEM_PD
++// JTAG access : synchronous
++// Description : Control memory power down
++#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
++#define VIDEO_OUT_CFG_MEM_PD_BITS   0x00000003
++#define VIDEO_OUT_CFG_MEM_PD_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_MEM_PD_VEC
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET	0x0
++#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS	0x00000002
++#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB	1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB	1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_MEM_PD_DPI
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET	0x0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS	0x00000001
++#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB	0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB	0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_TEST_OVERRIDE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS   0xffffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET  0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS   0x80000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB    31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB    31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET	0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS	0x40000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB	30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB	30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET  0x00000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS	  0x3fffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB	  29
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB	  0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
++#define VIDEO_OUT_CFG_INTR_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTR_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTR_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTR_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTR_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTR_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTR_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTR_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTR_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTR_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTR_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTR_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
++#define VIDEO_OUT_CFG_INTE_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTE_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTE_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTE_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTE_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTE_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTE_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTE_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTE_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTE_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTE_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTE_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
++#define VIDEO_OUT_CFG_INTF_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTF_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTF_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTF_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTF_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTF_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTF_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTF_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTF_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTF_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTF_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTF_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
++#define VIDEO_OUT_CFG_INTS_BITS	  0x00000003
++#define VIDEO_OUT_CFG_INTS_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTS_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTS_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_INTS_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_INTS_DPI_MSB    1
++#define VIDEO_OUT_CFG_INTS_DPI_LSB    1
++#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_INTS_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTS_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_INTS_VEC_BITS   0x00000001
++#define VIDEO_OUT_CFG_INTS_VEC_MSB    0
++#define VIDEO_OUT_CFG_INTS_VEC_LSB    0
++#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_BLOCK_ID
++// JTAG access : synchronous
++// Description : Block Identifier
++//		 Hexadecimal representation of "VOCF"
++#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
++#define VIDEO_OUT_CFG_BLOCK_ID_BITS   0xffffffff
++#define VIDEO_OUT_CFG_BLOCK_ID_RESET  0x564f4346
++#define VIDEO_OUT_CFG_BLOCK_ID_MSB    31
++#define VIDEO_OUT_CFG_BLOCK_ID_LSB    0
++#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_INSTANCE_ID
++// JTAG access : synchronous
++// Description : Block Instance Identifier
++#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
++#define VIDEO_OUT_CFG_INSTANCE_ID_BITS	 0x0000000f
++#define VIDEO_OUT_CFG_INSTANCE_ID_RESET	 0x00000000
++#define VIDEO_OUT_CFG_INSTANCE_ID_MSB	 3
++#define VIDEO_OUT_CFG_INSTANCE_ID_LSB	 0
++#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET	 0x00000007
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++//		 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET  0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS   0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET  0x00000006
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET	 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS	 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB	 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB	 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET	 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS	 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB	 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB	 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET	0x0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS	0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB	0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB	0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++//		 0 = keep the reset deasserted
++//		 This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// =============================================================================
++// Register    : VIDEO_OUT_CFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS	 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET	 0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS   0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB    2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS   0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB    1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET  0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS   0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB    0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++
++#define CFG_WRITE(reg, val)  writel((val),  vec->hw_base[RP1VEC_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg)	     readl(vec->hw_base[RP1VEC_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++void rp1vec_vidout_setup(struct rp1_vec *vec)
++{
++	/*
++	 * We assume DPI and VEC can't be used at the same time (due to
++	 * clashing requirements for PLL_VIDEO, and potentially for VDAC).
++	 * We therefore leave DPI memories powered down.
++	 */
++	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_DPI_BITS);
++	CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE, 0x00000000);
++
++	/* DPI->Pads; VEC->VDAC */
++	CFG_WRITE(VIDEO_OUT_CFG_SEL, VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS);
++
++	/* configure VDAC for 1 channel, bandgap on, 1.28V swing */
++	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0x0019ffff);
++
++	/* enable VEC interrupt */
++	CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_VEC_BITS);
++}
++
++void rp1vec_vidout_poweroff(struct rp1_vec *vec)
++{
++	/* disable VEC interrupt */
++	CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
++
++	/* Ensure VDAC is turned off; power down DPI,VEC memories */
++	CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++	CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -0,0 +1,469 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for VEC output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_vec.h"
++#include "vec_regs.h"
++
++#define BITS(field, val) (((val) << (field ## _LSB)) & (field ## _BITS))
++
++#define VEC_WRITE(reg, val) writel((val), vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
++#define VEC_READ(reg)	    readl(vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
++
++int rp1vec_hw_busy(struct rp1_vec *vec)
++{
++	/* Read the undocumented "pline_busy" flag */
++	return VEC_READ(VEC_STATUS) & 1;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1vec_ipixfmt {
++	u32 format; /* DRM format code				 */
++	u32 mask;   /* RGB masks (10 bits each, left justified)	 */
++	u32 shift;  /* RGB MSB positions in the memory word	 */
++	u32 rgbsz;  /* Shifts used for scaling; also (BPP/8-1)	 */
++};
++
++#define MASK_RGB(r, g, b) \
++	(BITS(VEC_IMASK_MASK_R, r) | BITS(VEC_IMASK_MASK_G, g) | BITS(VEC_IMASK_MASK_B, b))
++#define SHIFT_RGB(r, g, b) \
++	(BITS(VEC_SHIFT_SHIFT_R, r) | BITS(VEC_SHIFT_SHIFT_G, g) | BITS(VEC_SHIFT_SHIFT_B, b))
++
++static const struct rp1vec_ipixfmt my_formats[] = {
++	{
++		.format = DRM_FORMAT_XRGB8888,
++		.mask	= MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = SHIFT_RGB(23, 15, 7),
++		.rgbsz  = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++	},
++	{
++		.format = DRM_FORMAT_XBGR8888,
++		.mask	= MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = SHIFT_RGB(7, 15, 23),
++		.rgbsz  = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++	},
++	{
++		.format = DRM_FORMAT_RGB888,
++		.mask	= MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = SHIFT_RGB(23, 15, 7),
++		.rgbsz  = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
++	},
++	{
++		.format = DRM_FORMAT_BGR888,
++		.mask	= MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++		.shift  = SHIFT_RGB(7, 15, 23),
++		.rgbsz  = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
++	},
++	{
++		.format = DRM_FORMAT_RGB565,
++		.mask	= MASK_RGB(0x3e0, 0x3f0, 0x3e0),
++		.shift  = SHIFT_RGB(15, 10, 4),
++		.rgbsz  = BITS(VEC_RGBSZ_SCALE_R, 5) |
++			  BITS(VEC_RGBSZ_SCALE_G, 6) |
++			  BITS(VEC_RGBSZ_SCALE_B, 5) |
++			  BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 1),
++	}
++};
++
++/*
++ * Hardware mode descriptions (@ 108 MHz clock rate).
++ * These rely largely on "canned" register settings.
++ * TODO: Port the generating software from FP to integer,
++ * or better factorize the differences between modes.
++ */
++
++struct rp1vec_hwmode {
++	u16  total_cols;	/* active columns, plus padding for filter context  */
++	u16  rows_per_field;	/* active lines per field (including partial ones)  */
++	bool interlaced;	/* set for interlaced				    */
++	bool first_field_odd;	/* set for interlaced and 30fps			    */
++	u32  yuv_scaling;	/* three 10-bit fields {Y, U, V} in 2.8 format	    */
++	u32  back_end_regs[28]; /* All registers 0x80 .. 0xEC			    */
++};
++
++/* { NTSC, PAL, PAL-M } x { progressive, interlaced } x { 13.5 MHz, 15.428571 MHz } */
++static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
++	{
++		/* NTSC */
++		{
++			{
++				.total_cols = 724,
++				.rows_per_field = 240,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x1071d0cf,
++				.back_end_regs = {
++					0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++					0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170106, 0x00000000, 0x004c020e,
++					0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++					0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ec,
++				},
++			}, {
++				.total_cols = 815,
++				.rows_per_field = 240,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x1c131962,
++				.back_end_regs = {
++					0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++					0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170106, 0x00000000, 0x004c020e,
++					0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++					0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ac,
++				},
++			},
++		}, {
++			{
++				.total_cols = 724,
++				.rows_per_field = 243,
++				.interlaced = true,
++				.first_field_odd = true,
++				.yuv_scaling = 0x1071d0cf,
++				.back_end_regs = {
++					0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++					0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
++					0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
++					0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++					0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dee,
++				},
++			}, {
++				.total_cols = 815,
++				.rows_per_field = 243,
++				.interlaced = true,
++				.first_field_odd = true,
++				.yuv_scaling = 0x1c131962,
++				.back_end_regs = {
++					0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++					0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
++					0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
++					0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++					0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dae,
++				},
++			},
++		},
++	}, {
++		/* PAL */
++		{
++			{
++				.total_cols = 724,
++				.rows_per_field = 288,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x11c1f8e0,
++				.back_end_regs = {
++					0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++					0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++					0x00070135, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170136, 0x00000000, 0x000a0270,
++					0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++					0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
++				},
++			}, {
++				.total_cols = 804,
++				.rows_per_field = 288,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x1e635d7f,
++				.back_end_regs = {
++					0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++					0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++					0x00070135, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170136, 0x00000000, 0x000a0270,
++					0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++					0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
++				},
++			},
++		}, {
++			{
++				.total_cols = 724,
++				.rows_per_field = 288,
++				.interlaced = true,
++				.first_field_odd = false,
++				.yuv_scaling = 0x11c1f8e0,
++				.back_end_regs = {
++					0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++					0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++					0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
++					0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
++					0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++					0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
++				},
++			}, {
++				.total_cols = 804,
++				.rows_per_field = 288,
++				.interlaced = true,
++				.first_field_odd = false,
++				.yuv_scaling = 0x1e635d7f,
++				.back_end_regs = {
++					0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++					0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++					0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
++					0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
++					0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++					0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
++				},
++			},
++		},
++	}, {
++		/* PAL-M */
++		{
++			{
++				.total_cols = 724,
++				.rows_per_field = 240,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x11c1f8e0,
++				.back_end_regs = {
++					0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++					0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170106, 0x00000000, 0x000a020c,
++					0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++					0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
++				},
++			}, {
++				.total_cols = 815,
++				.rows_per_field = 240,
++				.interlaced = false,
++				.first_field_odd = false,
++				.yuv_scaling = 0x1e635d7f,
++				.back_end_regs = {
++					0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++					0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
++					0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++					0x00000000, 0x00170106, 0x00000000, 0x000a020c,
++					0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++					0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
++				},
++			},
++		}, {
++			{
++				.total_cols = 724,
++				.rows_per_field = 243,
++				.interlaced = true,
++				.first_field_odd = true,
++				.yuv_scaling = 0x11c1f8e0,
++				.back_end_regs = {
++					0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++					0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
++					0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
++					0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
++					0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++					0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
++				},
++			}, {
++				.total_cols = 815,
++				.rows_per_field = 243,
++				.interlaced = true,
++				.first_field_odd = true,
++				.yuv_scaling = 0x1e635d7f,
++				.back_end_regs = {
++					0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++					0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
++					0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
++					0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
++					0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++					0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++					0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
++				},
++			},
++		},
++	},
++};
++
++void rp1vec_hw_setup(struct rp1_vec *vec,
++		     u32 in_format,
++		     struct drm_display_mode const *mode,
++		     int tvstd)
++{
++	unsigned int i, mode_family, mode_ilaced, mode_narrow;
++	const struct rp1vec_hwmode *hwm;
++	unsigned int w, h;
++
++	/* Pick the appropriate "base" mode, which we may modify */
++	mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
++	if (mode->vtotal > 263 * (1 + mode_ilaced))
++		mode_family = 1;
++	else
++		mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
++	mode_narrow = (mode->clock >= 14336);
++	hwm = &rp1vec_hwmodes[mode_family][mode_ilaced][mode_narrow];
++	dev_info(&vec->pdev->dev,
++		 "%s: in_fmt=\'%c%c%c%c\' mode=%dx%d%s [%d%d%d] tvstd=%d (%s)",
++		__func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24,
++		mode->hdisplay, mode->vdisplay, (mode_ilaced) ? "i" : "",
++		mode_family, mode_ilaced, mode_narrow,
++		tvstd, rp1vec_tvstd_names[tvstd]);
++
++	w = mode->hdisplay;
++	h = mode->vdisplay;
++	if (mode_ilaced)
++		h >>= 1;
++	if (w > hwm->total_cols)
++		w = hwm->total_cols;
++	if (h > hwm->rows_per_field)
++		w = hwm->rows_per_field;
++
++	/* Configure the hardware */
++	VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
++	VEC_WRITE(VEC_QOS,
++		  BITS(VEC_QOS_DQOS, 0x0) |
++		  BITS(VEC_QOS_ULEV, 0x8) |
++		  BITS(VEC_QOS_UQOS, 0x2) |
++		  BITS(VEC_QOS_LLEV, 0x4) |
++		  BITS(VEC_QOS_LQOS, 0x7));
++	VEC_WRITE(VEC_DMA_AREA,
++		  BITS(VEC_DMA_AREA_COLS_MINUS1, w - 1) |
++		  BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
++	VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
++	VEC_WRITE(VEC_BACK_PORCH,
++		  BITS(VEC_BACK_PORCH_HBP_MINUS1, (hwm->total_cols - w - 1) >> 1) |
++		  BITS(VEC_BACK_PORCH_VBP_MINUS1, (hwm->rows_per_field - h - 1) >> 1));
++	VEC_WRITE(VEC_FRONT_PORCH,
++		  BITS(VEC_FRONT_PORCH_HFP_MINUS1, (hwm->total_cols - w - 2) >> 1) |
++		  BITS(VEC_FRONT_PORCH_VFP_MINUS1, (hwm->rows_per_field - h - 2) >> 1));
++	VEC_WRITE(VEC_MODE,
++		  BITS(VEC_MODE_HIGH_WATER, 0xE0)			  |
++		  BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15))	  |
++		  BITS(VEC_MODE_VFP_EN, (hwm->rows_per_field > h + 1))	  |
++		  BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h))	  |
++		  BITS(VEC_MODE_HFP_EN, (hwm->total_cols > w + 1))          |
++		  BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w))		  |
++		  BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
++		  BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
++	for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {
++		writel(hwm->back_end_regs[i],
++		       vec->hw_base[RP1VEC_HW_BLOCK_VEC] + 0x80 + 4 * i);
++	}
++
++	/* Apply modifications */
++	if (tvstd == RP1VEC_TVSTD_NTSC_J && mode_family == 0) {
++		/* Reduce pedestal (not quite to zero, for FIR overshoot); increase gain */
++		VEC_WRITE(VEC_DAC_BC,
++			  BITS(VEC_DAC_BC_S11_PEDESTAL, 10) |
++			  (hwm->back_end_regs[(0xBC - 0x80) / 4] & ~VEC_DAC_BC_S11_PEDESTAL_BITS));
++		VEC_WRITE(VEC_DAC_C8,
++			  BITS(VEC_DAC_C8_U16_SCALE_LUMA, 0x9400) |
++			  (hwm->back_end_regs[(0xC8 - 0x80) / 4] &
++							~VEC_DAC_C8_U16_SCALE_LUMA_BITS));
++	} else if ((tvstd == RP1VEC_TVSTD_NTSC_443 || tvstd == RP1VEC_TVSTD_PAL60) &&
++		   mode_family != 1) {
++		/* Change colour carrier frequency to 4433618.75 Hz; disable hard sync */
++		VEC_WRITE(VEC_DAC_D4, 0xcc48c1d1);
++		VEC_WRITE(VEC_DAC_D8, 0x0a8262b2);
++		VEC_WRITE(VEC_DAC_EC,
++			  hwm->back_end_regs[(0xEC - 0x80) / 4] & ~VEC_DAC_EC_SEQ_EN_BITS);
++	} else if (tvstd == RP1VEC_TVSTD_PAL_N && mode_family == 1) {
++		/* Change colour carrier frequency to 3582056.25 Hz */
++		VEC_WRITE(VEC_DAC_D4, 0x9ce075f7);
++		VEC_WRITE(VEC_DAC_D8, 0x087da511);
++	}
++
++	/* Input pixel format conversion */
++	for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++		if (my_formats[i].format == in_format)
++			break;
++	}
++	if (i >= ARRAY_SIZE(my_formats)) {
++		dev_err(&vec->pdev->dev, "%s: bad input format\n", __func__);
++		i = 0;
++	}
++	VEC_WRITE(VEC_IMASK, my_formats[i].mask);
++	VEC_WRITE(VEC_SHIFT, my_formats[i].shift);
++	VEC_WRITE(VEC_RGBSZ, my_formats[i].rgbsz);
++
++	VEC_WRITE(VEC_IRQ_FLAGS, 0xffffffff);
++	rp1vec_hw_vblank_ctrl(vec, 1);
++
++	i = rp1vec_hw_busy(vec);
++	if (i)
++		dev_warn(&vec->pdev->dev,
++			 "%s: VEC unexpectedly busy at start (0x%08x)",
++			__func__, VEC_READ(VEC_STATUS));
++
++	VEC_WRITE(VEC_CONTROL,
++		  BITS(VEC_CONTROL_START_ARM, (!i)) |
++		  BITS(VEC_CONTROL_AUTO_REPEAT, 1));
++}
++
++void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride)
++{
++	/*
++	 * Update STRIDE, DMAH and DMAL only. When called after rp1vec_hw_setup(),
++	 * DMA starts immediately; if already running, the buffer will flip at
++	 * the next vertical sync event.
++	 */
++	u64 a = addr + offset;
++
++	VEC_WRITE(VEC_DMA_STRIDE, stride);
++	VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
++	VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1vec_hw_stop(struct rp1_vec *vec)
++{
++	/*
++	 * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++	 * the current and any queued frame to end. "Force drain" flags are not used,
++	 * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++	 */
++
++	reinit_completion(&vec->finished);
++	VEC_WRITE(VEC_CONTROL, 0);
++	if (!wait_for_completion_timeout(&vec->finished, HZ / 10))
++		drm_err(vec->drm, "%s: timed out waiting for idle\n", __func__);
++	VEC_WRITE(VEC_IRQ_ENABLES, 0);
++}
++
++void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable)
++{
++	VEC_WRITE(VEC_IRQ_ENABLES,
++		  BITS(VEC_IRQ_ENABLES_DONE, 1) |
++		  BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
++		  BITS(VEC_IRQ_ENABLES_MATCH_ROW, 1023));
++}
++
++irqreturn_t rp1vec_hw_isr(int irq, void *dev)
++{
++	struct rp1_vec *vec = dev;
++	u32 u = VEC_READ(VEC_IRQ_FLAGS);
++
++	if (u) {
++		VEC_WRITE(VEC_IRQ_FLAGS, u);
++		if (u & VEC_IRQ_FLAGS_DMA_BITS)
++			drm_crtc_handle_vblank(&vec->pipe.crtc);
++		if (u & VEC_IRQ_FLAGS_DONE_BITS)
++			complete(&vec->finished);
++	}
++	return u ? IRQ_HANDLED : IRQ_NONE;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/vec_regs.h
+@@ -0,0 +1,1420 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++// =============================================================================
++// Copyright Raspberry Pi Ltd. 2023
++// vrbuild version: 56aac1a23c016cbbd229108f3b6efc1343842156-clean
++// THIS FILE IS GENERATED BY VRBUILD - DO NOT EDIT
++// =============================================================================
++// Register block : VEC
++// Version        : 1
++// Bus type       : apb
++// Description    : None
++// =============================================================================
++#ifndef VEC_REGS_DEFINED
++#define VEC_REGS_DEFINED
++#define VEC_REGS_RWTYPE_MSB 13
++#define VEC_REGS_RWTYPE_LSB 12
++// =============================================================================
++// Register    : VEC_CONTROL
++// JTAG access : synchronous
++// Description : None
++#define VEC_CONTROL_OFFSET 0x00000000
++#define VEC_CONTROL_BITS   0x00000007
++#define VEC_CONTROL_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_CONTROL_BARS
++// Description : Write '1' to display colour bar test pattern
++#define VEC_CONTROL_BARS_RESET  0x0
++#define VEC_CONTROL_BARS_BITS   0x00000004
++#define VEC_CONTROL_BARS_MSB    2
++#define VEC_CONTROL_BARS_LSB    2
++#define VEC_CONTROL_BARS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_CONTROL_AUTO_REPEAT
++// Description : Write '1' to re-display same frame continuously
++#define VEC_CONTROL_AUTO_REPEAT_RESET  0x0
++#define VEC_CONTROL_AUTO_REPEAT_BITS   0x00000002
++#define VEC_CONTROL_AUTO_REPEAT_MSB    1
++#define VEC_CONTROL_AUTO_REPEAT_LSB    1
++#define VEC_CONTROL_AUTO_REPEAT_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_CONTROL_START_ARM
++// Description : Write '1' before first DMA address is written This bit always
++//               reads back as '0'
++#define VEC_CONTROL_START_ARM_RESET  0x0
++#define VEC_CONTROL_START_ARM_BITS   0x00000001
++#define VEC_CONTROL_START_ARM_MSB    0
++#define VEC_CONTROL_START_ARM_LSB    0
++#define VEC_CONTROL_START_ARM_ACCESS "SC"
++// =============================================================================
++// Register    : VEC_IRQ_ENABLES
++// JTAG access : synchronous
++// Description : None
++#define VEC_IRQ_ENABLES_OFFSET 0x00000004
++#define VEC_IRQ_ENABLES_BITS   0x03ff003f
++#define VEC_IRQ_ENABLES_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_MATCH_ROW
++// Description : Raster line at which MATCH interrupt is signalled
++#define VEC_IRQ_ENABLES_MATCH_ROW_RESET  0x000
++#define VEC_IRQ_ENABLES_MATCH_ROW_BITS   0x03ff0000
++#define VEC_IRQ_ENABLES_MATCH_ROW_MSB    25
++#define VEC_IRQ_ENABLES_MATCH_ROW_LSB    16
++#define VEC_IRQ_ENABLES_MATCH_ROW_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_MATCH
++// Description : Output raster == match_row reached
++#define VEC_IRQ_ENABLES_MATCH_RESET  0x0
++#define VEC_IRQ_ENABLES_MATCH_BITS   0x00000020
++#define VEC_IRQ_ENABLES_MATCH_MSB    5
++#define VEC_IRQ_ENABLES_MATCH_LSB    5
++#define VEC_IRQ_ENABLES_MATCH_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_ERROR
++// Description : DMA address overwritten before it was taken
++#define VEC_IRQ_ENABLES_ERROR_RESET  0x0
++#define VEC_IRQ_ENABLES_ERROR_BITS   0x00000010
++#define VEC_IRQ_ENABLES_ERROR_MSB    4
++#define VEC_IRQ_ENABLES_ERROR_LSB    4
++#define VEC_IRQ_ENABLES_ERROR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_DONE
++// Description : Last word sent to DAC after end of video (= all clear)
++#define VEC_IRQ_ENABLES_DONE_RESET  0x0
++#define VEC_IRQ_ENABLES_DONE_BITS   0x00000008
++#define VEC_IRQ_ENABLES_DONE_MSB    3
++#define VEC_IRQ_ENABLES_DONE_LSB    3
++#define VEC_IRQ_ENABLES_DONE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_FRAME
++// Description : Start of frame
++#define VEC_IRQ_ENABLES_FRAME_RESET  0x0
++#define VEC_IRQ_ENABLES_FRAME_BITS   0x00000004
++#define VEC_IRQ_ENABLES_FRAME_MSB    2
++#define VEC_IRQ_ENABLES_FRAME_LSB    2
++#define VEC_IRQ_ENABLES_FRAME_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_UNDERFLOW
++// Description : Underflow has occurred
++#define VEC_IRQ_ENABLES_UNDERFLOW_RESET  0x0
++#define VEC_IRQ_ENABLES_UNDERFLOW_BITS   0x00000002
++#define VEC_IRQ_ENABLES_UNDERFLOW_MSB    1
++#define VEC_IRQ_ENABLES_UNDERFLOW_LSB    1
++#define VEC_IRQ_ENABLES_UNDERFLOW_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_ENABLES_DMA
++// Description : DMA ready to accept next frame start address
++#define VEC_IRQ_ENABLES_DMA_RESET  0x0
++#define VEC_IRQ_ENABLES_DMA_BITS   0x00000001
++#define VEC_IRQ_ENABLES_DMA_MSB    0
++#define VEC_IRQ_ENABLES_DMA_LSB    0
++#define VEC_IRQ_ENABLES_DMA_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_IRQ_FLAGS
++// JTAG access : synchronous
++// Description : None
++#define VEC_IRQ_FLAGS_OFFSET 0x00000008
++#define VEC_IRQ_FLAGS_BITS   0x0000003f
++#define VEC_IRQ_FLAGS_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_MATCH
++// Description : Output raster == match_row reached
++#define VEC_IRQ_FLAGS_MATCH_RESET  0x0
++#define VEC_IRQ_FLAGS_MATCH_BITS   0x00000020
++#define VEC_IRQ_FLAGS_MATCH_MSB    5
++#define VEC_IRQ_FLAGS_MATCH_LSB    5
++#define VEC_IRQ_FLAGS_MATCH_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_ERROR
++// Description : DMA address overwritten before it was taken
++#define VEC_IRQ_FLAGS_ERROR_RESET  0x0
++#define VEC_IRQ_FLAGS_ERROR_BITS   0x00000010
++#define VEC_IRQ_FLAGS_ERROR_MSB    4
++#define VEC_IRQ_FLAGS_ERROR_LSB    4
++#define VEC_IRQ_FLAGS_ERROR_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_DONE
++// Description : Last word sent to DAC after end of video (= all clear)
++#define VEC_IRQ_FLAGS_DONE_RESET  0x0
++#define VEC_IRQ_FLAGS_DONE_BITS   0x00000008
++#define VEC_IRQ_FLAGS_DONE_MSB    3
++#define VEC_IRQ_FLAGS_DONE_LSB    3
++#define VEC_IRQ_FLAGS_DONE_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_FRAME
++// Description : Start of frame
++#define VEC_IRQ_FLAGS_FRAME_RESET  0x0
++#define VEC_IRQ_FLAGS_FRAME_BITS   0x00000004
++#define VEC_IRQ_FLAGS_FRAME_MSB    2
++#define VEC_IRQ_FLAGS_FRAME_LSB    2
++#define VEC_IRQ_FLAGS_FRAME_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_UNDERFLOW
++// Description : Underflow has occurred
++#define VEC_IRQ_FLAGS_UNDERFLOW_RESET  0x0
++#define VEC_IRQ_FLAGS_UNDERFLOW_BITS   0x00000002
++#define VEC_IRQ_FLAGS_UNDERFLOW_MSB    1
++#define VEC_IRQ_FLAGS_UNDERFLOW_LSB    1
++#define VEC_IRQ_FLAGS_UNDERFLOW_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IRQ_FLAGS_DMA
++// Description : DMA ready to accept next frame start address
++#define VEC_IRQ_FLAGS_DMA_RESET  0x0
++#define VEC_IRQ_FLAGS_DMA_BITS   0x00000001
++#define VEC_IRQ_FLAGS_DMA_MSB    0
++#define VEC_IRQ_FLAGS_DMA_LSB    0
++#define VEC_IRQ_FLAGS_DMA_ACCESS "WC"
++// =============================================================================
++// Register    : VEC_QOS
++// JTAG access : synchronous
++// Description : This register configures panic levels for the AXI ar_qos
++//               quality of service field. Panic status is driven by the number
++//               of rows held in the SRAM cache:
++#define VEC_QOS_OFFSET 0x0000000c
++#define VEC_QOS_BITS   0x000fffff
++#define VEC_QOS_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_QOS_UQOS
++// Description : Upper AXI QOS
++#define VEC_QOS_UQOS_RESET  0x0
++#define VEC_QOS_UQOS_BITS   0x000f0000
++#define VEC_QOS_UQOS_MSB    19
++#define VEC_QOS_UQOS_LSB    16
++#define VEC_QOS_UQOS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_QOS_ULEV
++// Description : Upper trip level (resolution = 1 / 16 of cache size)
++#define VEC_QOS_ULEV_RESET  0x0
++#define VEC_QOS_ULEV_BITS   0x0000f000
++#define VEC_QOS_ULEV_MSB    15
++#define VEC_QOS_ULEV_LSB    12
++#define VEC_QOS_ULEV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_QOS_LQOS
++// Description : Lower AXI QOS
++#define VEC_QOS_LQOS_RESET  0x0
++#define VEC_QOS_LQOS_BITS   0x00000f00
++#define VEC_QOS_LQOS_MSB    11
++#define VEC_QOS_LQOS_LSB    8
++#define VEC_QOS_LQOS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_QOS_LLEV
++// Description : Lower trip level (resolution = 1 / 16 of cache size)
++#define VEC_QOS_LLEV_RESET  0x0
++#define VEC_QOS_LLEV_BITS   0x000000f0
++#define VEC_QOS_LLEV_MSB    7
++#define VEC_QOS_LLEV_LSB    4
++#define VEC_QOS_LLEV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_QOS_DQOS
++// Description : Default QOS
++#define VEC_QOS_DQOS_RESET  0x0
++#define VEC_QOS_DQOS_BITS   0x0000000f
++#define VEC_QOS_DQOS_MSB    3
++#define VEC_QOS_DQOS_LSB    0
++#define VEC_QOS_DQOS_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DMA_ADDR_L
++// JTAG access : synchronous
++// Description : Lower 32-bits
++#define VEC_DMA_ADDR_L_OFFSET 0x00000010
++#define VEC_DMA_ADDR_L_BITS   0xffffffff
++#define VEC_DMA_ADDR_L_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DMA_ADDR_L_AXI_ADDR
++// Description : Byte address of DMA transfer frame buffer.
++#define VEC_DMA_ADDR_L_AXI_ADDR_RESET  0x00000000
++#define VEC_DMA_ADDR_L_AXI_ADDR_BITS   0xffffffff
++#define VEC_DMA_ADDR_L_AXI_ADDR_MSB    31
++#define VEC_DMA_ADDR_L_AXI_ADDR_LSB    0
++#define VEC_DMA_ADDR_L_AXI_ADDR_ACCESS "RWF"
++// =============================================================================
++// Register    : VEC_DMA_STRIDE
++// JTAG access : synchronous
++// Description : This register sets the line byte stride.
++#define VEC_DMA_STRIDE_OFFSET 0x00000014
++#define VEC_DMA_STRIDE_BITS   0xffffffff
++#define VEC_DMA_STRIDE_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DMA_STRIDE_STRIDE
++// Description : Byte stride
++#define VEC_DMA_STRIDE_STRIDE_RESET  0x00000000
++#define VEC_DMA_STRIDE_STRIDE_BITS   0xffffffff
++#define VEC_DMA_STRIDE_STRIDE_MSB    31
++#define VEC_DMA_STRIDE_STRIDE_LSB    0
++#define VEC_DMA_STRIDE_STRIDE_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DMA_AREA
++// JTAG access : synchronous
++// Description : Interlaced pixel area. See example driver code.
++#define VEC_DMA_AREA_OFFSET 0x00000018
++#define VEC_DMA_AREA_BITS   0x03ff03ff
++#define VEC_DMA_AREA_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DMA_AREA_COLS_MINUS1
++// Description : Width
++#define VEC_DMA_AREA_COLS_MINUS1_RESET  0x000
++#define VEC_DMA_AREA_COLS_MINUS1_BITS   0x03ff0000
++#define VEC_DMA_AREA_COLS_MINUS1_MSB    25
++#define VEC_DMA_AREA_COLS_MINUS1_LSB    16
++#define VEC_DMA_AREA_COLS_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1
++// Description : Lines per field = half of lines per interlaced frame
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_RESET  0x000
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_BITS   0x000003ff
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_MSB    9
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_LSB    0
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_YUV_SCALING
++// JTAG access : synchronous
++// Description : None
++#define VEC_YUV_SCALING_OFFSET 0x0000001c
++#define VEC_YUV_SCALING_BITS   0x3fffffff
++#define VEC_YUV_SCALING_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_YUV_SCALING_U10_SCALE_Y
++// Description : Y unsigned scaling factor - 8 binary places
++#define VEC_YUV_SCALING_U10_SCALE_Y_RESET  0x000
++#define VEC_YUV_SCALING_U10_SCALE_Y_BITS   0x3ff00000
++#define VEC_YUV_SCALING_U10_SCALE_Y_MSB    29
++#define VEC_YUV_SCALING_U10_SCALE_Y_LSB    20
++#define VEC_YUV_SCALING_U10_SCALE_Y_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_YUV_SCALING_S10_SCALE_U
++// Description : U signed scaling factor - 8 binary places
++#define VEC_YUV_SCALING_S10_SCALE_U_RESET  0x000
++#define VEC_YUV_SCALING_S10_SCALE_U_BITS   0x000ffc00
++#define VEC_YUV_SCALING_S10_SCALE_U_MSB    19
++#define VEC_YUV_SCALING_S10_SCALE_U_LSB    10
++#define VEC_YUV_SCALING_S10_SCALE_U_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_YUV_SCALING_S10_SCALE_V
++// Description : V signed scaling factor - 8 binary please
++#define VEC_YUV_SCALING_S10_SCALE_V_RESET  0x000
++#define VEC_YUV_SCALING_S10_SCALE_V_BITS   0x000003ff
++#define VEC_YUV_SCALING_S10_SCALE_V_MSB    9
++#define VEC_YUV_SCALING_S10_SCALE_V_LSB    0
++#define VEC_YUV_SCALING_S10_SCALE_V_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_BACK_PORCH
++// JTAG access : synchronous
++// Description : None
++#define VEC_BACK_PORCH_OFFSET 0x00000020
++#define VEC_BACK_PORCH_BITS   0x03ff03ff
++#define VEC_BACK_PORCH_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_BACK_PORCH_HBP_MINUS1
++// Description : Horizontal back porch
++#define VEC_BACK_PORCH_HBP_MINUS1_RESET  0x000
++#define VEC_BACK_PORCH_HBP_MINUS1_BITS   0x03ff0000
++#define VEC_BACK_PORCH_HBP_MINUS1_MSB    25
++#define VEC_BACK_PORCH_HBP_MINUS1_LSB    16
++#define VEC_BACK_PORCH_HBP_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_BACK_PORCH_VBP_MINUS1
++// Description : Vertical back porch
++#define VEC_BACK_PORCH_VBP_MINUS1_RESET  0x000
++#define VEC_BACK_PORCH_VBP_MINUS1_BITS   0x000003ff
++#define VEC_BACK_PORCH_VBP_MINUS1_MSB    9
++#define VEC_BACK_PORCH_VBP_MINUS1_LSB    0
++#define VEC_BACK_PORCH_VBP_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_FRONT_PORCH
++// JTAG access : synchronous
++// Description : None
++#define VEC_FRONT_PORCH_OFFSET 0x00000024
++#define VEC_FRONT_PORCH_BITS   0x03ff03ff
++#define VEC_FRONT_PORCH_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_FRONT_PORCH_HFP_MINUS1
++// Description : Horizontal front porch
++#define VEC_FRONT_PORCH_HFP_MINUS1_RESET  0x000
++#define VEC_FRONT_PORCH_HFP_MINUS1_BITS   0x03ff0000
++#define VEC_FRONT_PORCH_HFP_MINUS1_MSB    25
++#define VEC_FRONT_PORCH_HFP_MINUS1_LSB    16
++#define VEC_FRONT_PORCH_HFP_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_FRONT_PORCH_VFP_MINUS1
++// Description : Vertical front porch
++#define VEC_FRONT_PORCH_VFP_MINUS1_RESET  0x000
++#define VEC_FRONT_PORCH_VFP_MINUS1_BITS   0x000003ff
++#define VEC_FRONT_PORCH_VFP_MINUS1_MSB    9
++#define VEC_FRONT_PORCH_VFP_MINUS1_LSB    0
++#define VEC_FRONT_PORCH_VFP_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_SHIFT
++// JTAG access : synchronous
++// Description : Positions of R,G,B MS bits in the memory word. Note: due to an
++//               unintended red/blue swap, these fields have been renamed since
++//               a previous version. There is no functional change.
++#define VEC_SHIFT_OFFSET 0x00000028
++#define VEC_SHIFT_BITS   0x00007fff
++#define VEC_SHIFT_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_SHIFT_SHIFT_R
++// Description : Red MSB
++#define VEC_SHIFT_SHIFT_R_RESET  0x00
++#define VEC_SHIFT_SHIFT_R_BITS   0x00007c00
++#define VEC_SHIFT_SHIFT_R_MSB    14
++#define VEC_SHIFT_SHIFT_R_LSB    10
++#define VEC_SHIFT_SHIFT_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_SHIFT_SHIFT_G
++// Description : Green MSB
++#define VEC_SHIFT_SHIFT_G_RESET  0x00
++#define VEC_SHIFT_SHIFT_G_BITS   0x000003e0
++#define VEC_SHIFT_SHIFT_G_MSB    9
++#define VEC_SHIFT_SHIFT_G_LSB    5
++#define VEC_SHIFT_SHIFT_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_SHIFT_SHIFT_B
++// Description : Blue MSB
++#define VEC_SHIFT_SHIFT_B_RESET  0x00
++#define VEC_SHIFT_SHIFT_B_BITS   0x0000001f
++#define VEC_SHIFT_SHIFT_B_MSB    4
++#define VEC_SHIFT_SHIFT_B_LSB    0
++#define VEC_SHIFT_SHIFT_B_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_IMASK
++// JTAG access : synchronous
++// Description : Masks for R,G,B significant bits, left-justified within 10-bit
++//               fields.
++#define VEC_IMASK_OFFSET 0x0000002c
++#define VEC_IMASK_BITS   0x3fffffff
++#define VEC_IMASK_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_IMASK_MASK_R
++// Description : Red mask
++#define VEC_IMASK_MASK_R_RESET  0x000
++#define VEC_IMASK_MASK_R_BITS   0x3ff00000
++#define VEC_IMASK_MASK_R_MSB    29
++#define VEC_IMASK_MASK_R_LSB    20
++#define VEC_IMASK_MASK_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IMASK_MASK_G
++// Description : Green mask
++#define VEC_IMASK_MASK_G_RESET  0x000
++#define VEC_IMASK_MASK_G_BITS   0x000ffc00
++#define VEC_IMASK_MASK_G_MSB    19
++#define VEC_IMASK_MASK_G_LSB    10
++#define VEC_IMASK_MASK_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_IMASK_MASK_B
++// Description : Blue mask
++#define VEC_IMASK_MASK_B_RESET  0x000
++#define VEC_IMASK_MASK_B_BITS   0x000003ff
++#define VEC_IMASK_MASK_B_MSB    9
++#define VEC_IMASK_MASK_B_LSB    0
++#define VEC_IMASK_MASK_B_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_MODE
++// JTAG access : synchronous
++// Description : None
++#define VEC_MODE_OFFSET 0x00000030
++#define VEC_MODE_BITS   0x01ff003f
++#define VEC_MODE_RESET  0x01c00000
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_HIGH_WATER
++// Description : ALWAYS WRITE 8'hE0
++#define VEC_MODE_HIGH_WATER_RESET  0xe0
++#define VEC_MODE_HIGH_WATER_BITS   0x01fe0000
++#define VEC_MODE_HIGH_WATER_MSB    24
++#define VEC_MODE_HIGH_WATER_LSB    17
++#define VEC_MODE_HIGH_WATER_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_ALIGN16
++// Description : Data: 0=BYTE aligned; 1=BEAT aligned
++#define VEC_MODE_ALIGN16_RESET  0x0
++#define VEC_MODE_ALIGN16_BITS   0x00010000
++#define VEC_MODE_ALIGN16_MSB    16
++#define VEC_MODE_ALIGN16_LSB    16
++#define VEC_MODE_ALIGN16_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_VFP_EN
++// Description : Enable vertical front porch
++#define VEC_MODE_VFP_EN_RESET  0x0
++#define VEC_MODE_VFP_EN_BITS   0x00000020
++#define VEC_MODE_VFP_EN_MSB    5
++#define VEC_MODE_VFP_EN_LSB    5
++#define VEC_MODE_VFP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_VBP_EN
++// Description : Enable vertical back porch
++#define VEC_MODE_VBP_EN_RESET  0x0
++#define VEC_MODE_VBP_EN_BITS   0x00000010
++#define VEC_MODE_VBP_EN_MSB    4
++#define VEC_MODE_VBP_EN_LSB    4
++#define VEC_MODE_VBP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_HFP_EN
++// Description : Enable horizontal front porch
++#define VEC_MODE_HFP_EN_RESET  0x0
++#define VEC_MODE_HFP_EN_BITS   0x00000008
++#define VEC_MODE_HFP_EN_MSB    3
++#define VEC_MODE_HFP_EN_LSB    3
++#define VEC_MODE_HFP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_HBP_EN
++// Description : Enable horizontal back porch
++#define VEC_MODE_HBP_EN_RESET  0x0
++#define VEC_MODE_HBP_EN_BITS   0x00000004
++#define VEC_MODE_HBP_EN_MSB    2
++#define VEC_MODE_HBP_EN_LSB    2
++#define VEC_MODE_HBP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_FIELDS_PER_FRAME_MINUS1
++// Description : Interlaced / progressive
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_RESET  0x0
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_BITS   0x00000002
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_MSB    1
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_LSB    1
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_MODE_FIRST_FIELD_ODD
++// Description : Interlacing order: odd/even or even/odd
++#define VEC_MODE_FIRST_FIELD_ODD_RESET  0x0
++#define VEC_MODE_FIRST_FIELD_ODD_BITS   0x00000001
++#define VEC_MODE_FIRST_FIELD_ODD_MSB    0
++#define VEC_MODE_FIRST_FIELD_ODD_LSB    0
++#define VEC_MODE_FIRST_FIELD_ODD_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_RGBSZ
++// JTAG access : synchronous
++// Description : None
++#define VEC_RGBSZ_OFFSET 0x00000034
++#define VEC_RGBSZ_BITS   0x00030fff
++#define VEC_RGBSZ_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1
++// Description : Pixel stride
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_RESET  0x0
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_BITS   0x00030000
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_MSB    17
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_LSB    16
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_RGBSZ_SCALE_R
++// Description : Red number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_R_RESET  0x0
++#define VEC_RGBSZ_SCALE_R_BITS   0x00000f00
++#define VEC_RGBSZ_SCALE_R_MSB    11
++#define VEC_RGBSZ_SCALE_R_LSB    8
++#define VEC_RGBSZ_SCALE_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_RGBSZ_SCALE_G
++// Description : Green number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_G_RESET  0x0
++#define VEC_RGBSZ_SCALE_G_BITS   0x000000f0
++#define VEC_RGBSZ_SCALE_G_MSB    7
++#define VEC_RGBSZ_SCALE_G_LSB    4
++#define VEC_RGBSZ_SCALE_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_RGBSZ_SCALE_B
++// Description : Blue number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_B_RESET  0x0
++#define VEC_RGBSZ_SCALE_B_BITS   0x0000000f
++#define VEC_RGBSZ_SCALE_B_MSB    3
++#define VEC_RGBSZ_SCALE_B_LSB    0
++#define VEC_RGBSZ_SCALE_B_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_PANICS
++// JTAG access : synchronous
++// Description : None
++#define VEC_PANICS_OFFSET 0x00000038
++#define VEC_PANICS_BITS   0xffffffff
++#define VEC_PANICS_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_PANICS_UCOUNT
++// Description : Upper panic count
++#define VEC_PANICS_UCOUNT_RESET  0x0000
++#define VEC_PANICS_UCOUNT_BITS   0xffff0000
++#define VEC_PANICS_UCOUNT_MSB    31
++#define VEC_PANICS_UCOUNT_LSB    16
++#define VEC_PANICS_UCOUNT_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field       : VEC_PANICS_LCOUNT
++// Description : Lower panic count
++#define VEC_PANICS_LCOUNT_RESET  0x0000
++#define VEC_PANICS_LCOUNT_BITS   0x0000ffff
++#define VEC_PANICS_LCOUNT_MSB    15
++#define VEC_PANICS_LCOUNT_LSB    0
++#define VEC_PANICS_LCOUNT_ACCESS "WC"
++// =============================================================================
++// Register    : VEC_STATUS
++// JTAG access : synchronous
++// Description : None
++#define VEC_STATUS_OFFSET 0x0000003c
++#define VEC_STATUS_BITS   0xff000000
++#define VEC_STATUS_RESET  0x0d000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_STATUS_VERSION
++// Description : VEC module version code
++#define VEC_STATUS_VERSION_RESET  0x0d
++#define VEC_STATUS_VERSION_BITS   0xff000000
++#define VEC_STATUS_VERSION_MSB    31
++#define VEC_STATUS_VERSION_LSB    24
++#define VEC_STATUS_VERSION_ACCESS "RO"
++// =============================================================================
++// Register    : VEC_DMA_ADDR_H
++// JTAG access : synchronous
++// Description : Upper 32-bits
++#define VEC_DMA_ADDR_H_OFFSET 0x00000040
++#define VEC_DMA_ADDR_H_BITS   0xffffffff
++#define VEC_DMA_ADDR_H_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DMA_ADDR_H_AXI_ADDR
++// Description : Byte address of DMA transfer frame buffer.
++#define VEC_DMA_ADDR_H_AXI_ADDR_RESET  0x00000000
++#define VEC_DMA_ADDR_H_AXI_ADDR_BITS   0xffffffff
++#define VEC_DMA_ADDR_H_AXI_ADDR_MSB    31
++#define VEC_DMA_ADDR_H_AXI_ADDR_LSB    0
++#define VEC_DMA_ADDR_H_AXI_ADDR_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_BURST_ADDR_L
++// JTAG access : synchronous
++// Description : None
++#define VEC_BURST_ADDR_L_OFFSET 0x00000044
++#define VEC_BURST_ADDR_L_BITS   0xffffffff
++#define VEC_BURST_ADDR_L_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_BURST_ADDR_L_BURST_ADDR
++// Description : the lower 32-bits of the most recent read request sent to AXI
++//               memory.
++#define VEC_BURST_ADDR_L_BURST_ADDR_RESET  0x00000000
++#define VEC_BURST_ADDR_L_BURST_ADDR_BITS   0xffffffff
++#define VEC_BURST_ADDR_L_BURST_ADDR_MSB    31
++#define VEC_BURST_ADDR_L_BURST_ADDR_LSB    0
++#define VEC_BURST_ADDR_L_BURST_ADDR_ACCESS "RO"
++// =============================================================================
++// Register    : VEC_APB_TIMEOUT
++// JTAG access : synchronous
++// Description : None
++#define VEC_APB_TIMEOUT_OFFSET 0x00000048
++#define VEC_APB_TIMEOUT_BITS   0x000103ff
++#define VEC_APB_TIMEOUT_RESET  0x00000014
++// -----------------------------------------------------------------------------
++// Field       : VEC_APB_TIMEOUT_SLVERR_EN
++// Description : 1 = Assert PREADY and PSLVERR on timeout 0 = Assert PREADY only
++#define VEC_APB_TIMEOUT_SLVERR_EN_RESET  0x0
++#define VEC_APB_TIMEOUT_SLVERR_EN_BITS   0x00010000
++#define VEC_APB_TIMEOUT_SLVERR_EN_MSB    16
++#define VEC_APB_TIMEOUT_SLVERR_EN_LSB    16
++#define VEC_APB_TIMEOUT_SLVERR_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_APB_TIMEOUT_TIMEOUT
++// Description : Maximum AXI clock cycles to wait for responses from DAC clock
++//               domain APB block
++#define VEC_APB_TIMEOUT_TIMEOUT_RESET  0x014
++#define VEC_APB_TIMEOUT_TIMEOUT_BITS   0x000003ff
++#define VEC_APB_TIMEOUT_TIMEOUT_MSB    9
++#define VEC_APB_TIMEOUT_TIMEOUT_LSB    0
++#define VEC_APB_TIMEOUT_TIMEOUT_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_80
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_80_OFFSET 0x00000080
++#define VEC_DAC_80_BITS   0x3fff3fff
++#define VEC_DAC_80_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_80_U14_DE_BGN
++// Description : Beginning of active data enable within each visible line
++#define VEC_DAC_80_U14_DE_BGN_RESET  0x0000
++#define VEC_DAC_80_U14_DE_BGN_BITS   0x3fff0000
++#define VEC_DAC_80_U14_DE_BGN_MSB    29
++#define VEC_DAC_80_U14_DE_BGN_LSB    16
++#define VEC_DAC_80_U14_DE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_80_U14_DE_END
++// Description : End of active data enable within each visible line
++#define VEC_DAC_80_U14_DE_END_RESET  0x0000
++#define VEC_DAC_80_U14_DE_END_BITS   0x00003fff
++#define VEC_DAC_80_U14_DE_END_MSB    13
++#define VEC_DAC_80_U14_DE_END_LSB    0
++#define VEC_DAC_80_U14_DE_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_84
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_84_OFFSET 0x00000084
++#define VEC_DAC_84_BITS   0x1fff1fff
++#define VEC_DAC_84_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_84_U13_ACTIVE_RISE
++// Description : Horizontal blanking interval
++#define VEC_DAC_84_U13_ACTIVE_RISE_RESET  0x0000
++#define VEC_DAC_84_U13_ACTIVE_RISE_BITS   0x1fff0000
++#define VEC_DAC_84_U13_ACTIVE_RISE_MSB    28
++#define VEC_DAC_84_U13_ACTIVE_RISE_LSB    16
++#define VEC_DAC_84_U13_ACTIVE_RISE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_84_U13_ACTIVE_FALL
++// Description : Horizontal blanking interval
++#define VEC_DAC_84_U13_ACTIVE_FALL_RESET  0x0000
++#define VEC_DAC_84_U13_ACTIVE_FALL_BITS   0x00001fff
++#define VEC_DAC_84_U13_ACTIVE_FALL_MSB    12
++#define VEC_DAC_84_U13_ACTIVE_FALL_LSB    0
++#define VEC_DAC_84_U13_ACTIVE_FALL_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_88
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_88_OFFSET 0x00000088
++#define VEC_DAC_88_BITS   0x1fff1fff
++#define VEC_DAC_88_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_88_U13_HALF_LINE_PERIOD
++// Description : Ratio of DAC clock to horizontal line rate, halved
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_RESET  0x0000
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_BITS   0x1fff0000
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_MSB    28
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_LSB    16
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_88_U13_HORZ_SYNC
++// Description : Width of horizontal sync pulses
++#define VEC_DAC_88_U13_HORZ_SYNC_RESET  0x0000
++#define VEC_DAC_88_U13_HORZ_SYNC_BITS   0x00001fff
++#define VEC_DAC_88_U13_HORZ_SYNC_MSB    12
++#define VEC_DAC_88_U13_HORZ_SYNC_LSB    0
++#define VEC_DAC_88_U13_HORZ_SYNC_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_8C
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_8C_OFFSET 0x0000008c
++#define VEC_DAC_8C_BITS   0x1fff1fff
++#define VEC_DAC_8C_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_8C_U13_BURST_RISE
++// Description : Start of raised-cosine colour burst envelope
++#define VEC_DAC_8C_U13_BURST_RISE_RESET  0x0000
++#define VEC_DAC_8C_U13_BURST_RISE_BITS   0x1fff0000
++#define VEC_DAC_8C_U13_BURST_RISE_MSB    28
++#define VEC_DAC_8C_U13_BURST_RISE_LSB    16
++#define VEC_DAC_8C_U13_BURST_RISE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_8C_U13_BURST_FALL
++// Description : End of raised-cosine colour burst envelope
++#define VEC_DAC_8C_U13_BURST_FALL_RESET  0x0000
++#define VEC_DAC_8C_U13_BURST_FALL_BITS   0x00001fff
++#define VEC_DAC_8C_U13_BURST_FALL_MSB    12
++#define VEC_DAC_8C_U13_BURST_FALL_LSB    0
++#define VEC_DAC_8C_U13_BURST_FALL_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_90
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_90_OFFSET 0x00000090
++#define VEC_DAC_90_BITS   0x1fff3fff
++#define VEC_DAC_90_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_90_U13_VERT_EQ
++// Description : Width of vertical equalisation pulses (= half line minus
++//               serration)
++#define VEC_DAC_90_U13_VERT_EQ_RESET  0x0000
++#define VEC_DAC_90_U13_VERT_EQ_BITS   0x1fff0000
++#define VEC_DAC_90_U13_VERT_EQ_MSB    28
++#define VEC_DAC_90_U13_VERT_EQ_LSB    16
++#define VEC_DAC_90_U13_VERT_EQ_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_90_U14_VERT_SYNC
++// Description : Width of vertical sync pulses
++#define VEC_DAC_90_U14_VERT_SYNC_RESET  0x0000
++#define VEC_DAC_90_U14_VERT_SYNC_BITS   0x00003fff
++#define VEC_DAC_90_U14_VERT_SYNC_MSB    13
++#define VEC_DAC_90_U14_VERT_SYNC_LSB    0
++#define VEC_DAC_90_U14_VERT_SYNC_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_94
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_94_OFFSET 0x00000094
++#define VEC_DAC_94_BITS   0x03ff03ff
++#define VEC_DAC_94_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_94_U10_PRE_EQ_BGN
++// Description : Half-lines, inclusive, relative to field datum, where vertical
++//               pre-equalisation pulses start
++#define VEC_DAC_94_U10_PRE_EQ_BGN_RESET  0x000
++#define VEC_DAC_94_U10_PRE_EQ_BGN_BITS   0x03ff0000
++#define VEC_DAC_94_U10_PRE_EQ_BGN_MSB    25
++#define VEC_DAC_94_U10_PRE_EQ_BGN_LSB    16
++#define VEC_DAC_94_U10_PRE_EQ_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_94_U10_PRE_EQ_END
++// Description : Half-lines, inclusive, relative to field datum, where vertical
++//               pre-equalisation pulses end
++#define VEC_DAC_94_U10_PRE_EQ_END_RESET  0x000
++#define VEC_DAC_94_U10_PRE_EQ_END_BITS   0x000003ff
++#define VEC_DAC_94_U10_PRE_EQ_END_MSB    9
++#define VEC_DAC_94_U10_PRE_EQ_END_LSB    0
++#define VEC_DAC_94_U10_PRE_EQ_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_98
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_98_OFFSET 0x00000098
++#define VEC_DAC_98_BITS   0x03ff03ff
++#define VEC_DAC_98_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_98_U10_FIELD_SYNC_BGN
++// Description : Half-lines containing vertical sync pulses (inclusive)
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_RESET  0x000
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_BITS   0x03ff0000
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_MSB    25
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_LSB    16
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_98_U10_FIELD_SYNC_END
++// Description : Half-lines containing vertical sync pulses (inclusive)
++#define VEC_DAC_98_U10_FIELD_SYNC_END_RESET  0x000
++#define VEC_DAC_98_U10_FIELD_SYNC_END_BITS   0x000003ff
++#define VEC_DAC_98_U10_FIELD_SYNC_END_MSB    9
++#define VEC_DAC_98_U10_FIELD_SYNC_END_LSB    0
++#define VEC_DAC_98_U10_FIELD_SYNC_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_9C
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_9C_OFFSET 0x0000009c
++#define VEC_DAC_9C_BITS   0x03ff03ff
++#define VEC_DAC_9C_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_9C_U10_POST_EQ_BGN
++// Description : Half-lines containing vertical post-equalisation pulses
++#define VEC_DAC_9C_U10_POST_EQ_BGN_RESET  0x000
++#define VEC_DAC_9C_U10_POST_EQ_BGN_BITS   0x03ff0000
++#define VEC_DAC_9C_U10_POST_EQ_BGN_MSB    25
++#define VEC_DAC_9C_U10_POST_EQ_BGN_LSB    16
++#define VEC_DAC_9C_U10_POST_EQ_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_9C_U10_POST_EQ_END
++// Description : Half-lines containing vertical post-equalisation pulses
++#define VEC_DAC_9C_U10_POST_EQ_END_RESET  0x000
++#define VEC_DAC_9C_U10_POST_EQ_END_BITS   0x000003ff
++#define VEC_DAC_9C_U10_POST_EQ_END_MSB    9
++#define VEC_DAC_9C_U10_POST_EQ_END_LSB    0
++#define VEC_DAC_9C_U10_POST_EQ_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_A0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A0_OFFSET 0x000000a0
++#define VEC_DAC_A0_BITS   0x03ff03ff
++#define VEC_DAC_A0_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A0_U10_FLD1_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_RESET  0x000
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_BITS   0x03ff0000
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_MSB    25
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_LSB    16
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A0_U10_FLD1_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A0_U10_FLD1_BURST_END_RESET  0x000
++#define VEC_DAC_A0_U10_FLD1_BURST_END_BITS   0x000003ff
++#define VEC_DAC_A0_U10_FLD1_BURST_END_MSB    9
++#define VEC_DAC_A0_U10_FLD1_BURST_END_LSB    0
++#define VEC_DAC_A0_U10_FLD1_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_A4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A4_OFFSET 0x000000a4
++#define VEC_DAC_A4_BITS   0x03ff03ff
++#define VEC_DAC_A4_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A4_U10_FLD2_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_RESET  0x000
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_BITS   0x03ff0000
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_MSB    25
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_LSB    16
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A4_U10_FLD2_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A4_U10_FLD2_BURST_END_RESET  0x000
++#define VEC_DAC_A4_U10_FLD2_BURST_END_BITS   0x000003ff
++#define VEC_DAC_A4_U10_FLD2_BURST_END_MSB    9
++#define VEC_DAC_A4_U10_FLD2_BURST_END_LSB    0
++#define VEC_DAC_A4_U10_FLD2_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_A8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A8_OFFSET 0x000000a8
++#define VEC_DAC_A8_BITS   0x03ff03ff
++#define VEC_DAC_A8_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A8_U10_FLD3_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_RESET  0x000
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_BITS   0x03ff0000
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_MSB    25
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_LSB    16
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_A8_U10_FLD3_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A8_U10_FLD3_BURST_END_RESET  0x000
++#define VEC_DAC_A8_U10_FLD3_BURST_END_BITS   0x000003ff
++#define VEC_DAC_A8_U10_FLD3_BURST_END_MSB    9
++#define VEC_DAC_A8_U10_FLD3_BURST_END_LSB    0
++#define VEC_DAC_A8_U10_FLD3_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_AC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_AC_OFFSET 0x000000ac
++#define VEC_DAC_AC_BITS   0x03ff03ff
++#define VEC_DAC_AC_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_AC_U10_FLD4_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_RESET  0x000
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_BITS   0x03ff0000
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_MSB    25
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_LSB    16
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_AC_U10_FLD4_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++//               PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_AC_U10_FLD4_BURST_END_RESET  0x000
++#define VEC_DAC_AC_U10_FLD4_BURST_END_BITS   0x000003ff
++#define VEC_DAC_AC_U10_FLD4_BURST_END_MSB    9
++#define VEC_DAC_AC_U10_FLD4_BURST_END_LSB    0
++#define VEC_DAC_AC_U10_FLD4_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_B0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B0_OFFSET 0x000000b0
++#define VEC_DAC_B0_BITS   0x03ff03ff
++#define VEC_DAC_B0_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN
++// Description : First and last full visible lines (1-based numbering) in the
++//               PAL/NTSC four field sequence
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_RESET  0x000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_BITS   0x03ff0000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_MSB    25
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_LSB    16
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B0_U10_FLD24_FULL_LINE_END
++// Description : First and last full visible lines (1-based numbering) in the
++//               PAL/NTSC four field sequence
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_RESET  0x000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_BITS   0x000003ff
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_MSB    9
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_LSB    0
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_B4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B4_OFFSET 0x000000b4
++#define VEC_DAC_B4_BITS   0x03ff03ff
++#define VEC_DAC_B4_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN
++// Description : First and last full visible lines (1-based numbering) in the
++//               PAL/NTSC four field sequence
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_RESET  0x000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_BITS   0x03ff0000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_MSB    25
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_LSB    16
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B4_U10_FLD13_FULL_LINE_END
++// Description : First and last full visible lines (1-based numbering) in the
++//               PAL/NTSC four field sequence
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_RESET  0x000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_BITS   0x000003ff
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_MSB    9
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_LSB    0
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_B8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B8_OFFSET 0x000000b8
++#define VEC_DAC_B8_BITS   0x03ff03ff
++#define VEC_DAC_B8_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B8_U10_BOT_HALF_LINE
++// Description : Top and bottom visible half-lines in 1-based standard full
++//               frame numbering, for interlaced modes. Set to zero to disable.
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_RESET  0x000
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_BITS   0x03ff0000
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_MSB    25
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_LSB    16
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_B8_U10_TOP_HALF_LINE
++// Description : Top and bottom visible half-lines in 1-based standard full
++//               frame numbering, for interlaced modes. Set to zero to disable.
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_RESET  0x000
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_BITS   0x000003ff
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_MSB    9
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_LSB    0
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_BC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_BC_OFFSET 0x000000bc
++#define VEC_DAC_BC_BITS   0x07ff07ff
++#define VEC_DAC_BC_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_BC_S11_PEDESTAL
++// Description : NTSC pedestal. For 7.5 IRE, this field is 1024 * 7.5/100. For
++//               PAL, or Japanese NTSC, this field should be zero.
++#define VEC_DAC_BC_S11_PEDESTAL_RESET  0x000
++#define VEC_DAC_BC_S11_PEDESTAL_BITS   0x07ff0000
++#define VEC_DAC_BC_S11_PEDESTAL_MSB    26
++#define VEC_DAC_BC_S11_PEDESTAL_LSB    16
++#define VEC_DAC_BC_S11_PEDESTAL_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_BC_U11_HALF_LINES_PER_FIELD
++// Description : Mode = 625 PAL, Lines per field = 312.5,
++//               u11_half_lines_per_field = 1+2*312 Mode = 525 NTSC, Lines per
++//               field = 262.5, u11_half_lines_per_field = 1+2*262
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_RESET  0x000
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_BITS   0x000007ff
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_MSB    10
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_LSB    0
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_C0
++// JTAG access : synchronous
++// Description : Synopsis DesignWare control
++#define VEC_DAC_C0_OFFSET 0x000000c0
++#define VEC_DAC_C0_BITS   0x000fffff
++#define VEC_DAC_C0_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_CABLE_ENCTR3
++// Description : Synopsis test input
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_RESET  0x0
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_BITS   0x00080000
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_MSB    19
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_LSB    19
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_CABLE_CABLEOUT
++// Description : cable detect state
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_RESET  0x0
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_BITS   0x00070000
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_MSB    18
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_LSB    16
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_MUX_2
++// Description : Select DAC channel 2 output
++#define VEC_DAC_C0_DWC_MUX_2_RESET  0x0
++#define VEC_DAC_C0_DWC_MUX_2_BITS   0x0000c000
++#define VEC_DAC_C0_DWC_MUX_2_MSB    15
++#define VEC_DAC_C0_DWC_MUX_2_LSB    14
++#define VEC_DAC_C0_DWC_MUX_2_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_MUX_1
++// Description : Select DAC channel 1 output
++#define VEC_DAC_C0_DWC_MUX_1_RESET  0x0
++#define VEC_DAC_C0_DWC_MUX_1_BITS   0x00003000
++#define VEC_DAC_C0_DWC_MUX_1_MSB    13
++#define VEC_DAC_C0_DWC_MUX_1_LSB    12
++#define VEC_DAC_C0_DWC_MUX_1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_MUX_0
++// Description : Select DAC channel 0 output
++#define VEC_DAC_C0_DWC_MUX_0_RESET  0x0
++#define VEC_DAC_C0_DWC_MUX_0_BITS   0x00000c00
++#define VEC_DAC_C0_DWC_MUX_0_MSB    11
++#define VEC_DAC_C0_DWC_MUX_0_LSB    10
++#define VEC_DAC_C0_DWC_MUX_0_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C0_DWC_TEST
++// Description : Fixed DAC command word
++#define VEC_DAC_C0_DWC_TEST_RESET  0x000
++#define VEC_DAC_C0_DWC_TEST_BITS   0x000003ff
++#define VEC_DAC_C0_DWC_TEST_MSB    9
++#define VEC_DAC_C0_DWC_TEST_LSB    0
++#define VEC_DAC_C0_DWC_TEST_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_C4
++// JTAG access : synchronous
++// Description : Synopsis DAC control
++#define VEC_DAC_C4_OFFSET 0x000000c4
++#define VEC_DAC_C4_BITS   0x1fffffff
++#define VEC_DAC_C4_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_ENCTR
++// Description : Always write3'b000
++#define VEC_DAC_C4_ENCTR_RESET  0x0
++#define VEC_DAC_C4_ENCTR_BITS   0x1c000000
++#define VEC_DAC_C4_ENCTR_MSB    28
++#define VEC_DAC_C4_ENCTR_LSB    26
++#define VEC_DAC_C4_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_ENSC
++// Description : Enable cable detect - write 3'b000
++#define VEC_DAC_C4_ENSC_RESET  0x0
++#define VEC_DAC_C4_ENSC_BITS   0x03800000
++#define VEC_DAC_C4_ENSC_MSB    25
++#define VEC_DAC_C4_ENSC_LSB    23
++#define VEC_DAC_C4_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_ENDAC
++// Description : Enable DAC channel
++#define VEC_DAC_C4_ENDAC_RESET  0x0
++#define VEC_DAC_C4_ENDAC_BITS   0x00700000
++#define VEC_DAC_C4_ENDAC_MSB    22
++#define VEC_DAC_C4_ENDAC_LSB    20
++#define VEC_DAC_C4_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_ENVBG
++// Description : Enable internal bandgap reference - write '1'
++#define VEC_DAC_C4_ENVBG_RESET  0x0
++#define VEC_DAC_C4_ENVBG_BITS   0x00080000
++#define VEC_DAC_C4_ENVBG_MSB    19
++#define VEC_DAC_C4_ENVBG_LSB    19
++#define VEC_DAC_C4_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_ENEXTREF
++// Description : Enable external reference - write '0'
++#define VEC_DAC_C4_ENEXTREF_RESET  0x0
++#define VEC_DAC_C4_ENEXTREF_BITS   0x00040000
++#define VEC_DAC_C4_ENEXTREF_MSB    18
++#define VEC_DAC_C4_ENEXTREF_LSB    18
++#define VEC_DAC_C4_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_DAC2GC
++// Description : DAC channel 2 gain control - write 6'd63
++#define VEC_DAC_C4_DAC2GC_RESET  0x00
++#define VEC_DAC_C4_DAC2GC_BITS   0x0003f000
++#define VEC_DAC_C4_DAC2GC_MSB    17
++#define VEC_DAC_C4_DAC2GC_LSB    12
++#define VEC_DAC_C4_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_DAC1GC
++// Description : DAC channel 1 gain control - write 6'd63
++#define VEC_DAC_C4_DAC1GC_RESET  0x00
++#define VEC_DAC_C4_DAC1GC_BITS   0x00000fc0
++#define VEC_DAC_C4_DAC1GC_MSB    11
++#define VEC_DAC_C4_DAC1GC_LSB    6
++#define VEC_DAC_C4_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C4_DAC0GC
++// Description : DAC channel 0 gain control - write 6'd63
++#define VEC_DAC_C4_DAC0GC_RESET  0x00
++#define VEC_DAC_C4_DAC0GC_BITS   0x0000003f
++#define VEC_DAC_C4_DAC0GC_MSB    5
++#define VEC_DAC_C4_DAC0GC_LSB    0
++#define VEC_DAC_C4_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_C8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_C8_OFFSET 0x000000c8
++#define VEC_DAC_C8_BITS   0xffffffff
++#define VEC_DAC_C8_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C8_U16_SCALE_SYNC
++// Description : Scaling applied prior to final summation to form the DAC
++//               command word(s)
++#define VEC_DAC_C8_U16_SCALE_SYNC_RESET  0x0000
++#define VEC_DAC_C8_U16_SCALE_SYNC_BITS   0xffff0000
++#define VEC_DAC_C8_U16_SCALE_SYNC_MSB    31
++#define VEC_DAC_C8_U16_SCALE_SYNC_LSB    16
++#define VEC_DAC_C8_U16_SCALE_SYNC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_C8_U16_SCALE_LUMA
++// Description : Scaling applied prior to final summation to form the DAC
++//               command word(s)
++#define VEC_DAC_C8_U16_SCALE_LUMA_RESET  0x0000
++#define VEC_DAC_C8_U16_SCALE_LUMA_BITS   0x0000ffff
++#define VEC_DAC_C8_U16_SCALE_LUMA_MSB    15
++#define VEC_DAC_C8_U16_SCALE_LUMA_LSB    0
++#define VEC_DAC_C8_U16_SCALE_LUMA_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_CC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_CC_OFFSET 0x000000cc
++#define VEC_DAC_CC_BITS   0xffffffff
++#define VEC_DAC_CC_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_CC_S16_SCALE_BURST
++// Description : Scaling applied prior to final summation to form the DAC
++//               command word(s)
++#define VEC_DAC_CC_S16_SCALE_BURST_RESET  0x0000
++#define VEC_DAC_CC_S16_SCALE_BURST_BITS   0xffff0000
++#define VEC_DAC_CC_S16_SCALE_BURST_MSB    31
++#define VEC_DAC_CC_S16_SCALE_BURST_LSB    16
++#define VEC_DAC_CC_S16_SCALE_BURST_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_CC_S16_SCALE_CHROMA
++// Description : Scaling applied prior to final summation to form the DAC
++//               command word(s)
++#define VEC_DAC_CC_S16_SCALE_CHROMA_RESET  0x0000
++#define VEC_DAC_CC_S16_SCALE_CHROMA_BITS   0x0000ffff
++#define VEC_DAC_CC_S16_SCALE_CHROMA_MSB    15
++#define VEC_DAC_CC_S16_SCALE_CHROMA_LSB    0
++#define VEC_DAC_CC_S16_SCALE_CHROMA_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_D0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D0_OFFSET 0x000000d0
++#define VEC_DAC_D0_BITS   0xffffffff
++#define VEC_DAC_D0_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_D0_S16_OFFSET_LUMA
++// Description : These offsets are applied to the chroma and luma channels
++//               before the final MUX
++#define VEC_DAC_D0_S16_OFFSET_LUMA_RESET  0x0000
++#define VEC_DAC_D0_S16_OFFSET_LUMA_BITS   0xffff0000
++#define VEC_DAC_D0_S16_OFFSET_LUMA_MSB    31
++#define VEC_DAC_D0_S16_OFFSET_LUMA_LSB    16
++#define VEC_DAC_D0_S16_OFFSET_LUMA_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_D0_S16_OFFSET_CHRO
++// Description : These offsets are applied to the chroma and luma channels
++//               before the final MUX
++#define VEC_DAC_D0_S16_OFFSET_CHRO_RESET  0x0000
++#define VEC_DAC_D0_S16_OFFSET_CHRO_BITS   0x0000ffff
++#define VEC_DAC_D0_S16_OFFSET_CHRO_MSB    15
++#define VEC_DAC_D0_S16_OFFSET_CHRO_LSB    0
++#define VEC_DAC_D0_S16_OFFSET_CHRO_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_D4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D4_OFFSET 0x000000d4
++#define VEC_DAC_D4_BITS   0xffffffff
++#define VEC_DAC_D4_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_D4_NCO_FREQ
++// Description : This 64-bit frequency command is applied to the phase
++//               accumulator of the NCO (numerically controlled oscillator)
++//               which generates the colour sub-carrier. This value is computed
++//               as ratio of sub-carrier frequency to DAC clock multiplied by
++//               2^64.
++#define VEC_DAC_D4_NCO_FREQ_RESET  0x00000000
++#define VEC_DAC_D4_NCO_FREQ_BITS   0xffffffff
++#define VEC_DAC_D4_NCO_FREQ_MSB    31
++#define VEC_DAC_D4_NCO_FREQ_LSB    0
++#define VEC_DAC_D4_NCO_FREQ_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_D8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D8_OFFSET 0x000000d8
++#define VEC_DAC_D8_BITS   0xffffffff
++#define VEC_DAC_D8_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_D8_NCO_FREQ
++// Description : This 64-bit frequency command is applied to the phase
++//               accumulator of the NCO (numerically controlled oscillator)
++//               which generates the colour sub-carrier. This value is computed
++//               as ratio of sub-carrier frequency to DAC clock multiplied by
++//               2^64.
++#define VEC_DAC_D8_NCO_FREQ_RESET  0x00000000
++#define VEC_DAC_D8_NCO_FREQ_BITS   0xffffffff
++#define VEC_DAC_D8_NCO_FREQ_MSB    31
++#define VEC_DAC_D8_NCO_FREQ_LSB    0
++#define VEC_DAC_D8_NCO_FREQ_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_DC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_DC_OFFSET 0x000000dc
++#define VEC_DAC_DC_BITS   0xffffffff
++#define VEC_DAC_DC_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_DC_FIR_COEFF_CHROMA_0_6
++// Description : FIR filter coefficients
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_RESET  0x0000
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_BITS   0xffff0000
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_MSB    31
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_LSB    16
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_DC_FIR_COEFF_LUMA_0_6
++// Description : FIR filter coefficients
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_RESET  0x0000
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_BITS   0x0000ffff
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_MSB    15
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_LSB    0
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_E0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E0_OFFSET 0x000000e0
++#define VEC_DAC_E0_BITS   0xffffffff
++#define VEC_DAC_E0_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E0_FIR_COEFF_CHROMA_1_5
++// Description : FIR filter coefficients
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_RESET  0x0000
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_BITS   0xffff0000
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_MSB    31
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_LSB    16
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E0_FIR_COEFF_LUMA_1_5
++// Description : FIR filter coefficients
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_RESET  0x0000
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_BITS   0x0000ffff
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_MSB    15
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_LSB    0
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_E4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E4_OFFSET 0x000000e4
++#define VEC_DAC_E4_BITS   0xffffffff
++#define VEC_DAC_E4_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E4_FIR_COEFF_CHROMA_2_4
++// Description : FIR filter coefficients
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_RESET  0x0000
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_BITS   0xffff0000
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_MSB    31
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_LSB    16
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E4_FIR_COEFF_LUMA_2_4
++// Description : FIR filter coefficients
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_RESET  0x0000
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_BITS   0x0000ffff
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_MSB    15
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_LSB    0
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_E8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E8_OFFSET 0x000000e8
++#define VEC_DAC_E8_BITS   0xffffffff
++#define VEC_DAC_E8_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E8_FIR_COEFF_CHROMA_3
++// Description : FIR filter coefficients
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_RESET  0x0000
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_BITS   0xffff0000
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_MSB    31
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_LSB    16
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_E8_FIR_COEFF_LUMA_3
++// Description : FIR filter coefficients
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_RESET  0x0000
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_BITS   0x0000ffff
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_MSB    15
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_LSB    0
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_ACCESS "RW"
++// =============================================================================
++// Register    : VEC_DAC_EC
++// JTAG access : synchronous
++// Description : Misc. control
++#define VEC_DAC_EC_OFFSET 0x000000ec
++#define VEC_DAC_EC_BITS   0x001fffff
++#define VEC_DAC_EC_RESET  0x00000000
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_SLOW_CLOCK
++// Description : Doubles the raised-cosine rate
++#define VEC_DAC_EC_SLOW_CLOCK_RESET  0x0
++#define VEC_DAC_EC_SLOW_CLOCK_BITS   0x00100000
++#define VEC_DAC_EC_SLOW_CLOCK_MSB    20
++#define VEC_DAC_EC_SLOW_CLOCK_LSB    20
++#define VEC_DAC_EC_SLOW_CLOCK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_FIR_RMINUS1
++// Description : Select 1, 3, 5 or 7 FIR taps
++#define VEC_DAC_EC_FIR_RMINUS1_RESET  0x0
++#define VEC_DAC_EC_FIR_RMINUS1_BITS   0x000c0000
++#define VEC_DAC_EC_FIR_RMINUS1_MSB    19
++#define VEC_DAC_EC_FIR_RMINUS1_LSB    18
++#define VEC_DAC_EC_FIR_RMINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_VERT_FULL_NOT_HALF
++// Description : Disable half-line pulses during VBI
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_RESET  0x0
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_BITS   0x00020000
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_MSB    17
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_LSB    17
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_SEQ_EN
++// Description : Enable NCO reset
++#define VEC_DAC_EC_SEQ_EN_RESET  0x0
++#define VEC_DAC_EC_SEQ_EN_BITS   0x00010000
++#define VEC_DAC_EC_SEQ_EN_MSB    16
++#define VEC_DAC_EC_SEQ_EN_LSB    16
++#define VEC_DAC_EC_SEQ_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_U2_FLD_MASK
++// Description : Field sequence
++#define VEC_DAC_EC_U2_FLD_MASK_RESET  0x0
++#define VEC_DAC_EC_U2_FLD_MASK_BITS   0x0000c000
++#define VEC_DAC_EC_U2_FLD_MASK_MSB    15
++#define VEC_DAC_EC_U2_FLD_MASK_LSB    14
++#define VEC_DAC_EC_U2_FLD_MASK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_U4_SEQ_MASK
++// Description : NCO reset sequence
++#define VEC_DAC_EC_U4_SEQ_MASK_RESET  0x0
++#define VEC_DAC_EC_U4_SEQ_MASK_BITS   0x00003c00
++#define VEC_DAC_EC_U4_SEQ_MASK_MSB    13
++#define VEC_DAC_EC_U4_SEQ_MASK_LSB    10
++#define VEC_DAC_EC_U4_SEQ_MASK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_INTERP_RATE_MINUS1
++// Description : Interpolation rate 2<=R<=16
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_RESET  0x0
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_BITS   0x000003c0
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_MSB    9
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_LSB    6
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_INTERP_SHIFT_MINUS1
++// Description : Power-of-2 scaling after interpolation
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_RESET  0x0
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_BITS   0x0000003c
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_MSB    5
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_LSB    2
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1
++// Description : Interlaced / progressive
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_RESET  0x0
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_BITS   0x00000002
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_MSB    1
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_LSB    1
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field       : VEC_DAC_EC_PAL_EN
++// Description : Enable phase alternate line (PAL) mode
++#define VEC_DAC_EC_PAL_EN_RESET  0x0
++#define VEC_DAC_EC_PAL_EN_BITS   0x00000001
++#define VEC_DAC_EC_PAL_EN_MSB    0
++#define VEC_DAC_EC_PAL_EN_LSB    0
++#define VEC_DAC_EC_PAL_EN_ACCESS "RW"
++// =============================================================================
++#endif // VEC_REGS_DEFINED

+ 75 - 0
target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch

@@ -0,0 +1,75 @@
+From 3a419974ba02d32795a5ecfaf3c020f23173b6a1 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Tue, 14 Feb 2023 20:58:59 +0000
+Subject: [PATCH] v4l2: Add pisp compression format support to v4l2
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c  | 12 ++++++++----
+ include/uapi/linux/media-bus-format.h | 14 ++++++++++++++
+ include/uapi/linux/videodev2.h        | 12 ++++++++----
+ 3 files changed, 30 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1507,10 +1507,14 @@ static void v4l_fill_fmtdesc(struct v4l2
+ 		case V4L2_PIX_FMT_QC08C:	descr = "QCOM Compressed 8-bit Format"; break;
+ 		case V4L2_PIX_FMT_QC10C:	descr = "QCOM Compressed 10-bit Format"; break;
+ 		case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
+-		case V4L2_PIX_FMT_PISP_COMP_RGGB:
+-		case V4L2_PIX_FMT_PISP_COMP_GRBG:
+-		case V4L2_PIX_FMT_PISP_COMP_GBRG:
+-		case V4L2_PIX_FMT_PISP_COMP_BGGR: descr = "PiSP Bayer Compressed Format"; break;
++		case V4L2_PIX_FMT_PISP_COMP1_RGGB:
++		case V4L2_PIX_FMT_PISP_COMP1_GRBG:
++		case V4L2_PIX_FMT_PISP_COMP1_GBRG:
++		case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP Bayer Comp 1"; break;
++		case V4L2_PIX_FMT_PISP_COMP2_RGGB:
++		case V4L2_PIX_FMT_PISP_COMP2_GRBG:
++		case V4L2_PIX_FMT_PISP_COMP2_GBRG:
++		case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP Bayer Comp 2"; break;
+ 		default:
+ 			if (fmt->description[0])
+ 				return;
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -175,4 +175,18 @@
+ /* Sensor ancillary metadata formats - next is 0x7002 */
+ #define MEDIA_BUS_FMT_SENSOR_DATA		0x7002
+ 
++/* PiSP Formats */
++#define MEDIA_BUS_FMT_PISP_COMP1_RGGB		0x8001
++#define MEDIA_BUS_FMT_PISP_COMP1_GRBG		0x8002
++#define MEDIA_BUS_FMT_PISP_COMP1_GBRG		0x8003
++#define MEDIA_BUS_FMT_PISP_COMP1_BGGR		0x8004
++#define MEDIA_BUS_FMT_PISP_COMP2_RGGB		0x8005
++#define MEDIA_BUS_FMT_PISP_COMP2_GRBG		0x8006
++#define MEDIA_BUS_FMT_PISP_COMP2_GBRG		0x8007
++#define MEDIA_BUS_FMT_PISP_COMP2_BGGR		0x8008
++
++#define MEDIA_BUS_FMT_PISP_FE_CONFIG		0x8100
++#define MEDIA_BUS_FMT_PISP_FE_STATS		0x8101
++#define MEDIA_BUS_FMT_PISP_BE_CONFIG		0x8200
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -795,10 +795,14 @@ struct v4l2_pix_format {
+ 
+ /* The pixel format for all our buffers (the precise format is found in the config buffer). */
+ #define V4L2_PIX_FMT_RPI_BE		v4l2_fourcc('R', 'P', 'B', 'P')
+-#define V4L2_PIX_FMT_PISP_COMP_RGGB	v4l2_fourcc('P', 'C', 'R', 'G')
+-#define V4L2_PIX_FMT_PISP_COMP_GRBG	v4l2_fourcc('P', 'C', 'G', 'R')
+-#define V4L2_PIX_FMT_PISP_COMP_GBRG	v4l2_fourcc('P', 'C', 'G', 'B')
+-#define V4L2_PIX_FMT_PISP_COMP_BGGR	v4l2_fourcc('P', 'C', 'B', 'G')
++#define V4L2_PIX_FMT_PISP_COMP1_RGGB	v4l2_fourcc('P', 'C', '1', 'R')
++#define V4L2_PIX_FMT_PISP_COMP1_GRBG	v4l2_fourcc('P', 'C', '1', 'G')
++#define V4L2_PIX_FMT_PISP_COMP1_GBRG	v4l2_fourcc('P', 'C', '1', 'g')
++#define V4L2_PIX_FMT_PISP_COMP1_BGGR	v4l2_fourcc('P', 'C', '1', 'B')
++#define V4L2_PIX_FMT_PISP_COMP2_RGGB	v4l2_fourcc('P', 'C', '2', 'R')
++#define V4L2_PIX_FMT_PISP_COMP2_GRBG	v4l2_fourcc('P', 'C', '2', 'G')
++#define V4L2_PIX_FMT_PISP_COMP2_GBRG	v4l2_fourcc('P', 'C', '2', 'g')
++#define V4L2_PIX_FMT_PISP_COMP2_BGGR	v4l2_fourcc('P', 'C', '2', 'B')
+ 
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */

+ 4527 - 0
target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch

@@ -0,0 +1,4527 @@
+From 8a31623de7f034f6521b348e9a510e78a6e7e493 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Tue, 14 Feb 2023 17:30:12 +0000
+Subject: [PATCH] media: rp1: Add CFE (Camera Front End) support
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/platform/raspberrypi/Kconfig    |    1 +
+ drivers/media/platform/raspberrypi/Makefile   |    1 +
+ .../platform/raspberrypi/rp1_cfe/Kconfig      |   14 +
+ .../platform/raspberrypi/rp1_cfe/Makefile     |    6 +
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 2186 +++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h  |   40 +
+ .../platform/raspberrypi/rp1_cfe/cfe_fmts.h   |  294 +++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c |  446 ++++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h |   75 +
+ .../media/platform/raspberrypi/rp1_cfe/dphy.c |  177 ++
+ .../media/platform/raspberrypi/rp1_cfe/dphy.h |   26 +
+ .../raspberrypi/rp1_cfe/pisp_common.h         |   69 +
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c    |  563 +++++
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.h    |   53 +
+ .../raspberrypi/rp1_cfe/pisp_fe_config.h      |  272 ++
+ .../raspberrypi/rp1_cfe/pisp_statistics.h     |   62 +
+ .../platform/raspberrypi/rp1_cfe/pisp_types.h |  144 ++
+ 17 files changed, 4429 insertions(+)
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+
+--- a/drivers/media/platform/raspberrypi/Kconfig
++++ b/drivers/media/platform/raspberrypi/Kconfig
+@@ -3,3 +3,4 @@
+ comment "Raspberry Pi media platform drivers"
+ 
+ source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
++source "drivers/media/platform/raspberrypi/rp1_cfe/Kconfig"
+--- a/drivers/media/platform/raspberrypi/Makefile
++++ b/drivers/media/platform/raspberrypi/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+ 
+ obj-y += pisp_be/
++obj-y += rp1_cfe/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig
+@@ -0,0 +1,14 @@
++# RP1 V4L2 camera support
++
++config VIDEO_RP1_CFE
++	tristate "RP1 Camera Frond End (CFE) video capture driver"
++	depends on VIDEO_DEV
++	select VIDEO_V4L2_SUBDEV_API
++	select MEDIA_CONTROLLER
++	select VIDEOBUF2_DMA_CONTIG
++	select V4L2_FWNODE
++	help
++	  Say Y here to enable support for the RP1 Camera Front End.
++
++	  To compile this driver as a module, choose M here. The module will be
++	  called rp1-cfe.
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for RP1 Camera Front End driver
++#
++rp1-cfe-objs := cfe.o csi2.o pisp_fe.o dphy.o
++obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -0,0 +1,2186 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 Camera Front End Driver
++ *
++ * Copyright (C) 2021-2022 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/atomic.h>
++#include <linux/clk.h>
++#include <linux/debugfs.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of_graph.h>
++#include <linux/phy/phy.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-async.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-fwnode.h>
++#include <media/v4l2-ioctl.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "cfe.h"
++#include "cfe_fmts.h"
++#include "csi2.h"
++#include "pisp_fe.h"
++#include "pisp_fe_config.h"
++#include "pisp_statistics.h"
++
++#define CFE_MODULE_NAME	"rp1-cfe"
++#define CFE_VERSION	"1.0"
++
++bool cfe_debug_irq;
++
++#define cfe_dbg_irq(fmt, arg...)                              \
++	do {                                                  \
++		if (cfe_debug_irq)                            \
++			dev_dbg(&cfe->pdev->dev, fmt, ##arg); \
++	} while (0)
++#define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg)
++#define cfe_info(fmt, arg...) dev_info(&cfe->pdev->dev, fmt, ##arg)
++#define cfe_err(fmt, arg...) dev_err(&cfe->pdev->dev, fmt, ##arg)
++
++/* MIPICFG registers */
++#define MIPICFG_CFG		0x004
++#define MIPICFG_INTR		0x028
++#define MIPICFG_INTE		0x02c
++#define MIPICFG_INTF		0x030
++#define MIPICFG_INTS		0x034
++
++#define MIPICFG_CFG_SEL_CSI	BIT(0)
++
++#define MIPICFG_INT_CSI_DMA	BIT(0)
++#define MIPICFG_INT_CSI_HOST	BIT(2)
++#define MIPICFG_INT_PISP_FE	BIT(4)
++
++#define BPL_ALIGNMENT 16
++#define MAX_BYTESPERLINE 0xffffff00
++#define MAX_BUFFER_SIZE  0xffffff00
++/*
++ * Max width is therefore determined by the max stride divided by the number of
++ * bits per pixel.
++ *
++ * However, to avoid overflow issues let's use a 16k maximum. This lets us
++ * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful
++ * review and adjustment of the code is needed so that it will deal with
++ * overflows correctly.
++ */
++#define MAX_WIDTH 16384
++#define MAX_HEIGHT MAX_WIDTH
++/* Define a nominal minimum image size */
++#define MIN_WIDTH 16
++#define MIN_HEIGHT 16
++/* Default size of the embedded buffer */
++#define DEFAULT_EMBEDDED_SIZE 8192
++
++const struct v4l2_mbus_framefmt cfe_default_format = {
++	.width = 640,
++	.height = 480,
++	.code = MEDIA_BUS_FMT_SRGGB10_1X10,
++	.field = V4L2_FIELD_NONE,
++	.colorspace = V4L2_COLORSPACE_RAW,
++	.ycbcr_enc = V4L2_YCBCR_ENC_601,
++	.quantization = V4L2_QUANTIZATION_FULL_RANGE,
++	.xfer_func = V4L2_XFER_FUNC_NONE,
++};
++
++const struct v4l2_mbus_framefmt cfe_default_meta_format = {
++	.width = 8192,
++	.height = 1,
++	.code = MEDIA_BUS_FMT_SENSOR_DATA,
++};
++
++enum node_ids {
++	/* CSI2 HW output nodes first. */
++	CSI2_CH0,
++	CSI2_CH1_EMBEDDED,
++	CSI2_CH2,
++	CSI2_CH3,
++	/* FE only nodes from here on. */
++	FE_OUT0,
++	FE_OUT1,
++	FE_STATS,
++	FE_CONFIG,
++	NUM_NODES
++};
++
++struct node_description {
++	unsigned int id;
++	const char *name;
++	enum v4l2_buf_type buf_type;
++	unsigned int cap;
++	unsigned int pad_flags;
++	unsigned int link_pad;
++};
++
++/* Must match the ordering of enum ids */
++static const struct node_description node_desc[NUM_NODES] = {
++	[CSI2_CH0] = {
++		.name = "csi2_ch0",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++		.cap = V4L2_CAP_VIDEO_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = CSI2_NUM_CHANNELS + 0
++	},
++	/* This node is assigned for the embedded data channel! */
++	[CSI2_CH1_EMBEDDED] = {
++		.name = "embedded",
++		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++		.cap = V4L2_CAP_META_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = CSI2_NUM_CHANNELS + 1
++	},
++	[CSI2_CH2] = {
++		.name = "csi2_ch2",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++		.cap = V4L2_CAP_META_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = CSI2_NUM_CHANNELS + 2
++	},
++	[CSI2_CH3] = {
++		.name = "csi2_ch3",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++		.cap = V4L2_CAP_META_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = CSI2_NUM_CHANNELS + 3
++	},
++	[FE_OUT0] = {
++		.name = "fe_image0",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++		.cap = V4L2_CAP_VIDEO_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = FE_OUTPUT0_PAD
++	},
++	[FE_OUT1] = {
++		.name = "fe_image1",
++		.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++		.cap = V4L2_CAP_VIDEO_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = FE_OUTPUT1_PAD
++	},
++	[FE_STATS] = {
++		.name = "fe_stats",
++		.buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++		.cap = V4L2_CAP_META_CAPTURE,
++		.pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = FE_STATS_PAD
++	},
++	[FE_CONFIG] = {
++		.name = "fe_config",
++		.buf_type = V4L2_BUF_TYPE_META_OUTPUT,
++		.cap = V4L2_CAP_META_OUTPUT,
++		.pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
++		.link_pad = FE_CONFIG_PAD
++	},
++};
++
++#define is_fe_node(node) (((node)->id) >= FE_OUT0)
++#define is_csi2_node(node) (!is_fe_node(node))
++#define is_image_output_node(node)                                               \
++	(node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++#define is_meta_output_node(node)                                                \
++	(node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
++#define is_meta_input_node(node)                                                 \
++	(node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_OUTPUT)
++#define is_meta_node(node) (is_meta_output_node(node) || is_meta_input_node(node))
++
++/* To track state across all nodes. */
++#define NUM_STATES		5
++#define NODE_REGISTERED		BIT(0)
++#define NODE_ENABLED		BIT(1)
++#define NODE_STREAMING		BIT(2)
++#define FS_INT			BIT(3)
++#define FE_INT			BIT(4)
++
++struct cfe_buffer {
++	struct vb2_v4l2_buffer vb;
++	struct list_head list;
++};
++
++struct cfe_config_buffer {
++	struct cfe_buffer buf;
++	struct pisp_fe_config config;
++};
++
++static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb)
++{
++	return container_of(vb, struct cfe_buffer, vb.vb2_buf);
++}
++
++static inline
++struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf)
++{
++	return container_of(buf, struct cfe_config_buffer, buf);
++}
++
++struct cfe_node {
++	unsigned int id;
++	/* Pointer pointing to current v4l2_buffer */
++	struct cfe_buffer *cur_frm;
++	/* Pointer pointing to next v4l2_buffer */
++	struct cfe_buffer *next_frm;
++	/* Used to store current pixel format */
++	struct v4l2_format fmt;
++	/* Buffer queue used in video-buf */
++	struct vb2_queue buffer_queue;
++	/* Queue of filled frames */
++	struct list_head dma_queue;
++	/* 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 cfe_device *cfe;
++	struct media_pad pad;
++};
++
++struct cfe_device {
++	struct dentry *debugfs;
++	struct kref kref;
++
++	/* V4l2 specific parameters */
++	struct v4l2_async_subdev asd;
++
++	/* peripheral base address */
++	void __iomem *mipi_cfg_base;
++
++	struct clk *clk;
++
++	/* V4l2 device */
++	struct v4l2_device v4l2_dev;
++	struct media_device mdev;
++	struct media_pipeline pipe;
++
++	/* IRQ lock for node state and DMA queues */
++	spinlock_t state_lock;
++	bool job_ready;
++	bool job_queued;
++
++	/* parent device */
++	struct platform_device *pdev;
++	/* subdevice async Notifier */
++	struct v4l2_async_notifier notifier;
++
++	/* ptr to sub device */
++	struct v4l2_subdev *sensor;
++
++	struct cfe_node node[NUM_NODES];
++	DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES);
++
++	struct csi2_device csi2;
++	struct pisp_fe_device fe;
++
++	bool sensor_embedded_data;
++	int fe_csi2_channel;
++
++	unsigned int sequence;
++	u64 ts;
++};
++
++static inline bool is_fe_enabled(struct cfe_device *cfe)
++{
++	return cfe->fe_csi2_channel != -1;
++}
++
++static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev)
++{
++	return container_of(v4l2_dev, struct cfe_device, v4l2_dev);
++}
++
++static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset)
++{
++	return readl(cfe->mipi_cfg_base + offset);
++}
++
++static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val)
++{
++	writel(val, cfe->mipi_cfg_base + offset);
++}
++
++static bool check_state(struct cfe_device *cfe, unsigned long state,
++			unsigned int node_id)
++{
++	unsigned long bit;
++
++	for_each_set_bit(bit, &state, sizeof(state)) {
++		if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags))
++			return false;
++	}
++	return true;
++}
++
++static void set_state(struct cfe_device *cfe, unsigned long state,
++		      unsigned int node_id)
++{
++	unsigned long bit;
++
++	for_each_set_bit(bit, &state, sizeof(state))
++		set_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
++}
++
++static void clear_state(struct cfe_device *cfe, unsigned long state,
++			unsigned int node_id)
++{
++	unsigned long bit;
++
++	for_each_set_bit(bit, &state, sizeof(state))
++		clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
++}
++
++static bool test_any_node(struct cfe_device *cfe, unsigned long cond)
++{
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		if (check_state(cfe, cond, i))
++			return true;
++	}
++
++	return false;
++}
++
++static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond,
++			   unsigned long cond)
++{
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		if (check_state(cfe, precond, i)) {
++			if (!check_state(cfe, cond, i))
++				return false;
++		}
++	}
++
++	return true;
++}
++
++static void clear_all_nodes(struct cfe_device *cfe, unsigned long precond,
++			    unsigned long state)
++{
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		if (check_state(cfe, precond, i))
++			clear_state(cfe, state, i);
++	}
++}
++
++static int mipi_cfg_regs_show(struct seq_file *s, void *data)
++{
++	struct cfe_device *cfe = s->private;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++	if (ret)
++		return ret;
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg))
++	DUMP(MIPICFG_CFG);
++	DUMP(MIPICFG_INTR);
++	DUMP(MIPICFG_INTE);
++	DUMP(MIPICFG_INTF);
++	DUMP(MIPICFG_INTS);
++#undef DUMP
++
++	pm_runtime_put(&cfe->pdev->dev);
++
++	return 0;
++}
++
++static int format_show(struct seq_file *s, void *data)
++{
++	struct cfe_device *cfe = s->private;
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++		unsigned long sb, state = 0;
++
++		for (sb = 0; sb < NUM_STATES; sb++) {
++			if (check_state(cfe, BIT(sb), i))
++				state |= BIT(sb);
++		}
++
++		seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
++			   node_desc[i].name, state);
++
++		if (is_image_output_node(node))
++			seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n"
++				      "resolution: %ux%u\nbpl: %u\nsize: %u\n",
++				   V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat),
++				   node->fmt.fmt.pix.pixelformat,
++				   node->fmt.fmt.pix.width,
++				   node->fmt.fmt.pix.height,
++				   node->fmt.fmt.pix.bytesperline,
++				   node->fmt.fmt.pix.sizeimage);
++		else
++			seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\nsize: %u\n",
++				   V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat),
++				   node->fmt.fmt.meta.dataformat,
++				   node->fmt.fmt.meta.buffersize);
++	}
++
++	return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs);
++DEFINE_SHOW_ATTRIBUTE(format);
++
++/* Format setup functions */
++const struct cfe_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 const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(formats); i++) {
++		if (formats[i].fourcc == pixelformat)
++			return &formats[i];
++	}
++
++	return NULL;
++}
++
++static int cfe_calc_format_size_bpl(struct cfe_device *cfe,
++				    const struct cfe_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 =
++		ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT);
++
++	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;
++
++	cfe_dbg("%s: " V4L2_FOURCC_CONV " size: %ux%u bpl:%u img_size:%u\n",
++		__func__, V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat),
++		f->fmt.pix.width, f->fmt.pix.height,
++		f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
++
++	return 0;
++}
++
++static void cfe_schedule_next_csi2_job(struct cfe_device *cfe)
++{
++	struct cfe_buffer *buf;
++	unsigned int i;
++	dma_addr_t addr;
++
++	for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++		struct cfe_node *node = &cfe->node[i];
++		unsigned int stride, size;
++
++		if (!check_state(cfe, NODE_STREAMING, i))
++			continue;
++
++		buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
++				       list);
++		node->next_frm = buf;
++		list_del(&buf->list);
++
++		cfe_dbg("%s: [%s] buffer:%p\n",
++			__func__, node_desc[node->id].name, &buf->vb.vb2_buf);
++
++		if (is_meta_node(node)) {
++			size = node->fmt.fmt.meta.buffersize;
++			stride = 0;
++		} else {
++			size = node->fmt.fmt.pix.sizeimage;
++			stride = node->fmt.fmt.pix.bytesperline;
++		}
++
++		addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++		csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size);
++	}
++}
++
++static void cfe_schedule_next_pisp_job(struct cfe_device *cfe)
++{
++	struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 };
++	struct cfe_config_buffer *config_buf;
++	struct cfe_buffer *buf;
++	unsigned int i;
++
++	for (i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++
++		if (!check_state(cfe, NODE_STREAMING, i))
++			continue;
++
++		buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
++				       list);
++
++		cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
++			    node_desc[node->id].name, &buf->vb.vb2_buf);
++
++		node->next_frm = buf;
++		vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf;
++		list_del(&buf->list);
++	}
++
++	config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm);
++	pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config);
++}
++
++static bool cfe_check_job_ready(struct cfe_device *cfe)
++{
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++
++		if (!check_state(cfe, NODE_ENABLED, i))
++			continue;
++
++		if (list_empty(&node->dma_queue)) {
++			cfe_dbg_irq("%s: [%s] has no buffer, unable to schedule job\n",
++				    __func__, node_desc[i].name);
++			return false;
++		}
++	}
++
++	return true;
++}
++
++static void cfe_prepare_next_job(struct cfe_device *cfe)
++{
++	cfe->job_queued = true;
++	cfe_schedule_next_csi2_job(cfe);
++	if (is_fe_enabled(cfe))
++		cfe_schedule_next_pisp_job(cfe);
++
++	/* Flag if another job is ready after this. */
++	cfe->job_ready = cfe_check_job_ready(cfe);
++
++	cfe_dbg_irq("%s: end with scheduled job\n", __func__);
++}
++
++static void cfe_process_buffer_complete(struct cfe_node *node,
++					unsigned int sequence)
++{
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++		    &node->cur_frm->vb.vb2_buf);
++
++	node->cur_frm->vb.sequence = sequence;
++	vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++}
++
++static void cfe_queue_event_sof(struct cfe_node *node)
++{
++	struct v4l2_event event = {
++		.type = V4L2_EVENT_FRAME_SYNC,
++		.u.frame_sync.frame_sequence = node->cfe->sequence,
++	};
++
++	v4l2_event_queue(&node->video_dev, &event);
++}
++
++static void cfe_sof_isr_handler(struct cfe_node *node)
++{
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++		    cfe->sequence);
++
++	node->cur_frm = node->next_frm;
++	node->next_frm = NULL;
++
++	/*
++	 * If this is the first node to see a frame start,  sample the
++	 * timestamp to use for all frames across all channels.
++	 */
++	if (!test_any_node(cfe, NODE_STREAMING | FS_INT))
++		cfe->ts = ktime_get_ns();
++
++	set_state(cfe, FS_INT, node->id);
++
++	/* If all nodes have seen a frame start, we can queue another job. */
++	if (test_all_nodes(cfe, NODE_STREAMING, FS_INT))
++		cfe->job_queued = false;
++
++	if (node->cur_frm)
++		node->cur_frm->vb.vb2_buf.timestamp = cfe->ts;
++
++	if (is_image_output_node(node))
++		cfe_queue_event_sof(node);
++}
++
++static void cfe_eof_isr_handler(struct cfe_node *node)
++{
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++		    cfe->sequence);
++
++	if (node->cur_frm)
++		cfe_process_buffer_complete(node, cfe->sequence);
++
++	node->cur_frm = NULL;
++	set_state(cfe, FE_INT, node->id);
++
++	/*
++	 * If all nodes have seen a frame end, we can increment
++	 * the sequence counter now.
++	 */
++	if (test_all_nodes(cfe, NODE_STREAMING, FE_INT)) {
++		cfe->sequence++;
++		clear_all_nodes(cfe, NODE_STREAMING, FE_INT | FS_INT);
++	}
++}
++
++static irqreturn_t cfe_isr(int irq, void *dev)
++{
++	struct cfe_device *cfe = dev;
++	unsigned int i;
++	bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}, lci[NUM_NODES] = {0};
++	u32 sts;
++
++	sts = cfg_reg_read(cfe, MIPICFG_INTS);
++
++	if (sts & MIPICFG_INT_CSI_DMA)
++		csi2_isr(&cfe->csi2, sof, eof, lci);
++
++	if (sts & MIPICFG_INT_PISP_FE)
++		pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
++			    eof + CSI2_NUM_CHANNELS);
++
++	spin_lock(&cfe->state_lock);
++
++	for (i = 0; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++
++		/*
++		 * The check_state(NODE_STREAMING) is to ensure we do not loop
++		 * over the CSI2_CHx nodes when the FE is active since they
++		 * generate interrupts even though the node is not streaming.
++		 */
++		if (!check_state(cfe, NODE_STREAMING, i) ||
++		    !(sof[i] || eof[i] || lci[i]))
++			continue;
++
++		/*
++		 * There are 3 cases where we could get FS + FE_ACK at
++		 * the same time:
++		 * 1) FE of the current frame, and FS of the next frame.
++		 * 2) FS + FE of the same frame.
++		 * 3) FE of the current frame, and FS + FE of the next
++		 *    frame. To handle this, see the sof handler below.
++		 *
++		 * (1) is handled implicitly by the ordering of the FE and FS
++		 * handlers below.
++		 */
++		if (eof[i]) {
++			/*
++			 * The condition below tests for (2). Run the FS handler
++			 * first before the FE handler, both for the current
++			 * frame.
++			 */
++			if (sof[i] && !check_state(cfe, FS_INT, i)) {
++				cfe_sof_isr_handler(node);
++				sof[i] = false;
++			}
++
++			cfe_eof_isr_handler(node);
++		}
++
++		if (sof[i]) {
++			/*
++			 * The condition below tests for (3). In such cases, we
++			 * come in here with FS flag set in the node state from
++			 * the previous frame since it only gets cleared in
++			 * eof_isr_handler(). Handle the FE for the previous
++			 * frame first before the FS handler for the current
++			 * frame.
++			 */
++			if (check_state(cfe, FS_INT, node->id)) {
++				cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n",
++					__func__, node_desc[node->id].name);
++				cfe_eof_isr_handler(node);
++			}
++
++			cfe_sof_isr_handler(node);
++		}
++
++		if (!cfe->job_queued && cfe->job_ready)
++			cfe_prepare_next_job(cfe);
++	}
++
++	spin_unlock(&cfe->state_lock);
++
++	return IRQ_HANDLED;
++}
++
++/*
++ * Stream helpers
++ */
++
++static void cfe_start_channel(struct cfe_node *node)
++{
++	struct cfe_device *cfe = node->cfe;
++	struct v4l2_subdev_state *state;
++	struct v4l2_mbus_framefmt *source_fmt;
++	const struct cfe_fmt *fmt;
++	unsigned long flags;
++	unsigned int width = 0, height = 0;
++	bool start_fe = is_fe_enabled(cfe) &&
++			test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (start_fe || is_image_output_node(node)) {
++		width = node->fmt.fmt.pix.width;
++		height = node->fmt.fmt.pix.height;
++	}
++
++	state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
++
++	if (start_fe) {
++		WARN_ON(!is_fe_enabled(cfe));
++		cfe_dbg("%s: %s using csi2 channel %d\n",
++			__func__, node_desc[FE_OUT0].name,
++			cfe->fe_csi2_channel);
++
++		source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, cfe->fe_csi2_channel);
++		fmt = find_format_by_code(source_fmt->code);
++
++		/*
++		 * Start the associated CSI2 Channel as well.
++		 *
++		 * Must write to the ADDR register to latch the ctrl values
++		 * even if we are connected to the front end. Once running,
++		 * this is handled by the CSI2 AUTO_ARM mode.
++		 */
++		csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
++				   fmt->csi_dt, CSI2_MODE_FE_STREAMING,
++				   true, false, width, height);
++		csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
++		pisp_fe_start(&cfe->fe);
++	}
++
++	if (is_csi2_node(node)) {
++		u32 mode = CSI2_MODE_NORMAL;
++
++		source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
++			node_desc[node->id].link_pad - CSI2_NUM_CHANNELS);
++		fmt = find_format_by_code(source_fmt->code);
++
++		if (is_image_output_node(node)) {
++			if (node->fmt.fmt.pix.pixelformat ==
++					fmt->remap[CFE_REMAP_16BIT])
++				mode = CSI2_MODE_REMAP;
++			else if (node->fmt.fmt.pix.pixelformat ==
++					fmt->remap[CFE_REMAP_COMPRESSED]) {
++				mode = CSI2_MODE_COMPRESSED;
++				csi2_set_compression(&cfe->csi2, node->id,
++						     CSI2_COMPRESSION_DELTA, 0,
++						     0);
++			}
++		}
++		/* Unconditionally start this CSI2 channel. */
++		csi2_start_channel(&cfe->csi2, node->id, fmt->csi_dt,
++				   mode,
++				   /* Auto arm */
++				   false,
++				   /* Pack bytes */
++				   node->id == CSI2_CH1_EMBEDDED ? true : false,
++				   width, height);
++	}
++
++	v4l2_subdev_unlock_state(state);
++
++	spin_lock_irqsave(&cfe->state_lock, flags);
++	if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING))
++		cfe_prepare_next_job(cfe);
++	spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++static void cfe_stop_channel(struct cfe_node *node, bool fe_stop)
++{
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg("%s: [%s] fe_stop %u\n", __func__,
++		node_desc[node->id].name, fe_stop);
++
++	if (fe_stop) {
++		csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel);
++		pisp_fe_stop(&cfe->fe);
++	}
++
++	if (is_csi2_node(node))
++		csi2_stop_channel(&cfe->csi2, node->id);
++}
++
++static void cfe_return_buffers(struct cfe_node *node,
++			       enum vb2_buffer_state state)
++{
++	struct cfe_device *cfe = node->cfe;
++	struct cfe_buffer *buf, *tmp;
++	unsigned long flags;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	spin_lock_irqsave(&cfe->state_lock, flags);
++	list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
++		list_del(&buf->list);
++		vb2_buffer_done(&buf->vb.vb2_buf, state);
++	}
++
++	if (node->cur_frm)
++		vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
++	if (node->next_frm && node->cur_frm != node->next_frm)
++		vb2_buffer_done(&node->next_frm->vb.vb2_buf, state);
++
++	node->cur_frm = NULL;
++	node->next_frm = NULL;
++	spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++/*
++ * vb2 ops
++ */
++
++static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
++			   unsigned int *nplanes, unsigned int sizes[],
++			   struct device *alloc_devs[])
++{
++	struct cfe_node *node = vb2_get_drv_priv(vq);
++	struct cfe_device *cfe = node->cfe;
++	unsigned int size = is_image_output_node(node) ?
++					  node->fmt.fmt.pix.sizeimage :
++					  node->fmt.fmt.meta.buffersize;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (vq->num_buffers + *nbuffers < 3)
++		*nbuffers = 3 - vq->num_buffers;
++
++	if (*nplanes) {
++		if (sizes[0] < size) {
++			cfe_err("sizes[0] %i < size %u\n", sizes[0], size);
++			return -EINVAL;
++		}
++		size = sizes[0];
++	}
++
++	*nplanes = 1;
++	sizes[0] = size;
++
++	return 0;
++}
++
++static int cfe_buffer_prepare(struct vb2_buffer *vb)
++{
++	struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++	struct cfe_device *cfe = node->cfe;
++	struct cfe_buffer *buf = to_cfe_buffer(vb);
++	unsigned long size;
++
++	cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++		    vb);
++
++	size = is_image_output_node(node) ? node->fmt.fmt.pix.sizeimage :
++					    node->fmt.fmt.meta.buffersize;
++	if (vb2_plane_size(vb, 0) < size) {
++		cfe_err("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);
++
++	if (node->id == FE_CONFIG) {
++		struct cfe_config_buffer *b = to_cfe_config_buffer(buf);
++		void *addr = vb2_plane_vaddr(vb, 0);
++
++		memcpy(&b->config, addr, sizeof(struct pisp_fe_config));
++		return pisp_fe_validate_config(&cfe->fe, &b->config,
++					       &cfe->node[FE_OUT0].fmt,
++					       &cfe->node[FE_OUT1].fmt);
++	}
++
++	return 0;
++}
++
++static void cfe_buffer_queue(struct vb2_buffer *vb)
++{
++	struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++	struct cfe_device *cfe = node->cfe;
++	struct cfe_buffer *buf = to_cfe_buffer(vb);
++	unsigned long flags;
++
++	cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++		    vb);
++
++	spin_lock_irqsave(&cfe->state_lock, flags);
++
++	list_add_tail(&buf->list, &node->dma_queue);
++
++	if (!cfe->job_ready)
++		cfe->job_ready = cfe_check_job_ready(cfe);
++
++	if (!cfe->job_queued && cfe->job_ready &&
++	    test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
++		cfe_dbg("Preparing job immediately for channel %u\n",
++			node->id);
++		cfe_prepare_next_job(cfe);
++	}
++
++	spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
++{
++	struct v4l2_mbus_config mbus_config = { 0 };
++	struct cfe_node *node = vb2_get_drv_priv(vq);
++	struct cfe_device *cfe = node->cfe;
++	int ret;
++
++	cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);
++
++	if (!check_state(cfe, NODE_ENABLED, node->id)) {
++		cfe_err("%s node link is not enabled.\n",
++			node_desc[node->id].name);
++		return -EINVAL;
++	}
++
++	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++	if (ret < 0) {
++		cfe_err("pm_runtime_resume_and_get failed\n");
++		goto err_streaming;
++	}
++
++	ret = media_pipeline_start(&node->pad, &cfe->pipe);
++	if (ret < 0) {
++		cfe_err("Failed to start media pipeline: %d\n", ret);
++		goto err_pm_put;
++	}
++
++	clear_state(cfe, FS_INT | FE_INT, node->id);
++	set_state(cfe, NODE_STREAMING, node->id);
++	cfe_start_channel(node);
++
++	if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
++		cfe_dbg("Not all nodes are set to streaming yet!\n");
++		return 0;
++	}
++
++	cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI);
++	cfg_reg_write(cfe, MIPICFG_INTE, MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE);
++
++	cfe->csi2.active_data_lanes = cfe->csi2.dphy.num_lanes;
++	cfe_dbg("Running with %u data lanes\n", cfe->csi2.active_data_lanes);
++
++	ret = v4l2_subdev_call(cfe->sensor, pad, get_mbus_config, 0,
++			       &mbus_config);
++	if (ret < 0 && ret != -ENOIOCTLCMD) {
++		cfe_err("g_mbus_config failed\n");
++		goto err_pm_put;
++	}
++
++	cfe->csi2.active_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
++	if (!cfe->csi2.active_data_lanes)
++		cfe->csi2.active_data_lanes = cfe->csi2.dphy.num_lanes;
++	if (cfe->csi2.active_data_lanes > cfe->csi2.dphy.num_lanes) {
++		cfe_err("Device has requested %u data lanes, which is >%u configured in DT\n",
++			cfe->csi2.active_data_lanes, cfe->csi2.dphy.num_lanes);
++		ret = -EINVAL;
++		goto err_disable_cfe;
++	}
++
++	cfe_dbg("Starting sensor streaming\n");
++
++	csi2_open_rx(&cfe->csi2);
++
++	cfe->sequence = 0;
++	ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
++	if (ret < 0) {
++		cfe_err("stream on failed in subdev\n");
++		goto err_disable_cfe;
++	}
++
++	cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);
++
++	return 0;
++
++err_disable_cfe:
++	csi2_close_rx(&cfe->csi2);
++	cfe_stop_channel(node, true);
++	media_pipeline_stop(&node->pad);
++err_pm_put:
++	pm_runtime_put(&cfe->pdev->dev);
++err_streaming:
++	cfe_return_buffers(node, VB2_BUF_STATE_QUEUED);
++	clear_state(cfe, NODE_STREAMING, node->id);
++
++	return ret;
++}
++
++static void cfe_stop_streaming(struct vb2_queue *vq)
++{
++	struct cfe_node *node = vb2_get_drv_priv(vq);
++	struct cfe_device *cfe = node->cfe;
++	unsigned long flags;
++	bool fe_stop;
++
++	cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);
++
++	spin_lock_irqsave(&cfe->state_lock, flags);
++	fe_stop = is_fe_enabled(cfe) &&
++		  test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
++
++	cfe->job_ready = false;
++	clear_state(cfe, NODE_STREAMING, node->id);
++	spin_unlock_irqrestore(&cfe->state_lock, flags);
++
++	cfe_stop_channel(node, fe_stop);
++
++	if (!test_any_node(cfe, NODE_STREAMING)) {
++		/* Stop streaming the sensor and disable the peripheral. */
++		if (v4l2_subdev_call(cfe->sensor, video, s_stream, 0) < 0)
++			cfe_err("stream off failed in subdev\n");
++
++		csi2_close_rx(&cfe->csi2);
++
++		cfg_reg_write(cfe, MIPICFG_INTE, 0);
++	}
++
++	media_pipeline_stop(&node->pad);
++
++	/* Clear all queued buffers for the node */
++	cfe_return_buffers(node, VB2_BUF_STATE_ERROR);
++
++	pm_runtime_put(&cfe->pdev->dev);
++
++	cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);
++}
++
++static const struct vb2_ops cfe_video_qops = {
++	.wait_prepare = vb2_ops_wait_prepare,
++	.wait_finish = vb2_ops_wait_finish,
++	.queue_setup = cfe_queue_setup,
++	.buf_prepare = cfe_buffer_prepare,
++	.buf_queue = cfe_buffer_queue,
++	.start_streaming = cfe_start_streaming,
++	.stop_streaming = cfe_stop_streaming,
++};
++
++/*
++ * v4l2 ioctl ops
++ */
++
++static int cfe_querycap(struct file *file, void *priv,
++			struct v4l2_capability *cap)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++
++	strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver));
++	strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card));
++
++	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++		 dev_name(&cfe->pdev->dev));
++
++	cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE |
++			     V4L2_CAP_META_OUTPUT;
++
++	return 0;
++}
++
++static int cfe_enum_fmt_vid_cap(struct file *file, void *priv,
++				struct v4l2_fmtdesc *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++	unsigned int i, j;
++
++	if (!is_image_output_node(node))
++		return -EINVAL;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) {
++		if (f->mbus_code && formats[i].code != f->mbus_code)
++			continue;
++
++		if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT ||
++		    formats[i].flags & CFE_FORMAT_FLAG_META_CAP)
++			continue;
++
++		if (is_fe_node(node) &&
++		    !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT))
++			continue;
++
++		if (j == f->index) {
++			f->pixelformat = formats[i].fourcc;
++			f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++			return 0;
++		}
++		j++;
++	}
++
++	return -EINVAL;
++}
++
++static int cfe_g_fmt(struct file *file, void *priv,
++		     struct v4l2_format *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (f->type != node->buffer_queue.type)
++		return -EINVAL;
++
++	*f = node->fmt;
++
++	return 0;
++}
++
++static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f)
++{
++	struct cfe_device *cfe = node->cfe;
++	const struct cfe_fmt *fmt;
++
++	cfe_dbg("%s: [%s] %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n",
++		__func__, node_desc[node->id].name,
++		f->fmt.pix.width, f->fmt.pix.height,
++		V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat));
++
++	if (!is_image_output_node(node))
++		return -EINVAL;
++
++	/*
++	 * Default to a format that works for both CSI2 and FE.
++	 */
++	fmt = find_format_by_pix(f->fmt.pix.pixelformat);
++	if (!fmt)
++		fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++	f->fmt.pix.pixelformat = fmt->fourcc;
++
++	if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) {
++		f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT];
++		fmt = find_format_by_pix(f->fmt.pix.pixelformat);
++	}
++
++	f->fmt.pix.field = V4L2_FIELD_NONE;
++
++	cfe_calc_format_size_bpl(cfe, fmt, f);
++
++	return 0;
++}
++
++static int cfe_s_fmt_vid_cap(struct file *file, void *priv,
++			     struct v4l2_format *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++	struct vb2_queue *q = &node->buffer_queue;
++	int ret;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (vb2_is_busy(q))
++		return -EBUSY;
++
++	ret = try_fmt_vid_cap(node, f);
++	if (ret)
++		return ret;
++
++	node->fmt = *f;
++
++	cfe_dbg("%s: Set %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", __func__,
++		node->fmt.fmt.pix.width, node->fmt.fmt.pix.height,
++		V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat));
++
++	return 0;
++}
++
++static int cfe_try_fmt_vid_cap(struct file *file, void *priv,
++			       struct v4l2_format *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	return try_fmt_vid_cap(node, f);
++}
++
++static int cfe_enum_fmt_meta(struct file *file, void *priv,
++			     struct v4l2_fmtdesc *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (!is_meta_node(node) || f->index != 0)
++		return -EINVAL;
++
++	switch (node->id) {
++	case CSI2_CH1_EMBEDDED:
++		f->pixelformat = V4L2_META_FMT_SENSOR_DATA;
++		return 0;
++	case FE_STATS:
++		f->pixelformat = V4L2_META_FMT_RPI_FE_STATS;
++		return 0;
++	case FE_CONFIG:
++		f->pixelformat = V4L2_META_FMT_RPI_FE_CFG;
++		return 0;
++	}
++
++	return -EINVAL;
++}
++
++static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
++{
++	switch (node->id) {
++	case CSI2_CH1_EMBEDDED:
++		f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
++		if (!f->fmt.meta.buffersize)
++			f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE;
++		f->fmt.meta.buffersize =
++			min_t(u32, f->fmt.meta.buffersize, MAX_BUFFER_SIZE);
++		f->fmt.meta.buffersize =
++			ALIGN(f->fmt.meta.buffersize, BPL_ALIGNMENT);
++		return 0;
++	case FE_STATS:
++		f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS;
++		f->fmt.meta.buffersize = sizeof(struct pisp_statistics);
++		return 0;
++	case FE_CONFIG:
++		f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG;
++		f->fmt.meta.buffersize = sizeof(struct pisp_fe_config);
++		return 0;
++	}
++
++	return -EINVAL;
++}
++
++static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++	struct vb2_queue *q = &node->buffer_queue;
++	int ret;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++	if (vb2_is_busy(q))
++		return -EBUSY;
++
++	if (f->type != node->buffer_queue.type)
++		return -EINVAL;
++
++	ret = try_fmt_meta(node, f);
++	if (ret)
++		return ret;
++
++	node->fmt = *f;
++
++	cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__,
++		V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat));
++
++	return 0;
++}
++
++static int cfe_try_fmt_meta(struct file *file, void *priv,
++			    struct v4l2_format *f)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++
++	cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++	return try_fmt_meta(node, f);
++}
++
++static int cfe_enum_framesizes(struct file *file, void *priv,
++			       struct v4l2_frmsizeenum *fsize)
++{
++	struct cfe_node *node = video_drvdata(file);
++	struct cfe_device *cfe = node->cfe;
++	const struct cfe_fmt *fmt;
++
++	cfe_dbg("%s [%s]\n", __func__, node_desc[node->id].name);
++
++	if (fsize->index > 0)
++		return -EINVAL;
++
++	/* check for valid format */
++	fmt = find_format_by_pix(fsize->pixel_format);
++	if (!fmt) {
++		cfe_dbg("Invalid pixel code: %x\n", fsize->pixel_format);
++		return -EINVAL;
++	}
++
++	/* TODO: Do we have limits on the step_width? */
++
++	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++	fsize->stepwise.min_width = MIN_WIDTH;
++	fsize->stepwise.max_width = MAX_WIDTH;
++	fsize->stepwise.step_width = 2;
++	fsize->stepwise.min_height = MIN_HEIGHT;
++	fsize->stepwise.max_height = MAX_HEIGHT;
++	fsize->stepwise.step_height = 1;
++
++	return 0;
++}
++
++static int cfe_subscribe_event(struct v4l2_fh *fh,
++			       const struct v4l2_event_subscription *sub)
++{
++	struct cfe_node *node = video_get_drvdata(fh->vdev);
++
++	switch (sub->type) {
++	case V4L2_EVENT_FRAME_SYNC:
++		if (!is_image_output_node(node))
++			break;
++
++		return v4l2_event_subscribe(fh, sub, 2, NULL);
++	case V4L2_EVENT_SOURCE_CHANGE:
++		if (is_meta_input_node(node))
++			break;
++
++		return v4l2_event_subscribe(fh, sub, 4, NULL);
++	}
++
++	return v4l2_ctrl_subscribe_event(fh, sub);
++}
++
++static const struct v4l2_ioctl_ops cfe_ioctl_ops = {
++	.vidioc_querycap = cfe_querycap,
++	.vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap,
++	.vidioc_g_fmt_vid_cap = cfe_g_fmt,
++	.vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap,
++	.vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap,
++
++	.vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta,
++	.vidioc_g_fmt_meta_cap = cfe_g_fmt,
++	.vidioc_s_fmt_meta_cap = cfe_s_fmt_meta,
++	.vidioc_try_fmt_meta_cap = cfe_try_fmt_meta,
++
++	.vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta,
++	.vidioc_g_fmt_meta_out = cfe_g_fmt,
++	.vidioc_s_fmt_meta_out = cfe_s_fmt_meta,
++	.vidioc_try_fmt_meta_out = cfe_try_fmt_meta,
++
++	.vidioc_enum_framesizes = cfe_enum_framesizes,
++
++	.vidioc_reqbufs = vb2_ioctl_reqbufs,
++	.vidioc_create_bufs = vb2_ioctl_create_bufs,
++	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++	.vidioc_querybuf = vb2_ioctl_querybuf,
++	.vidioc_qbuf = vb2_ioctl_qbuf,
++	.vidioc_dqbuf = vb2_ioctl_dqbuf,
++	.vidioc_expbuf = vb2_ioctl_expbuf,
++	.vidioc_streamon = vb2_ioctl_streamon,
++	.vidioc_streamoff = vb2_ioctl_streamoff,
++
++	.vidioc_subscribe_event = cfe_subscribe_event,
++	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++};
++
++static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification,
++		       void *arg)
++{
++	struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev);
++	unsigned int i;
++
++	switch (notification) {
++	case V4L2_DEVICE_NOTIFY_EVENT:
++		for (i = 0; i < NUM_NODES; i++) {
++			struct cfe_node *node = &cfe->node[i];
++
++			if (check_state(cfe, NODE_REGISTERED, i))
++				continue;
++
++			v4l2_event_queue(&node->video_dev, arg);
++		}
++		break;
++	default:
++		break;
++	}
++}
++
++/* cfe capture driver file operations */
++static const struct v4l2_file_operations cfe_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 cfe_video_link_validate(struct media_link *link)
++{
++	struct video_device *vd = container_of(link->sink->entity,
++					       struct video_device, entity);
++	struct cfe_node *node = container_of(vd, struct cfe_node, video_dev);
++	struct cfe_device *cfe = node->cfe;
++	struct v4l2_mbus_framefmt *source_fmt;
++	struct v4l2_subdev_state *state;
++	struct v4l2_subdev *source_sd;
++	int ret = 0;
++
++	cfe_dbg("%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__,
++		node_desc[node->id].name,
++		link->source->entity->name, link->source->index,
++		link->sink->entity->name, link->sink->index);
++
++	if (!media_entity_remote_source_pad_unique(link->sink->entity)) {
++		cfe_err("video node %s pad not connected\n", vd->name);
++		return -ENOTCONN;
++	}
++
++	source_sd = media_entity_to_v4l2_subdev(link->source->entity);
++
++	state = v4l2_subdev_lock_and_get_active_state(source_sd);
++
++	source_fmt = v4l2_subdev_get_pad_format(source_sd, state,
++						link->source->index);
++	if (!source_fmt) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	if (is_image_output_node(node)) {
++		struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
++		const struct cfe_fmt *fmt;
++
++		if (source_fmt->width != pix_fmt->width ||
++		    source_fmt->height != pix_fmt->height) {
++			cfe_err("Wrong width or height %ux%u (remote pad set to %ux%u)\n",
++				pix_fmt->width, pix_fmt->height,
++				source_fmt->width,
++				source_fmt->height);
++			ret = -EINVAL;
++			goto out;
++		}
++
++		fmt = find_format_by_code(source_fmt->code);
++		if (!fmt || fmt->fourcc != pix_fmt->pixelformat) {
++			cfe_err("Format mismatch!\n");
++			ret = -EINVAL;
++			goto out;
++		}
++	} else if (node->id == CSI2_CH1_EMBEDDED) {
++		struct v4l2_meta_format *meta_fmt = &node->fmt.fmt.meta;
++
++		if (source_fmt->width * source_fmt->height !=
++							meta_fmt->buffersize ||
++		    source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) {
++			cfe_err("WARNING: Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n",
++				meta_fmt->buffersize, 1,
++				MEDIA_BUS_FMT_SENSOR_DATA,
++				source_fmt->width,
++				source_fmt->height,
++				source_fmt->code);
++			/* TODO: this should throw an error eventually */
++		}
++	}
++
++out:
++	v4l2_subdev_unlock_state(state);
++
++	return ret;
++}
++
++static const struct media_entity_operations cfe_media_entity_ops = {
++	.link_validate = cfe_video_link_validate,
++};
++
++static int cfe_video_link_notify(struct media_link *link, u32 flags,
++				 unsigned int notification)
++{
++	struct media_device *mdev = link->graph_obj.mdev;
++	struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev);
++	struct media_entity *fe = &cfe->fe.sd.entity;
++	struct media_entity *csi2 = &cfe->csi2.sd.entity;
++	unsigned long lock_flags;
++	unsigned int i;
++
++	if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH)
++		return 0;
++
++	cfe_dbg("%s: %s[%u] -> %s[%u] 0x%x", __func__,
++		link->source->entity->name, link->source->index,
++		link->sink->entity->name, link->sink->index, flags);
++
++	spin_lock_irqsave(&cfe->state_lock, lock_flags);
++
++	for (i = 0; i < NUM_NODES; i++) {
++		if (link->sink->entity != &cfe->node[i].video_dev.entity &&
++		    link->source->entity != &cfe->node[i].video_dev.entity)
++			continue;
++
++		if (link->flags & MEDIA_LNK_FL_ENABLED)
++			set_state(cfe, NODE_ENABLED, i);
++		else
++			clear_state(cfe, NODE_ENABLED, i);
++
++		break;
++	}
++
++	spin_unlock_irqrestore(&cfe->state_lock, lock_flags);
++
++	if (link->source->entity != csi2)
++		return 0;
++	if (link->sink->index != 0)
++		return 0;
++	if (link->source->index == node_desc[CSI2_CH1_EMBEDDED].link_pad)
++		return 0;
++
++	cfe->fe_csi2_channel = -1;
++	if (link->sink->entity == fe && (link->flags & MEDIA_LNK_FL_ENABLED)) {
++		if (link->source->index == node_desc[CSI2_CH0].link_pad)
++			cfe->fe_csi2_channel = CSI2_CH0;
++		else if (link->source->index == node_desc[CSI2_CH2].link_pad)
++			cfe->fe_csi2_channel = CSI2_CH2;
++		else if (link->source->index == node_desc[CSI2_CH3].link_pad)
++			cfe->fe_csi2_channel = CSI2_CH3;
++	}
++
++	if (is_fe_enabled(cfe))
++		cfe_dbg("%s: Found CSI2:%d -> FE:0 link\n", __func__,
++			cfe->fe_csi2_channel);
++	else
++		cfe_dbg("%s: Unable to find CSI2:x -> FE:0 link\n", __func__);
++
++	return 0;
++}
++
++static const struct media_device_ops cfe_media_device_ops = {
++	.link_notify = cfe_video_link_notify,
++};
++
++static void cfe_release(struct kref *kref)
++{
++	struct cfe_device *cfe = container_of(kref, struct cfe_device, kref);
++
++	media_device_cleanup(&cfe->mdev);
++
++	kfree(cfe);
++}
++
++static void cfe_put(struct cfe_device *cfe)
++{
++	kref_put(&cfe->kref, cfe_release);
++}
++
++static void cfe_get(struct cfe_device *cfe)
++{
++	kref_get(&cfe->kref);
++}
++
++static void cfe_node_release(struct video_device *vdev)
++{
++	struct cfe_node *node = video_get_drvdata(vdev);
++
++	cfe_put(node->cfe);
++}
++
++static int cfe_register_node(struct cfe_device *cfe, int id)
++{
++	struct video_device *vdev;
++	const struct cfe_fmt *fmt;
++	struct vb2_queue *q;
++	struct cfe_node *node = &cfe->node[id];
++	int ret;
++
++	node->cfe = cfe;
++	node->id = id;
++
++	if (is_image_output_node(node)) {
++		fmt = find_format_by_code(cfe_default_format.code);
++		if (!fmt) {
++			cfe_err("Failed to find format code\n");
++			return -EINVAL;
++		}
++
++		node->fmt.fmt.pix.pixelformat = fmt->fourcc;
++		v4l2_fill_pix_format(&node->fmt.fmt.pix, &cfe_default_format);
++
++		ret = try_fmt_vid_cap(node, &node->fmt);
++		if (ret)
++			return ret;
++	} else {
++		ret = try_fmt_meta(node, &node->fmt);
++		if (ret)
++			return ret;
++	}
++	node->fmt.type = node_desc[id].buf_type;
++
++	mutex_init(&node->lock);
++
++	q = &node->buffer_queue;
++	q->type = node_desc[id].buf_type;
++	q->io_modes = VB2_MMAP | VB2_DMABUF;
++	q->drv_priv = node;
++	q->ops = &cfe_video_qops;
++	q->mem_ops = &vb2_dma_contig_memops;
++	q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer)
++					     : sizeof(struct cfe_buffer);
++	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++	q->lock = &node->lock;
++	q->min_buffers_needed = 1;
++	q->dev = &cfe->pdev->dev;
++
++	ret = vb2_queue_init(q);
++	if (ret) {
++		cfe_err("vb2_queue_init() failed\n");
++		return ret;
++	}
++
++	INIT_LIST_HEAD(&node->dma_queue);
++
++	vdev = &node->video_dev;
++	vdev->release = cfe_node_release;
++	vdev->fops = &cfe_fops;
++	vdev->ioctl_ops = &cfe_ioctl_ops;
++	vdev->entity.ops = &cfe_media_entity_ops;
++	vdev->v4l2_dev = &cfe->v4l2_dev;
++	vdev->vfl_dir = (is_image_output_node(node) || is_meta_output_node(node))
++				? VFL_DIR_RX : VFL_DIR_TX;
++	vdev->queue = q;
++	vdev->lock = &node->lock;
++	vdev->device_caps = node_desc[id].cap;
++	vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
++
++	/* Define the device names */
++	snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME,
++		 node_desc[id].name);
++
++	video_set_drvdata(vdev, node);
++	if (node->id == FE_OUT0)
++		vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
++	node->pad.flags = node_desc[id].pad_flags;
++	media_entity_pads_init(&vdev->entity, 1, &node->pad);
++
++	if (is_meta_node(node)) {
++		v4l2_disable_ioctl(&node->video_dev,
++				   VIDIOC_ENUM_FRAMEINTERVALS);
++		v4l2_disable_ioctl(&node->video_dev,
++				   VIDIOC_ENUM_FRAMESIZES);
++	}
++
++	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
++	if (ret) {
++		cfe_err("Unable to register video device %s\n", vdev->name);
++		return ret;
++	}
++
++	cfe_info("Registered [%s] node id %d successfully as /dev/video%u\n",
++		 vdev->name, id, vdev->num);
++
++	/*
++	 * Acquire a reference to cfe, which will be released when the video
++	 * device will be unregistered and userspace will have closed all open
++	 * file handles.
++	 */
++	cfe_get(cfe);
++	set_state(cfe, NODE_REGISTERED, id);
++
++	return 0;
++}
++
++static void cfe_unregister_nodes(struct cfe_device *cfe)
++{
++	unsigned int i;
++
++	for (i = 0; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++
++		if (check_state(cfe, NODE_REGISTERED, i)) {
++			clear_state(cfe, NODE_REGISTERED, i);
++			video_unregister_device(&node->video_dev);
++		}
++	}
++}
++
++static int cfe_link_node_pads(struct cfe_device *cfe)
++{
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++		struct cfe_node *node = &cfe->node[i];
++
++		if (!check_state(cfe, NODE_REGISTERED, i))
++			continue;
++
++		if (i < cfe->sensor->entity.num_pads) {
++			/* Sensor -> CSI2 */
++			ret = media_create_pad_link(&cfe->sensor->entity, i,
++						    &cfe->csi2.sd.entity, i,
++						    MEDIA_LNK_FL_IMMUTABLE |
++						    MEDIA_LNK_FL_ENABLED);
++			if (ret)
++				return ret;
++		}
++
++		/* CSI2 channel # -> /dev/video# */
++		ret = media_create_pad_link(&cfe->csi2.sd.entity,
++					    node_desc[i].link_pad,
++					    &node->video_dev.entity, 0, 0);
++		if (ret)
++			return ret;
++
++		if (node->id != CSI2_CH1_EMBEDDED) {
++			/* CSI2 channel # -> FE Input */
++			ret = media_create_pad_link(&cfe->csi2.sd.entity,
++						    node_desc[i].link_pad,
++						    &cfe->fe.sd.entity,
++						    FE_STREAM_PAD, 0);
++			if (ret)
++				return ret;
++		}
++	}
++
++	for (; i < NUM_NODES; i++) {
++		struct cfe_node *node = &cfe->node[i];
++		struct media_entity *src, *dst;
++		unsigned int src_pad, dst_pad;
++
++		if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) {
++			/* FE -> /dev/video# */
++			src = &cfe->fe.sd.entity;
++			src_pad = node_desc[i].link_pad;
++			dst = &node->video_dev.entity;
++			dst_pad = 0;
++		} else {
++			/* /dev/video# -> FE */
++			dst = &cfe->fe.sd.entity;
++			dst_pad = node_desc[i].link_pad;
++			src = &node->video_dev.entity;
++			src_pad = 0;
++		}
++
++		ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0);
++		if (ret)
++			return ret;
++	}
++
++	return 0;
++}
++
++static int cfe_probe_complete(struct cfe_device *cfe)
++{
++	unsigned int i;
++	int ret;
++
++	cfe->v4l2_dev.notify = cfe_notify;
++
++	cfe->sensor_embedded_data = (cfe->sensor->entity.num_pads >= 2);
++
++	for (i = 0; i < NUM_NODES; i++) {
++		ret = cfe_register_node(cfe, i);
++		if (ret) {
++			cfe_err("Unable to register video node %u.\n", i);
++			goto unregister;
++		}
++	}
++
++	ret = cfe_link_node_pads(cfe);
++	if (ret) {
++		cfe_err("Unable to link node pads.\n");
++		goto unregister;
++	}
++
++	ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev);
++	if (ret) {
++		cfe_err("Unable to register subdev nodes.\n");
++		goto unregister;
++	}
++
++	/*
++	 * Release the initial reference, all references are now owned by the
++	 * video devices.
++	 */
++	cfe_put(cfe);
++	return 0;
++
++unregister:
++	cfe_unregister_nodes(cfe);
++	cfe_put(cfe);
++
++	return ret;
++}
++
++static int cfe_async_bound(struct v4l2_async_notifier *notifier,
++			   struct v4l2_subdev *subdev,
++			   struct v4l2_async_subdev *asd)
++{
++	struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
++
++	if (cfe->sensor) {
++		cfe_info("Rejecting subdev %s (Already set!!)", subdev->name);
++		return 0;
++	}
++
++	cfe->sensor = subdev;
++	cfe_info("Using sensor %s for capture\n", subdev->name);
++
++	return 0;
++}
++
++static int cfe_async_complete(struct v4l2_async_notifier *notifier)
++{
++	struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
++
++	return cfe_probe_complete(cfe);
++}
++
++static const struct v4l2_async_notifier_operations cfe_async_ops = {
++	.bound = cfe_async_bound,
++	.complete = cfe_async_complete,
++};
++
++static int of_cfe_connect_subdevs(struct cfe_device *cfe)
++{
++	struct platform_device *pdev = cfe->pdev;
++	struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
++	struct device_node *node = pdev->dev.of_node;
++	struct device_node *ep_node;
++	struct device_node *sensor_node;
++	unsigned int lane;
++	int ret = -EINVAL;
++
++	/* Get the local endpoint and remote device. */
++	ep_node = of_graph_get_next_endpoint(node, NULL);
++	if (!ep_node) {
++		cfe_err("can't get next endpoint\n");
++		return -EINVAL;
++	}
++
++	cfe_dbg("ep_node is %pOF\n", ep_node);
++
++	sensor_node = of_graph_get_remote_port_parent(ep_node);
++	if (!sensor_node) {
++		cfe_err("can't get remote parent\n");
++		goto cleanup_exit;
++	}
++
++	cfe_info("found subdevice %pOF\n", sensor_node);
++
++	/* Parse the local endpoint and validate its configuration. */
++	v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep);
++
++	cfe->csi2.multipacket_line =
++		fwnode_property_present(of_fwnode_handle(ep_node),
++					"multipacket-line");
++
++	if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
++		cfe_err("endpoint node type != CSI2\n");
++		return -EINVAL;
++	}
++
++	for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) {
++		if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
++			cfe_err("subdevice %pOF: data lanes reordering not supported\n",
++				sensor_node);
++			goto cleanup_exit;
++		}
++	}
++
++	/* TODO: Get the frequency from devicetree */
++	cfe->csi2.dphy.dphy_freq = 999;
++	cfe->csi2.dphy.num_lanes = ep.bus.mipi_csi2.num_data_lanes;
++	cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags;
++
++	cfe_dbg("subdevice %pOF: %u data lanes, flags=0x%08x, multipacket_line=%u\n",
++		sensor_node, cfe->csi2.dphy.num_lanes, cfe->csi2.bus_flags,
++		cfe->csi2.multipacket_line);
++
++	/* Initialize and register the async notifier. */
++	v4l2_async_nf_init(&cfe->notifier);
++	cfe->notifier.ops = &cfe_async_ops;
++
++	cfe->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
++	cfe->asd.match.fwnode = of_fwnode_handle(sensor_node);
++	ret = __v4l2_async_nf_add_subdev(&cfe->notifier, &cfe->asd);
++	if (ret) {
++		cfe_err("Error adding subdevice: %d\n", ret);
++		goto cleanup_exit;
++	}
++
++	ret = v4l2_async_nf_register(&cfe->v4l2_dev, &cfe->notifier);
++	if (ret) {
++		cfe_err("Error registering async notifier: %d\n", ret);
++		ret = -EINVAL;
++	}
++
++cleanup_exit:
++	of_node_put(sensor_node);
++	of_node_put(ep_node);
++
++	return ret;
++}
++
++static int cfe_probe(struct platform_device *pdev)
++{
++	struct cfe_device *cfe;
++	char debugfs_name[32];
++	int ret;
++
++	cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
++	if (!cfe)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, cfe);
++
++	kref_init(&cfe->kref);
++	cfe->pdev = pdev;
++	cfe->fe_csi2_channel = -1;
++	spin_lock_init(&cfe->state_lock);
++
++	cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(cfe->csi2.base)) {
++		dev_err(&pdev->dev, "Failed to get dma io block\n");
++		ret = PTR_ERR(cfe->csi2.base);
++		goto err_cfe_put;
++	}
++
++	cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1);
++	if (IS_ERR(cfe->csi2.dphy.base)) {
++		dev_err(&pdev->dev, "Failed to get host io block\n");
++		ret = PTR_ERR(cfe->csi2.dphy.base);
++		goto err_cfe_put;
++	}
++
++	cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2);
++	if (IS_ERR(cfe->mipi_cfg_base)) {
++		dev_err(&pdev->dev, "Failed to get mipi cfg io block\n");
++		ret = PTR_ERR(cfe->mipi_cfg_base);
++		goto err_cfe_put;
++	}
++
++	cfe->fe.base = devm_platform_ioremap_resource(pdev, 3);
++	if (IS_ERR(cfe->fe.base)) {
++		dev_err(&pdev->dev, "Failed to get pisp fe io block\n");
++		ret = PTR_ERR(cfe->fe.base);
++		goto err_cfe_put;
++	}
++
++	ret = platform_get_irq(pdev, 0);
++	if (ret <= 0) {
++		dev_err(&pdev->dev, "No IRQ resource\n");
++		ret = -EINVAL;
++		goto err_cfe_put;
++	}
++
++	ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe);
++	if (ret) {
++		dev_err(&pdev->dev, "Unable to request interrupt\n");
++		ret = -EINVAL;
++		goto err_cfe_put;
++	}
++
++	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
++	if (ret) {
++		dev_err(&pdev->dev, "DMA enable failed\n");
++		goto err_cfe_put;
++	}
++
++	/* TODO: Enable clock only when running. */
++	cfe->clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(cfe->clk))
++		return dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk),
++				     "clock not found\n");
++
++	cfe->mdev.dev = &pdev->dev;
++	cfe->mdev.ops = &cfe_media_device_ops;
++	strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model));
++	strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial));
++	snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s",
++		 dev_name(&pdev->dev));
++
++	media_device_init(&cfe->mdev);
++
++	cfe->v4l2_dev.mdev = &cfe->mdev;
++
++	ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev);
++	if (ret) {
++		cfe_err("Unable to register v4l2 device.\n");
++		goto err_cfe_put;
++	}
++
++	snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s",
++		 dev_name(&pdev->dev));
++	cfe->debugfs = debugfs_create_dir(debugfs_name, NULL);
++	debugfs_create_file("format", 0444, cfe->debugfs, cfe, &format_fops);
++	debugfs_create_file("regs", 0444, cfe->debugfs, cfe,
++			    &mipi_cfg_regs_fops);
++
++	/* Enable the block power domain */
++	pm_runtime_enable(&pdev->dev);
++
++	ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++	if (ret)
++		goto err_runtime_disable;
++
++	cfe->csi2.v4l2_dev = &cfe->v4l2_dev;
++	ret = csi2_init(&cfe->csi2, cfe->debugfs);
++	if (ret) {
++		cfe_err("Failed to init csi2 (%d)\n", ret);
++		goto err_runtime_put;
++	}
++
++	cfe->fe.v4l2_dev = &cfe->v4l2_dev;
++	ret = pisp_fe_init(&cfe->fe, cfe->debugfs);
++	if (ret) {
++		cfe_err("Failed to init pisp fe (%d)\n", ret);
++		goto err_csi2_uninit;
++	}
++
++	cfe->mdev.hw_revision = cfe->fe.hw_revision;
++	ret = media_device_register(&cfe->mdev);
++	if (ret < 0) {
++		cfe_err("Unable to register media-controller device.\n");
++		goto err_pisp_fe_uninit;
++	}
++
++	ret = of_cfe_connect_subdevs(cfe);
++	if (ret) {
++		cfe_err("Failed to connect subdevs\n");
++		goto err_media_unregister;
++	}
++
++	pm_runtime_put(&cfe->pdev->dev);
++
++	return 0;
++
++err_media_unregister:
++	media_device_unregister(&cfe->mdev);
++err_pisp_fe_uninit:
++	pisp_fe_uninit(&cfe->fe);
++err_csi2_uninit:
++	csi2_uninit(&cfe->csi2);
++err_runtime_put:
++	pm_runtime_put(&cfe->pdev->dev);
++err_runtime_disable:
++	pm_runtime_disable(&pdev->dev);
++	debugfs_remove(cfe->debugfs);
++	v4l2_device_unregister(&cfe->v4l2_dev);
++err_cfe_put:
++	cfe_put(cfe);
++
++	return ret;
++}
++
++static int cfe_remove(struct platform_device *pdev)
++{
++	struct cfe_device *cfe = platform_get_drvdata(pdev);
++
++	debugfs_remove(cfe->debugfs);
++
++	v4l2_async_nf_unregister(&cfe->notifier);
++	media_device_unregister(&cfe->mdev);
++	cfe_unregister_nodes(cfe);
++
++	pisp_fe_uninit(&cfe->fe);
++	csi2_uninit(&cfe->csi2);
++
++	pm_runtime_disable(&pdev->dev);
++
++	v4l2_device_unregister(&cfe->v4l2_dev);
++
++	return 0;
++}
++
++static int cfe_runtime_suspend(struct device *dev)
++{
++	struct platform_device *pdev = to_platform_device(dev);
++	struct cfe_device *cfe = platform_get_drvdata(pdev);
++
++	clk_disable_unprepare(cfe->clk);
++
++	return 0;
++}
++
++static int cfe_runtime_resume(struct device *dev)
++{
++	struct platform_device *pdev = to_platform_device(dev);
++	struct cfe_device *cfe = platform_get_drvdata(pdev);
++	int ret;
++
++	ret = clk_prepare_enable(cfe->clk);
++	if (ret) {
++		dev_err(dev, "Unable to enable clock\n");
++		return ret;
++	}
++
++	return 0;
++}
++
++static const struct dev_pm_ops cfe_pm_ops = {
++	SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL)
++	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
++};
++
++static const struct of_device_id cfe_of_match[] = {
++	{ .compatible = "raspberrypi,rp1-cfe" },
++	{ /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, cfe_of_match);
++
++static struct platform_driver cfe_driver = {
++	.probe		= cfe_probe,
++	.remove		= cfe_remove,
++	.driver = {
++		.name	= CFE_MODULE_NAME,
++		.of_match_table = cfe_of_match,
++		.pm = &cfe_pm_ops,
++	},
++};
++
++module_platform_driver(cfe_driver);
++
++MODULE_AUTHOR("Naushir Patuck <[email protected]>");
++MODULE_DESCRIPTION("RP1 Camera Front End driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(CFE_VERSION);
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -0,0 +1,40 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 CFE driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _RP1_CFE_
++#define _RP1_CFE_
++
++#include <linux/types.h>
++#include <linux/media-bus-format.h>
++#include <linux/videodev2.h>
++
++extern bool cfe_debug_irq;
++
++enum cfe_remap_types {
++	CFE_REMAP_16BIT,
++	CFE_REMAP_COMPRESSED,
++	CFE_NUM_REMAP,
++};
++
++#define CFE_FORMAT_FLAG_META_OUT	BIT(0)
++#define CFE_FORMAT_FLAG_META_CAP	BIT(1)
++#define CFE_FORMAT_FLAG_FE_OUT		BIT(2)
++
++struct cfe_fmt {
++	u32 fourcc;
++	u32 code;
++	u8 depth;
++	u8 csi_dt;
++	u32 remap[CFE_NUM_REMAP];
++	u32 flags;
++};
++
++extern const struct v4l2_mbus_framefmt cfe_default_format;
++extern const struct v4l2_mbus_framefmt cfe_default_meta_format;
++
++const struct cfe_fmt *find_format_by_code(u32 code);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -0,0 +1,294 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 Camera Front End formats definition
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _CFE_FMTS_H_
++#define _CFE_FMTS_H_
++
++#include "cfe.h"
++
++static const struct cfe_fmt formats[] = {
++	/* YUV Formats */
++	{
++		.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,
++		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
++		.depth = 10,
++		.csi_dt = 0x2b,
++		.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGBRG10P,
++		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
++		.depth = 10,
++		.csi_dt = 0x2b,
++		.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGRBG10P,
++		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
++		.depth = 10,
++		.csi_dt = 0x2b,
++		.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SRGGB10P,
++		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
++		.depth = 10,
++		.csi_dt = 0x2b,
++		.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SBGGR12P,
++		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
++		.depth = 12,
++		.csi_dt = 0x2c,
++		.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGBRG12P,
++		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
++		.depth = 12,
++		.csi_dt = 0x2c,
++		.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGRBG12P,
++		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
++		.depth = 12,
++		.csi_dt = 0x2c,
++		.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SRGGB12P,
++		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
++		.depth = 12,
++		.csi_dt = 0x2c,
++		.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SBGGR14P,
++		.code = MEDIA_BUS_FMT_SBGGR14_1X14,
++		.depth = 14,
++		.csi_dt = 0x2d,
++		.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGBRG14P,
++		.code = MEDIA_BUS_FMT_SGBRG14_1X14,
++		.depth = 14,
++		.csi_dt = 0x2d,
++		.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGRBG14P,
++		.code = MEDIA_BUS_FMT_SGRBG14_1X14,
++		.depth = 14,
++		.csi_dt = 0x2d,
++		.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SRGGB14P,
++		.code = MEDIA_BUS_FMT_SRGGB14_1X14,
++		.depth = 14,
++		.csi_dt = 0x2d,
++		.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SBGGR16,
++		.code = MEDIA_BUS_FMT_SBGGR16_1X16,
++		.depth = 16,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGBRG16,
++		.code = MEDIA_BUS_FMT_SGBRG16_1X16,
++		.depth = 16,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SGRBG16,
++		.code = MEDIA_BUS_FMT_SGRBG16_1X16,
++		.depth = 16,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_SRGGB16,
++		.code = MEDIA_BUS_FMT_SRGGB16_1X16,
++		.depth = 16,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	/* PiSP Compressed Mode 1 */
++	{
++		.fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
++		.code = MEDIA_BUS_FMT_PISP_COMP1_RGGB,
++		.depth = 8,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
++		.code = MEDIA_BUS_FMT_PISP_COMP1_BGGR,
++		.depth = 8,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
++		.code = MEDIA_BUS_FMT_PISP_COMP1_GBRG,
++		.depth = 8,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
++		.code = MEDIA_BUS_FMT_PISP_COMP1_GRBG,
++		.depth = 8,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++	/* Greyscale format */
++	{
++		.fourcc = V4L2_PIX_FMT_GREY,
++		.code = MEDIA_BUS_FMT_Y8_1X8,
++		.depth = 8,
++		.csi_dt = 0x2a,
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_Y10P,
++		.code = MEDIA_BUS_FMT_Y10_1X10,
++		.depth = 10,
++		.csi_dt = 0x2b,
++		.remap = { V4L2_PIX_FMT_Y16 },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_Y12P,
++		.code = MEDIA_BUS_FMT_Y12_1X12,
++		.depth = 12,
++		.csi_dt = 0x2c,
++		.remap = { V4L2_PIX_FMT_Y16 },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_Y14P,
++		.code = MEDIA_BUS_FMT_Y14_1X14,
++		.depth = 14,
++		.csi_dt = 0x2d,
++		.remap = { V4L2_PIX_FMT_Y16 },
++	},
++	{
++		.fourcc = V4L2_PIX_FMT_Y16,
++		.depth = 16,
++		.flags = CFE_FORMAT_FLAG_FE_OUT,
++	},
++
++	/* Embedded data format */
++	{
++		.fourcc = V4L2_META_FMT_SENSOR_DATA,
++		.code = MEDIA_BUS_FMT_SENSOR_DATA,
++		.depth = 8,
++		.csi_dt = 0x12,
++		.flags = CFE_FORMAT_FLAG_META_CAP,
++	},
++
++	/* Frontend formats */
++	{
++		.fourcc = V4L2_META_FMT_RPI_FE_CFG,
++		.flags = CFE_FORMAT_FLAG_META_OUT,
++	},
++	{
++		.fourcc = V4L2_META_FMT_RPI_FE_STATS,
++		.flags = CFE_FORMAT_FLAG_META_CAP,
++	},
++};
++
++#endif /* _CFE_FMTS_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -0,0 +1,446 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 CSI-2 Driver
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "csi2.h"
++#include "cfe.h"
++
++#define csi2_dbg_irq(fmt, arg...)                                 \
++	do {                                                      \
++		if (cfe_debug_irq)                                \
++			dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \
++	} while (0)
++#define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg)
++#define csi2_info(fmt, arg...) dev_info(csi2->v4l2_dev->dev, fmt, ##arg)
++#define csi2_err(fmt, arg...) dev_err(csi2->v4l2_dev->dev, fmt, ##arg)
++
++/* CSI2-DMA registers */
++#define CSI2_STATUS		0x000
++#define CSI2_QOS		0x004
++#define CSI2_DISCARDS_OVERFLOW	0x008
++#define CSI2_DISCARDS_INACTIVE	0x00c
++#define CSI2_DISCARDS_UNMATCHED	0x010
++#define CSI2_DISCARDS_LEN_LIMIT	0x014
++#define CSI2_LLEV_PANICS	0x018
++#define CSI2_ULEV_PANICS	0x01c
++#define CSI2_IRQ_MASK		0x020
++#define CSI2_CTRL		0x024
++#define CSI2_CH_CTRL(x)		((x) * 0x40 + 0x28)
++#define CSI2_CH_ADDR0(x)	((x) * 0x40 + 0x2c)
++#define CSI2_CH_ADDR1(x)	((x) * 0x40 + 0x3c)
++#define CSI2_CH_STRIDE(x)	((x) * 0x40 + 0x30)
++#define CSI2_CH_LENGTH(x)	((x) * 0x40 + 0x34)
++#define CSI2_CH_DEBUG(x)	((x) * 0x40 + 0x38)
++#define CSI2_CH_FRAME_SIZE(x)	((x) * 0x40 + 0x40)
++#define CSI2_CH_COMP_CTRL(x)	((x) * 0x40 + 0x44)
++#define CSI2_CH_FE_FRAME_ID(x)	((x) * 0x40 + 0x48)
++
++/* CSI2_STATUS */
++#define IRQ_FS(x)		(BIT(0) << (x))
++#define IRQ_FE(x)		(BIT(4) << (x))
++#define IRQ_FE_ACK(x)		(BIT(8) << (x))
++#define IRQ_LE(x)		(BIT(12) << (x))
++#define IRQ_LE_ACK(x)		(BIT(16) << (x))
++#define IRQ_CH_MASK(x)		(IRQ_FS(x) | IRQ_FE(x) | IRQ_FE_ACK(x) | IRQ_LE(x) | IRQ_LE_ACK(x))
++#define IRQ_OVERFLOW		BIT(20)
++#define IRQ_DISCARD_OVERFLOW	BIT(21)
++#define IRQ_DISCARD_LEN_LIMIT	BIT(22)
++#define IRQ_DISCARD_UNMATCHED	BIT(23)
++#define IRQ_DISCARD_INACTIVE	BIT(24)
++
++/* CSI2_CTRL */
++#define EOP_IS_EOL		BIT(0)
++
++/* CSI2_CH_CTRL */
++#define DMA_EN			BIT(0)
++#define FORCE			BIT(3)
++#define AUTO_ARM		BIT(4)
++#define IRQ_EN_FS		BIT(13)
++#define IRQ_EN_FE		BIT(14)
++#define IRQ_EN_FE_ACK		BIT(15)
++#define IRQ_EN_LE		BIT(16)
++#define IRQ_EN_LE_ACK		BIT(17)
++#define FLUSH_FE		BIT(28)
++#define PACK_LINE		BIT(29)
++#define PACK_BYTES		BIT(30)
++#define CH_MODE_MASK		GENMASK(2, 1)
++#define VC_MASK			GENMASK(6, 5)
++#define DT_MASK			GENMASK(12, 7)
++#define LC_MASK			GENMASK(27, 18)
++
++/* CHx_COMPRESSION_CONTROL */
++#define COMP_OFFSET_MASK	GENMASK(15, 0)
++#define COMP_SHIFT_MASK		GENMASK(19, 16)
++#define COMP_MODE_MASK		GENMASK(25, 24)
++
++static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset)
++{
++	return readl(csi2->base + offset);
++}
++
++static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
++{
++	writel(val, csi2->base + offset);
++}
++
++static inline void set_field(u32 *valp, u32 field, u32 mask)
++{
++	u32 val = *valp;
++
++	val &= ~mask;
++	val |= (field << __ffs(mask)) & mask;
++	*valp = val;
++}
++
++static int csi2_regs_show(struct seq_file *s, void *data)
++{
++	struct csi2_device *csi2 = s->private;
++	unsigned int i;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev);
++	if (ret)
++		return ret;
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg))
++#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, csi2_reg_read(csi2, reg(idx)))
++
++	DUMP(CSI2_STATUS);
++	DUMP(CSI2_DISCARDS_OVERFLOW);
++	DUMP(CSI2_DISCARDS_INACTIVE);
++	DUMP(CSI2_DISCARDS_UNMATCHED);
++	DUMP(CSI2_DISCARDS_LEN_LIMIT);
++	DUMP(CSI2_LLEV_PANICS);
++	DUMP(CSI2_ULEV_PANICS);
++	DUMP(CSI2_IRQ_MASK);
++	DUMP(CSI2_CTRL);
++
++	for (i = 0; i < CSI2_NUM_CHANNELS; ++i) {
++		DUMP_CH(i, CSI2_CH_CTRL);
++		DUMP_CH(i, CSI2_CH_ADDR0);
++		DUMP_CH(i, CSI2_CH_ADDR1);
++		DUMP_CH(i, CSI2_CH_STRIDE);
++		DUMP_CH(i, CSI2_CH_LENGTH);
++		DUMP_CH(i, CSI2_CH_DEBUG);
++		DUMP_CH(i, CSI2_CH_FRAME_SIZE);
++		DUMP_CH(i, CSI2_CH_COMP_CTRL);
++		DUMP_CH(i, CSI2_CH_FE_FRAME_ID);
++	}
++
++#undef DUMP
++#undef DUMP_CH
++
++	pm_runtime_put(csi2->v4l2_dev->dev);
++
++	return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(csi2_regs);
++
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
++{
++	unsigned int i;
++	u32 status;
++
++	status = csi2_reg_read(csi2, CSI2_STATUS);
++	csi2_dbg_irq("ISR: STA: 0x%x\n", status);
++
++	/* Write value back to clear the interrupts */
++	csi2_reg_write(csi2, CSI2_STATUS, status);
++
++	for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++		u32 dbg;
++
++		if ((status & IRQ_CH_MASK(i)) == 0)
++			continue;
++
++		dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
++
++		csi2_dbg_irq("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n", i,
++			     (status & IRQ_FS(i)) ? "FS " : "",
++			     (status & IRQ_FE(i)) ? "FE " : "",
++			     (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "",
++			     (status & IRQ_LE(i)) ? "LE " : "",
++			     (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "",
++			     dbg >> 16,
++			     csi2->num_lines[i] ?
++				     ((dbg & 0xffff) % csi2->num_lines[i]) :
++				     0);
++
++		sof[i] = !!(status & IRQ_FS(i));
++		eof[i] = !!(status & IRQ_FE_ACK(i));
++		lci[i] = !!(status & IRQ_LE_ACK(i));
++	}
++}
++
++void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
++		     dma_addr_t dmaaddr, unsigned int stride, unsigned int size)
++{
++	u64 addr = dmaaddr;
++	/*
++	 * ADDRESS0 must be written last as it triggers the double buffering
++	 * mechanism for all buffer registers within the hardware.
++	 */
++	addr >>= 4;
++	csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4);
++	csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4);
++	csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32);
++	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff);
++}
++
++void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
++			  enum csi2_compression_mode mode, unsigned int shift,
++			  unsigned int offset)
++{
++	u32 compression = 0;
++
++	set_field(&compression, COMP_OFFSET_MASK, offset);
++	set_field(&compression, COMP_SHIFT_MASK, shift);
++	set_field(&compression, COMP_MODE_MASK, mode);
++	csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
++}
++
++void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
++			u16 dt, enum csi2_mode mode, bool auto_arm,
++			bool pack_bytes, unsigned int width,
++			unsigned int height)
++{
++	u32 ctrl;
++
++	csi2_dbg("%s [%u]\n", __func__, channel);
++
++	/*
++	 * Disable the channel, but ensure N != 0!  Otherwise we end up with a
++	 * spurious LE + LE_ACK interrupt when re-enabling the channel.
++	 */
++	csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0x100 << __ffs(LC_MASK));
++	csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
++	csi2_reg_write(csi2, CSI2_STATUS, IRQ_CH_MASK(channel));
++
++	/* Enable channel and FS/FE/LE interrupts. */
++	ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | IRQ_EN_LE_ACK | PACK_LINE;
++	/* PACK_BYTES ensures no striding for embedded data. */
++	if (pack_bytes)
++		ctrl |= PACK_BYTES;
++
++	if (auto_arm)
++		ctrl |= AUTO_ARM;
++
++	if (width && height) {
++		int line_int_freq = height >> 2;
++
++		line_int_freq = min(max(0x80, line_int_freq), 0x3ff);
++		set_field(&ctrl, line_int_freq, LC_MASK);
++		set_field(&ctrl, mode, CH_MODE_MASK);
++		csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
++			       (height << 16) | width);
++	} else {
++		/*
++		 * Do not disable line interrupts for the embedded data channel,
++		 * set it to the maximum value.  This avoids spamming the ISR
++		 * with spurious line interrupts.
++		 */
++		set_field(&ctrl, 0x3ff, LC_MASK);
++		set_field(&ctrl, 0x00, CH_MODE_MASK);
++	}
++
++	set_field(&ctrl, dt, DT_MASK);
++	csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
++	csi2->num_lines[channel] = height;
++}
++
++void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel)
++{
++	csi2_dbg("%s [%u]\n", __func__, channel);
++
++	/* Channel disable.  Use FORCE to allow stopping mid-frame. */
++	csi2_reg_write(csi2, CSI2_CH_CTRL(channel),
++		       (0x100 << __ffs(LC_MASK)) | FORCE);
++	/* Latch the above change by writing to the ADDR0 register. */
++	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
++	/* Write this again, the HW needs it! */
++	csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
++}
++
++void csi2_open_rx(struct csi2_device *csi2)
++{
++	dphy_start(&csi2->dphy);
++
++	if (!csi2->multipacket_line)
++		csi2_reg_write(csi2, CSI2_CTRL, EOP_IS_EOL);
++}
++
++void csi2_close_rx(struct csi2_device *csi2)
++{
++	dphy_stop(&csi2->dphy);
++}
++
++static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
++{
++	return container_of(subdev, struct csi2_device, sd);
++}
++
++static int csi2_init_cfg(struct v4l2_subdev *sd,
++			 struct v4l2_subdev_state *state)
++{
++	struct v4l2_mbus_framefmt *fmt;
++
++	for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) {
++		const struct v4l2_mbus_framefmt *def_fmt;
++
++		/* CSI2_CH1_EMBEDDED */
++		if (i == 1)
++			def_fmt = &cfe_default_meta_format;
++		else
++			def_fmt = &cfe_default_format;
++
++		fmt = v4l2_subdev_get_pad_format(sd, state, i);
++		*fmt = *def_fmt;
++
++		fmt = v4l2_subdev_get_pad_format(sd, state, i + CSI2_NUM_CHANNELS);
++		*fmt = *def_fmt;
++	}
++
++	return 0;
++}
++
++static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
++			    struct v4l2_subdev_state *state,
++			    struct v4l2_subdev_format *format)
++{
++	struct v4l2_mbus_framefmt *fmt;
++	const struct cfe_fmt *cfe_fmt;
++
++	/* TODO: format validation */
++
++	cfe_fmt = find_format_by_code(format->format.code);
++	if (!cfe_fmt)
++		cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++	format->format.code = cfe_fmt->code;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++	*fmt = format->format;
++
++	if (format->pad < CSI2_NUM_CHANNELS) {
++		/* Propagate to the source pad */
++		fmt = v4l2_subdev_get_pad_format(sd, state,
++						 format->pad + CSI2_NUM_CHANNELS);
++		*fmt = format->format;
++	}
++
++	return 0;
++}
++
++static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link,
++			      struct v4l2_subdev_format *source_fmt,
++			      struct v4l2_subdev_format *sink_fmt)
++{
++	struct csi2_device *csi2 = to_csi2_device(sd);
++
++	csi2_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
++		 link->source->entity->name, link->source->index,
++		 link->sink->entity->name, link->sink->index);
++
++	if ((link->source->entity == &csi2->sd.entity &&
++	     link->source->index == 1) ||
++	    (link->sink->entity == &csi2->sd.entity &&
++	     link->sink->index == 1)) {
++		csi2_dbg("Ignore metadata pad for now\n");
++		return 0;
++	}
++
++	/* The width, height and code must match. */
++	if (source_fmt->format.width != sink_fmt->format.width ||
++	    source_fmt->format.width != sink_fmt->format.width ||
++	    source_fmt->format.code != sink_fmt->format.code) {
++		csi2_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
++			 __func__,
++			 source_fmt->format.width, source_fmt->format.height,
++			 source_fmt->format.code,
++			 sink_fmt->format.width, sink_fmt->format.height,
++			 sink_fmt->format.code);
++		return -EPIPE;
++	}
++
++	return 0;
++}
++
++static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
++	.init_cfg = csi2_init_cfg,
++	.get_fmt = v4l2_subdev_get_fmt,
++	.set_fmt = csi2_pad_set_fmt,
++	.link_validate = csi2_link_validate,
++};
++
++static const struct media_entity_operations csi2_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_ops csi2_subdev_ops = {
++	.pad = &csi2_subdev_pad_ops,
++};
++
++int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
++{
++	unsigned int i, ret;
++
++	csi2->dphy.dev = csi2->v4l2_dev->dev;
++	dphy_probe(&csi2->dphy);
++
++	debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);
++
++	for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++)
++		csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ?
++				     MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
++
++	ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),
++				     csi2->pad);
++	if (ret)
++		return ret;
++
++	/* Initialize subdev */
++	v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
++	csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++	csi2->sd.entity.ops = &csi2_entity_ops;
++	csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
++	csi2->sd.owner = THIS_MODULE;
++	snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");
++
++	ret = v4l2_subdev_init_finalize(&csi2->sd);
++	if (ret)
++		goto err_entity_cleanup;
++
++	ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);
++	if (ret) {
++		csi2_err("Failed register csi2 subdev (%d)\n", ret);
++		goto err_subdev_cleanup;
++	}
++
++	return 0;
++
++err_subdev_cleanup:
++	v4l2_subdev_cleanup(&csi2->sd);
++err_entity_cleanup:
++	media_entity_cleanup(&csi2->sd.entity);
++
++	return ret;
++}
++
++void csi2_uninit(struct csi2_device *csi2)
++{
++	v4l2_device_unregister_subdev(&csi2->sd);
++	v4l2_subdev_cleanup(&csi2->sd);
++	media_entity_cleanup(&csi2->sd.entity);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -0,0 +1,75 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 CSI-2 driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _RP1_CSI2_
++#define _RP1_CSI2_
++
++#include <linux/debugfs.h>
++#include <linux/io.h>
++#include <linux/types.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++
++#include "dphy.h"
++
++#define CSI2_NUM_CHANNELS 4
++
++enum csi2_mode {
++	CSI2_MODE_NORMAL,
++	CSI2_MODE_REMAP,
++	CSI2_MODE_COMPRESSED,
++	CSI2_MODE_FE_STREAMING
++};
++
++enum csi2_compression_mode {
++	CSI2_COMPRESSION_DELTA = 1,
++	CSI2_COMPRESSION_SIMPLE = 2,
++	CSI2_COMPRESSION_COMBINED = 3,
++};
++
++struct csi2_cfg {
++	u16 width;
++	u16 height;
++	u32 stride;
++	u32 buffer_size;
++};
++
++struct csi2_device {
++	/* Parent V4l2 device */
++	struct v4l2_device *v4l2_dev;
++
++	void __iomem *base;
++
++	struct dphy_data dphy;
++
++	enum v4l2_mbus_type bus_type;
++	unsigned int bus_flags;
++	u32 active_data_lanes;
++	bool multipacket_line;
++	unsigned int num_lines[CSI2_NUM_CHANNELS];
++
++	struct media_pad pad[CSI2_NUM_CHANNELS * 2];
++	struct v4l2_subdev sd;
++};
++
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
++void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
++		     dma_addr_t dmaaddr, unsigned int stride,
++		     unsigned int size);
++void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
++			  enum csi2_compression_mode mode, unsigned int shift,
++			  unsigned int offset);
++void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
++			u16 dt, enum csi2_mode mode, bool auto_arm,
++			bool pack_bytes, unsigned int width,
++			unsigned int height);
++void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
++void csi2_open_rx(struct csi2_device *csi2);
++void csi2_close_rx(struct csi2_device *csi2);
++int csi2_init(struct csi2_device *csi2, struct dentry *debugfs);
++void csi2_uninit(struct csi2_device *csi2);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 CSI-2 Driver
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/dev_printk.h>
++#include <linux/pm_runtime.h>
++
++#include "dphy.h"
++
++#define dphy_dbg(fmt, arg...) dev_dbg(dphy->dev, fmt, ##arg)
++#define dphy_info(fmt, arg...) dev_info(dphy->dev, fmt, ##arg)
++#define dphy_err(fmt, arg...) dev_err(dphy->dev, fmt, ##arg)
++
++/* DW dphy Host registers */
++#define VERSION		0x000
++#define N_LANES		0x004
++#define RESETN		0x008
++#define PHY_SHUTDOWNZ	0x040
++#define PHY_RSTZ	0x044
++#define PHY_RX		0x048
++#define	PHY_STOPSTATE	0x04c
++#define PHY_TST_CTRL0	0x050
++#define PHY_TST_CTRL1	0x054
++#define PHY2_TST_CTRL0	0x058
++#define PHY2_TST_CTRL1	0x05c
++
++/* DW dphy Host Transactions */
++#define DPHY_HS_RX_CTRL_LANE0_OFFSET	0x44
++#define DPHY_PLL_INPUT_DIV_OFFSET	0x17
++#define DPHY_PLL_LOOP_DIV_OFFSET	0x18
++#define DPHY_PLL_DIV_CTRL_OFFSET	0x19
++
++static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset)
++{
++	return readl(dphy->base + offset);
++}
++
++static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
++{
++	writel(data, dphy->base + offset);
++}
++
++static void set_tstclr(struct dphy_data *dphy, u32 val)
++{
++	u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0);
++
++	dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~1) | val);
++}
++
++static void set_tstclk(struct dphy_data *dphy, u32 val)
++{
++	u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0);
++
++	dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1));
++}
++
++static uint8_t get_tstdout(struct dphy_data *dphy)
++{
++	u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++	return ((ctrl1 >> 8) & 0xff);
++}
++
++static void set_testen(struct dphy_data *dphy, u32 val)
++{
++	u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++	dw_csi2_host_write(dphy, PHY_TST_CTRL1,
++			   (ctrl1 & ~(1 << 16)) | (val << 16));
++}
++
++static void set_testdin(struct dphy_data *dphy, u32 val)
++{
++	u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++	dw_csi2_host_write(dphy, PHY_TST_CTRL1, (ctrl1 & ~0xff) | val);
++}
++
++static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code,
++				uint8_t test_data)
++{
++	/* See page 101 of the MIPI DPHY databook. */
++	set_tstclk(dphy, 1);
++	set_testen(dphy, 0);
++	set_testdin(dphy, test_code);
++	set_testen(dphy, 1);
++	set_tstclk(dphy, 0);
++	set_testen(dphy, 0);
++	set_testdin(dphy, test_data);
++	set_tstclk(dphy, 1);
++	return get_tstdout(dphy);
++}
++
++static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t freq_mhz)
++{
++	/* See Table 5-1 on page 65 of dphy databook */
++	static const u16 hsfreqrange_table[][2] = {
++		{ 89, 0b000000 },   { 99, 0b010000 },	{ 109, 0b100000 },
++		{ 129, 0b000001 },  { 139, 0b010001 },	{ 149, 0b100001 },
++		{ 169, 0b000010 },  { 179, 0b010010 },	{ 199, 0b100010 },
++		{ 219, 0b000011 },  { 239, 0b010011 },	{ 249, 0b100011 },
++		{ 269, 0b000100 },  { 299, 0b010100 },	{ 329, 0b000101 },
++		{ 359, 0b010101 },  { 399, 0b100101 },	{ 449, 0b000110 },
++		{ 499, 0b010110 },  { 549, 0b000111 },	{ 599, 0b010111 },
++		{ 649, 0b001000 },  { 699, 0b011000 },	{ 749, 0b001001 },
++		{ 799, 0b011001 },  { 849, 0b101001 },	{ 899, 0b111001 },
++		{ 949, 0b001010 },  { 999, 0b011010 },	{ 1049, 0b101010 },
++		{ 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 },
++		{ 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 },
++		{ 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 },
++	};
++	unsigned int i;
++
++	if (freq_mhz < 80 || freq_mhz > 1500)
++		dphy_err("DPHY: Frequency %u MHz out of range\n", freq_mhz);
++
++	for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
++		if (freq_mhz <= hsfreqrange_table[i][0])
++			break;
++	}
++
++	dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET,
++			 hsfreqrange_table[i][1] << 1);
++}
++
++static void dphy_init(struct dphy_data *dphy)
++{
++	dw_csi2_host_write(dphy, PHY_RSTZ, 0);
++	dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 0);
++	set_tstclk(dphy, 1);
++	set_testen(dphy, 0);
++	set_tstclr(dphy, 1);
++	usleep_range(15, 20);
++	set_tstclr(dphy, 0);
++	usleep_range(15, 20);
++
++	dphy_set_hsfreqrange(dphy, dphy->dphy_freq);
++
++	usleep_range(5, 10);
++	dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1);
++	usleep_range(5, 10);
++	dw_csi2_host_write(dphy, PHY_RSTZ, 1);
++}
++
++void dphy_start(struct dphy_data *dphy)
++{
++	dw_csi2_host_write(dphy, N_LANES, (dphy->num_lanes - 1));
++	dphy_init(dphy);
++	dw_csi2_host_write(dphy, RESETN, 0xffffffff);
++	usleep_range(10, 50);
++}
++
++void dphy_stop(struct dphy_data *dphy)
++{
++	/* Set only one lane (lane 0) as active (ON) */
++	dw_csi2_host_write(dphy, N_LANES, 0);
++	dw_csi2_host_write(dphy, RESETN, 0);
++}
++
++void dphy_probe(struct dphy_data *dphy)
++{
++	u32 host_ver;
++	u8 host_ver_major, host_ver_minor;
++
++	host_ver = dw_csi2_host_read(dphy, VERSION);
++	host_ver_major = (u8)((host_ver >> 24) - '0');
++	host_ver_minor = (u8)((host_ver >> 16) - '0');
++	host_ver_minor = host_ver_minor * 10;
++	host_ver_minor += (u8)((host_ver >> 8) - '0');
++
++	dphy_info("DW dphy Host HW v%u.%u\n", host_ver_major, host_ver_minor);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+@@ -0,0 +1,26 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++
++#ifndef _RP1_DPHY_
++#define _RP1_DPHY_
++
++#include <linux/io.h>
++#include <linux/types.h>
++
++struct dphy_data {
++	struct device *dev;
++
++	void __iomem *base;
++
++	u32 dphy_freq;
++	u32 num_lanes;
++};
++
++void dphy_probe(struct dphy_data *dphy);
++void dphy_start(struct dphy_data *dphy);
++void dphy_stop(struct dphy_data *dphy);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+@@ -0,0 +1,69 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP common definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_COMMON_H_
++#define _PISP_COMMON_H_
++
++#include "pisp_types.h"
++
++struct pisp_bla_config {
++	u16 black_level_r;
++	u16 black_level_gr;
++	u16 black_level_gb;
++	u16 black_level_b;
++	u16 output_black_level;
++	u8 pad[2];
++};
++
++struct pisp_wbg_config {
++	u16 gain_r;
++	u16 gain_g;
++	u16 gain_b;
++	u8 pad[2];
++};
++
++struct pisp_compress_config {
++	/* value subtracted from incoming data */
++	u16 offset;
++	u8 pad;
++	/* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++	u8 mode;
++};
++
++struct pisp_decompress_config {
++	/* value added to reconstructed data */
++	u16 offset;
++	u8 pad;
++	/* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++	u8 mode;
++};
++
++enum pisp_axi_flags {
++	/*
++	 * round down bursts to end at a 32-byte boundary, to align following
++	 * bursts
++	 */
++	PISP_AXI_FLAG_ALIGN = 128,
++	/* for FE writer: force WSTRB high, to pad output to 16-byte boundary */
++	PISP_AXI_FLAG_PAD = 64,
++	/* for FE writer: Use Output FIFO level to trigger "panic" */
++	PISP_AXI_FLAG_PANIC = 32,
++};
++
++struct pisp_axi_config {
++	/*
++	 * burst length minus one, which must be in the range 0:15; OR'd with
++	 * flags
++	 */
++	u8 maxlen_flags;
++	/* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */
++	u8 cache_prot;
++	/* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */
++	u16 qos;
++};
++
++#endif /* _PISP_COMMON_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -0,0 +1,563 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PiSP Front End driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/bitops.h>
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "pisp_fe.h"
++#include "cfe.h"
++
++#define FE_VERSION		0x000
++#define FE_CONTROL		0x004
++#define FE_STATUS		0x008
++#define FE_FRAME_STATUS		0x00c
++#define FE_ERROR_STATUS		0x010
++#define FE_OUTPUT_STATUS	0x014
++#define FE_INT_EN		0x018
++#define FE_INT_STATUS		0x01c
++
++/* CONTROL */
++#define FE_CONTROL_QUEUE	BIT(0)
++#define FE_CONTROL_ABORT	BIT(1)
++#define FE_CONTROL_RESET	BIT(2)
++#define FE_CONTROL_LATCH_REGS	BIT(3)
++
++/* INT_EN / INT_STATUS */
++#define FE_INT_EOF		BIT(0)
++#define FE_INT_SOF		BIT(1)
++#define FE_INT_LINES0		BIT(8)
++#define FE_INT_LINES1		BIT(9)
++#define FE_INT_STATS		BIT(16)
++#define FE_INT_QREADY		BIT(24)
++
++/* STATUS */
++#define FE_STATUS_QUEUED	BIT(0)
++#define FE_STATUS_WAITING	BIT(1)
++#define FE_STATUS_ACTIVE	BIT(2)
++
++#define PISP_FE_CONFIG_BASE_OFFSET	0x0040
++
++#define PISP_FE_ENABLE_STATS_CLUSTER \
++	(PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE    | \
++	 PISP_FE_ENABLE_BLC        | PISP_FE_ENABLE_CDAF_STATS  | \
++	 PISP_FE_ENABLE_AWB_STATS  | PISP_FE_ENABLE_RGBY        | \
++	 PISP_FE_ENABLE_LSC        | PISP_FE_ENABLE_AGC_STATS)
++
++#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i)				\
++	((PISP_FE_ENABLE_CROP0     | PISP_FE_ENABLE_DOWNSCALE0 |	\
++	  PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i)))
++
++struct pisp_fe_config_param {
++	u32 dirty_flags;
++	u32 dirty_flags_extra;
++	size_t offset;
++	size_t size;
++};
++
++static const struct pisp_fe_config_param pisp_fe_config_map[] = {
++	/* *_dirty_flag_extra types */
++	{ 0, PISP_FE_DIRTY_GLOBAL,     offsetof(struct pisp_fe_config, global),
++					sizeof(struct pisp_fe_global_config)         },
++	{ 0, PISP_FE_DIRTY_FLOATING,   offsetof(struct pisp_fe_config, floating_stats),
++					sizeof(struct pisp_fe_floating_stats_config) },
++	{ 0, PISP_FE_DIRTY_OUTPUT_AXI, offsetof(struct pisp_fe_config, output_axi),
++					sizeof(struct pisp_fe_output_axi_config)     },
++	/* *_dirty_flag types */
++	{ PISP_FE_ENABLE_INPUT,      0, offsetof(struct pisp_fe_config, input),
++					sizeof(struct pisp_fe_input_config)          },
++	{ PISP_FE_ENABLE_DECOMPRESS, 0, offsetof(struct pisp_fe_config, decompress),
++					sizeof(struct pisp_decompress_config)        },
++	{ PISP_FE_ENABLE_DECOMPAND,  0, offsetof(struct pisp_fe_config, decompand),
++					sizeof(struct pisp_fe_decompand_config)      },
++	{ PISP_FE_ENABLE_BLA,        0, offsetof(struct pisp_fe_config, bla),
++					sizeof(struct pisp_bla_config)               },
++	{ PISP_FE_ENABLE_DPC,        0, offsetof(struct pisp_fe_config, dpc),
++					sizeof(struct pisp_fe_dpc_config)            },
++	{ PISP_FE_ENABLE_STATS_CROP, 0, offsetof(struct pisp_fe_config, stats_crop),
++					sizeof(struct pisp_fe_crop_config)           },
++	{ PISP_FE_ENABLE_BLC,	     0, offsetof(struct pisp_fe_config, blc),
++					sizeof(struct pisp_bla_config)               },
++	{ PISP_FE_ENABLE_CDAF_STATS, 0, offsetof(struct pisp_fe_config, cdaf_stats),
++					sizeof(struct pisp_fe_cdaf_stats_config)     },
++	{ PISP_FE_ENABLE_AWB_STATS,  0, offsetof(struct pisp_fe_config, awb_stats),
++					sizeof(struct pisp_fe_awb_stats_config)      },
++	{ PISP_FE_ENABLE_RGBY,       0, offsetof(struct pisp_fe_config, rgby),
++					sizeof(struct pisp_fe_rgby_config)           },
++	{ PISP_FE_ENABLE_LSC,        0, offsetof(struct pisp_fe_config, lsc),
++					sizeof(struct pisp_fe_lsc_config)            },
++	{ PISP_FE_ENABLE_AGC_STATS,  0, offsetof(struct pisp_fe_config, agc_stats),
++					sizeof(struct pisp_agc_statistics)           },
++	{ PISP_FE_ENABLE_CROP0,      0, offsetof(struct pisp_fe_config, ch[0].crop),
++					sizeof(struct pisp_fe_crop_config)           },
++	{ PISP_FE_ENABLE_DOWNSCALE0, 0, offsetof(struct pisp_fe_config, ch[0].downscale),
++					sizeof(struct pisp_fe_downscale_config)      },
++	{ PISP_FE_ENABLE_COMPRESS0,  0, offsetof(struct pisp_fe_config, ch[0].compress),
++					sizeof(struct pisp_compress_config)          },
++	{ PISP_FE_ENABLE_OUTPUT0,    0, offsetof(struct pisp_fe_config, ch[0].output),
++					sizeof(struct pisp_fe_output_config)         },
++	{ PISP_FE_ENABLE_CROP1,      0, offsetof(struct pisp_fe_config, ch[1].crop),
++					sizeof(struct pisp_fe_crop_config)           },
++	{ PISP_FE_ENABLE_DOWNSCALE1, 0, offsetof(struct pisp_fe_config, ch[1].downscale),
++					sizeof(struct pisp_fe_downscale_config)      },
++	{ PISP_FE_ENABLE_COMPRESS1,  0, offsetof(struct pisp_fe_config, ch[1].compress),
++					sizeof(struct pisp_compress_config)          },
++	{ PISP_FE_ENABLE_OUTPUT1,    0, offsetof(struct pisp_fe_config, ch[1].output),
++					sizeof(struct pisp_fe_output_config)         },
++};
++
++#define pisp_fe_dbg_irq(fmt, arg...)                            \
++	do {                                                    \
++		if (cfe_debug_irq)                              \
++			dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \
++	} while (0)
++#define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg)
++#define pisp_fe_info(fmt, arg...) dev_info(fe->v4l2_dev->dev, fmt, ##arg)
++#define pisp_fe_err(fmt, arg...) dev_err(fe->v4l2_dev->dev, fmt, ##arg)
++
++static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset)
++{
++	return readl(fe->base + offset);
++}
++
++static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset,
++				     u32 val)
++{
++	writel(val, fe->base + offset);
++}
++
++static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset,
++					     u32 val)
++{
++	writel_relaxed(val, fe->base + offset);
++}
++
++static int pisp_regs_show(struct seq_file *s, void *data)
++{
++	struct pisp_fe_device *fe = s->private;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev);
++	if (ret)
++		return ret;
++
++	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg))
++	DUMP(FE_VERSION);
++	DUMP(FE_CONTROL);
++	DUMP(FE_STATUS);
++	DUMP(FE_FRAME_STATUS);
++	DUMP(FE_ERROR_STATUS);
++	DUMP(FE_OUTPUT_STATUS);
++	DUMP(FE_INT_EN);
++	DUMP(FE_INT_STATUS);
++#undef DUMP
++
++	pm_runtime_put(fe->v4l2_dev->dev);
++
++	return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(pisp_regs);
++
++static void pisp_config_write(struct pisp_fe_device *fe,
++			      struct pisp_fe_config *config,
++			      unsigned int start_offset,
++			      unsigned int size)
++{
++	const unsigned int max_offset =
++		offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]);
++	unsigned int i, end_offset;
++	u32 *cfg = (u32 *)config;
++
++	start_offset = min(start_offset, max_offset);
++	end_offset = min(start_offset + size, max_offset);
++
++	cfg += start_offset >> 2;
++	for (i = start_offset; i < end_offset; i += 4, cfg++)
++		pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i,
++					  *cfg);
++}
++
++void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof)
++{
++	u32 status, int_status, out_status, frame_status, error_status;
++	unsigned int i;
++
++	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
++	status = pisp_fe_reg_read(fe, FE_STATUS);
++	out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS);
++	frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS);
++	error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS);
++
++	int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
++	pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
++
++	pisp_fe_dbg_irq("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n",
++			__func__, status, out_status, frame_status, error_status,
++			int_status);
++
++	/* We do not report interrupts for the input/stream pad. */
++	for (i = 0; i < FE_NUM_PADS - 1; i++) {
++		sof[i] = !!(int_status & FE_INT_SOF);
++		eof[i] = !!(int_status & FE_INT_EOF);
++	}
++}
++
++static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg,
++				    unsigned int c, struct v4l2_format const *f)
++{
++	unsigned int wbytes;
++
++	wbytes = cfg->ch[c].output.format.width;
++	if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK)
++		wbytes *= 2;
++
++	/* Check output image dimensions are nonzero and not too big */
++	if (cfg->ch[c].output.format.width < 2 ||
++	    cfg->ch[c].output.format.height < 2 ||
++	    cfg->ch[c].output.format.height > f->fmt.pix.height ||
++	    cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline ||
++	    wbytes > f->fmt.pix.bytesperline)
++		return false;
++
++	/* Check for zero-sized crops, which could cause lockup */
++	if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) &&
++	    ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||
++	      cfg->ch[c].crop.offset_y >= cfg->input.format.height ||
++	      cfg->ch[c].crop.width < 2 ||
++	      cfg->ch[c].crop.height < 2)))
++		return false;
++
++	if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) &&
++	    (cfg->ch[c].downscale.output_width < 2 ||
++	     cfg->ch[c].downscale.output_height < 2))
++		return false;
++
++	return true;
++}
++
++static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg)
++{
++	/* Check for zero-sized crop, which could cause lockup */
++	return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) ||
++		(cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) &&
++		 cfg->stats_crop.offset_y < cfg->input.format.height &&
++		 cfg->stats_crop.width >= 2 &&
++		 cfg->stats_crop.height >= 2));
++}
++
++int pisp_fe_validate_config(struct pisp_fe_device *fe,
++			    struct pisp_fe_config *cfg,
++			    struct v4l2_format const *f0,
++			    struct v4l2_format const *f1)
++{
++	unsigned int i;
++
++	/*
++	 * Check the input is enabled, streaming and has nonzero size;
++	 * to avoid cases where the hardware might lock up or try to
++	 * read inputs from memory (which this driver doesn't support).
++	 */
++	if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) ||
++	    cfg->input.streaming != 1 || cfg->input.format.width < 2 ||
++	    cfg->input.format.height < 2) {
++		pisp_fe_err("%s: Input config not valid", __func__);
++		return -EINVAL;
++	}
++
++	for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
++		if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) {
++			if (cfg->global.enables &
++					PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) {
++				pisp_fe_err("%s: Output %u not valid",
++					    __func__, i);
++				return -EINVAL;
++			}
++			continue;
++		}
++
++		if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0))
++			return -EINVAL;
++	}
++
++	if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) &&
++	    !pisp_fe_validate_stats(cfg)) {
++		pisp_fe_err("%s: Stats config not valid", __func__);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
++			struct pisp_fe_config *cfg)
++{
++	unsigned int i;
++	u64 addr;
++	u32 status;
++
++	/*
++	 * Check output buffers exist and outputs are correctly configured.
++	 * If valid, set the buffer's DMA address; otherwise disable.
++	 */
++	for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
++		struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i];
++
++		if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i)))
++			continue;
++
++		addr = vb2_dma_contig_plane_dma_addr(buf, 0);
++		cfg->output_buffer[i].addr_lo = addr & 0xffffffff;
++		cfg->output_buffer[i].addr_hi = addr >> 32;
++	}
++
++	if (vb2_bufs[FE_STATS_PAD]) {
++		addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0);
++		cfg->stats_buffer.addr_lo = addr & 0xffffffff;
++		cfg->stats_buffer.addr_hi = addr >> 32;
++	}
++
++	/* Set up ILINES interrupts 3/4 of the way down each output */
++	cfg->ch[0].output.ilines =
++		max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2);
++	cfg->ch[1].output.ilines =
++		max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2);
++
++	/*
++	 * The hardware must have consumed the previous config by now.
++	 * This read of status also serves as a memory barrier before the
++	 * sequence of relaxed writes which follow.
++	 */
++	status = pisp_fe_reg_read(fe, FE_STATUS);
++	pisp_fe_dbg_irq("%s: status = 0x%x\n", __func__, status);
++	if (WARN_ON(status & FE_STATUS_QUEUED))
++		return;
++
++	/*
++	 * Unconditionally write buffers, global and input parameters.
++	 * Write cropping and output parameters whenever they are enabled.
++	 * Selectively write other parameters that have been marked as
++	 * changed through the dirty flags.
++	 */
++	pisp_config_write(fe, cfg, 0,
++			  offsetof(struct pisp_fe_config, decompress));
++	cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL;
++	cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT;
++	cfg->dirty_flags |= (cfg->global.enables &
++			     (PISP_FE_ENABLE_STATS_CROP        |
++			      PISP_FE_ENABLE_OUTPUT_CLUSTER(0) |
++			      PISP_FE_ENABLE_OUTPUT_CLUSTER(1)));
++	for (i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) {
++		const struct pisp_fe_config_param *p = &pisp_fe_config_map[i];
++
++		if (cfg->dirty_flags & p->dirty_flags ||
++		    cfg->dirty_flags_extra & p->dirty_flags_extra)
++			pisp_config_write(fe, cfg, p->offset, p->size);
++	}
++
++	/* This final non-relaxed write serves as a memory barrier */
++	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE);
++}
++
++void pisp_fe_start(struct pisp_fe_device *fe)
++{
++	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
++	pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++	pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1);
++	fe->inframe_count = 0;
++}
++
++void pisp_fe_stop(struct pisp_fe_device *fe)
++{
++	pisp_fe_reg_write(fe, FE_INT_EN, 0);
++	pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
++	usleep_range(1000, 2000);
++	WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
++	pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++}
++
++static struct pisp_fe_device *to_pisp_fe_device(struct v4l2_subdev *subdev)
++{
++	return container_of(subdev, struct pisp_fe_device, sd);
++}
++
++static int pisp_fe_init_cfg(struct v4l2_subdev *sd,
++			    struct v4l2_subdev_state *state)
++{
++	struct v4l2_mbus_framefmt *fmt;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++	*fmt = cfe_default_format;
++	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, FE_CONFIG_PAD);
++	*fmt = cfe_default_meta_format;
++	fmt->code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
++	*fmt = cfe_default_format;
++	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD);
++	*fmt = cfe_default_format;
++	fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD);
++	*fmt = cfe_default_meta_format;
++	fmt->code = MEDIA_BUS_FMT_PISP_FE_STATS;
++
++	return 0;
++}
++
++static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
++			       struct v4l2_subdev_state *state,
++			       struct v4l2_subdev_format *format)
++{
++	struct v4l2_mbus_framefmt *fmt;
++	const struct cfe_fmt *cfe_fmt;
++
++	/* TODO: format propagation to source pads */
++	/* TODO: format validation */
++
++	switch (format->pad) {
++	case FE_STREAM_PAD:
++	case FE_OUTPUT0_PAD:
++	case FE_OUTPUT1_PAD:
++		cfe_fmt = find_format_by_code(format->format.code);
++		if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
++			cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++		format->format.code = cfe_fmt->code;
++
++		break;
++
++	case FE_CONFIG_PAD:
++		format->format.code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
++		break;
++
++	case FE_STATS_PAD:
++		format->format.code = MEDIA_BUS_FMT_PISP_FE_STATS;
++		break;
++	}
++
++	fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++	*fmt = format->format;
++
++	return 0;
++}
++
++static int pisp_fe_link_validate(struct v4l2_subdev *sd,
++				 struct media_link *link,
++				 struct v4l2_subdev_format *source_fmt,
++				 struct v4l2_subdev_format *sink_fmt)
++{
++	struct pisp_fe_device *fe = to_pisp_fe_device(sd);
++
++	pisp_fe_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
++		    link->source->entity->name, link->source->index,
++		    link->sink->entity->name, link->sink->index);
++
++	/* The width, height and code must match. */
++	if (source_fmt->format.width != sink_fmt->format.width ||
++	    source_fmt->format.width != sink_fmt->format.width ||
++	    source_fmt->format.code != sink_fmt->format.code) {
++		pisp_fe_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
++			    __func__,
++			     source_fmt->format.width,
++			     source_fmt->format.height,
++			     source_fmt->format.code,
++			     sink_fmt->format.width,
++			     sink_fmt->format.height,
++			     sink_fmt->format.code);
++		return -EPIPE;
++	}
++
++	return 0;
++}
++
++static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
++	.init_cfg = pisp_fe_init_cfg,
++	.get_fmt = v4l2_subdev_get_fmt,
++	.set_fmt = pisp_fe_pad_set_fmt,
++	.link_validate = pisp_fe_link_validate,
++};
++
++static const struct media_entity_operations pisp_fe_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_ops pisp_fe_subdev_ops = {
++	.pad = &pisp_fe_subdev_pad_ops,
++};
++
++int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
++{
++	int ret;
++
++	debugfs_create_file("pisp_regs", 0444, debugfs, fe, &pisp_regs_fops);
++
++	fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);
++	pisp_fe_info("PiSP FE HW v%u.%u\n",
++		     (fe->hw_revision >> 24) & 0xff,
++		     (fe->hw_revision >> 20) & 0x0f);
++
++	fe->pad[FE_STREAM_PAD].flags =
++		MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
++	fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;
++	fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;
++	fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;
++	fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;
++
++	ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),
++				     fe->pad);
++	if (ret)
++		return ret;
++
++	/* Initialize subdev */
++	v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);
++	fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
++	fe->sd.entity.ops = &pisp_fe_entity_ops;
++	fe->sd.entity.name = "pisp-fe";
++	fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
++	fe->sd.owner = THIS_MODULE;
++	snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");
++
++	ret = v4l2_subdev_init_finalize(&fe->sd);
++	if (ret)
++		goto err_entity_cleanup;
++
++	ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);
++	if (ret) {
++		pisp_fe_err("Failed register pisp fe subdev (%d)\n", ret);
++		goto err_subdev_cleanup;
++	}
++
++	/* Must be in IDLE state (STATUS == 0) here. */
++	WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
++
++	return 0;
++
++err_subdev_cleanup:
++	v4l2_subdev_cleanup(&fe->sd);
++err_entity_cleanup:
++	media_entity_cleanup(&fe->sd.entity);
++
++	return ret;
++}
++
++void pisp_fe_uninit(struct pisp_fe_device *fe)
++{
++	v4l2_device_unregister_subdev(&fe->sd);
++	v4l2_subdev_cleanup(&fe->sd);
++	media_entity_cleanup(&fe->sd.entity);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h
+@@ -0,0 +1,53 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PiSP Front End driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_H_
++#define _PISP_FE_H_
++
++#include <linux/debugfs.h>
++#include <linux/io.h>
++#include <linux/types.h>
++#include <linux/videodev2.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++
++#include "pisp_fe_config.h"
++
++enum pisp_fe_pads {
++	FE_STREAM_PAD,
++	FE_CONFIG_PAD,
++	FE_OUTPUT0_PAD,
++	FE_OUTPUT1_PAD,
++	FE_STATS_PAD,
++	FE_NUM_PADS
++};
++
++struct pisp_fe_device {
++	/* Parent V4l2 device */
++	struct v4l2_device *v4l2_dev;
++	void __iomem *base;
++	u32 hw_revision;
++
++	u16 inframe_count;
++	struct media_pad pad[FE_NUM_PADS];
++	struct v4l2_subdev sd;
++};
++
++void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof);
++int pisp_fe_validate_config(struct pisp_fe_device *fe,
++			    struct pisp_fe_config *cfg,
++			    struct v4l2_format const *f0,
++			    struct v4l2_format const *f1);
++void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
++			struct pisp_fe_config *cfg);
++void pisp_fe_start(struct pisp_fe_device *fe);
++void pisp_fe_stop(struct pisp_fe_device *fe);
++int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs);
++void pisp_fe_uninit(struct pisp_fe_device *fe);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+@@ -0,0 +1,272 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End Driver Configuration structures
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_CONFIG_
++#define _PISP_FE_CONFIG_
++
++#include <media/raspberrypi/pisp_common.h>
++
++#include "pisp_statistics.h"
++
++#define PISP_FE_NUM_OUTPUTS 2
++
++enum pisp_fe_enable {
++	PISP_FE_ENABLE_INPUT = 0x000001,
++	PISP_FE_ENABLE_DECOMPRESS = 0x000002,
++	PISP_FE_ENABLE_DECOMPAND = 0x000004,
++	PISP_FE_ENABLE_BLA = 0x000008,
++	PISP_FE_ENABLE_DPC = 0x000010,
++	PISP_FE_ENABLE_STATS_CROP = 0x000020,
++	PISP_FE_ENABLE_DECIMATE = 0x000040,
++	PISP_FE_ENABLE_BLC = 0x000080,
++	PISP_FE_ENABLE_CDAF_STATS = 0x000100,
++	PISP_FE_ENABLE_AWB_STATS = 0x000200,
++	PISP_FE_ENABLE_RGBY = 0x000400,
++	PISP_FE_ENABLE_LSC = 0x000800,
++	PISP_FE_ENABLE_AGC_STATS = 0x001000,
++	PISP_FE_ENABLE_CROP0 = 0x010000,
++	PISP_FE_ENABLE_DOWNSCALE0 = 0x020000,
++	PISP_FE_ENABLE_COMPRESS0 = 0x040000,
++	PISP_FE_ENABLE_OUTPUT0 = 0x080000,
++	PISP_FE_ENABLE_CROP1 = 0x100000,
++	PISP_FE_ENABLE_DOWNSCALE1 = 0x200000,
++	PISP_FE_ENABLE_COMPRESS1 = 0x400000,
++	PISP_FE_ENABLE_OUTPUT1 = 0x800000
++};
++
++#define PISP_FE_ENABLE_CROP(i) (PISP_FE_ENABLE_CROP0 << (4 * (i)))
++#define PISP_FE_ENABLE_DOWNSCALE(i) (PISP_FE_ENABLE_DOWNSCALE0 << (4 * (i)))
++#define PISP_FE_ENABLE_COMPRESS(i) (PISP_FE_ENABLE_COMPRESS0 << (4 * (i)))
++#define PISP_FE_ENABLE_OUTPUT(i) (PISP_FE_ENABLE_OUTPUT0 << (4 * (i)))
++
++/*
++ * We use the enable flags to show when blocks are "dirty", but we need some
++ * extra ones too.
++ */
++enum pisp_fe_dirty {
++	PISP_FE_DIRTY_GLOBAL = 0x0001,
++	PISP_FE_DIRTY_FLOATING = 0x0002,
++	PISP_FE_DIRTY_OUTPUT_AXI = 0x0004
++};
++
++struct pisp_fe_global_config {
++	u32 enables;
++	u8 bayer_order;
++	u8 pad[3];
++};
++
++struct pisp_fe_input_axi_config {
++	/* burst length minus one, in the range 0..15; OR'd with flags */
++	u8 maxlen_flags;
++	/* { prot[2:0], cache[3:0] } fields */
++	u8 cache_prot;
++	/* QoS (only 4 LS bits are used) */
++	u16 qos;
++};
++
++struct pisp_fe_output_axi_config {
++	/* burst length minus one, in the range 0..15; OR'd with flags */
++	u8 maxlen_flags;
++	/* { prot[2:0], cache[3:0] } fields */
++	u8 cache_prot;
++	/* QoS (4 bitfields of 4 bits each for different panic levels) */
++	u16 qos;
++	/*  For Panic mode: Output FIFO panic threshold */
++	u16 thresh;
++	/*  For Panic mode: Output FIFO statistics throttle threshold */
++	u16 throttle;
++};
++
++struct pisp_fe_input_config {
++	u8 streaming;
++	u8 pad[3];
++	struct pisp_image_format_config format;
++	struct pisp_fe_input_axi_config axi;
++	/* Extra cycles delay before issuing each burst request */
++	u8 holdoff;
++	u8 pad2[3];
++};
++
++struct pisp_fe_output_config {
++	struct pisp_image_format_config format;
++	u16 ilines;
++	u8 pad[2];
++};
++
++struct pisp_fe_input_buffer_config {
++	u32 addr_lo;
++	u32 addr_hi;
++	u16 frame_id;
++	u16 pad;
++};
++
++#define PISP_FE_DECOMPAND_LUT_SIZE 65
++
++struct pisp_fe_decompand_config {
++	u16 lut[PISP_FE_DECOMPAND_LUT_SIZE];
++	u16 pad;
++};
++
++struct pisp_fe_dpc_config {
++	u8 coeff_level;
++	u8 coeff_range;
++	u8 coeff_range2;
++#define PISP_FE_DPC_FLAG_FOLDBACK 1
++#define PISP_FE_DPC_FLAG_VFLAG 2
++	u8 flags;
++};
++
++#define PISP_FE_LSC_LUT_SIZE 16
++
++struct pisp_fe_lsc_config {
++	u8 shift;
++	u8 pad0;
++	u16 scale;
++	u16 centre_x;
++	u16 centre_y;
++	u16 lut[PISP_FE_LSC_LUT_SIZE];
++};
++
++struct pisp_fe_rgby_config {
++	u16 gain_r;
++	u16 gain_g;
++	u16 gain_b;
++	u8 maxflag;
++	u8 pad;
++};
++
++struct pisp_fe_agc_stats_config {
++	u16 offset_x;
++	u16 offset_y;
++	u16 size_x;
++	u16 size_y;
++	/* each weight only 4 bits */
++	u8 weights[PISP_AGC_STATS_NUM_ZONES / 2];
++	u16 row_offset_x;
++	u16 row_offset_y;
++	u16 row_size_x;
++	u16 row_size_y;
++	u8 row_shift;
++	u8 float_shift;
++	u8 pad1[2];
++};
++
++struct pisp_fe_awb_stats_config {
++	u16 offset_x;
++	u16 offset_y;
++	u16 size_x;
++	u16 size_y;
++	u8 shift;
++	u8 pad[3];
++	u16 r_lo;
++	u16 r_hi;
++	u16 g_lo;
++	u16 g_hi;
++	u16 b_lo;
++	u16 b_hi;
++};
++
++struct pisp_fe_floating_stats_region {
++	u16 offset_x;
++	u16 offset_y;
++	u16 size_x;
++	u16 size_y;
++};
++
++struct pisp_fe_floating_stats_config {
++	struct pisp_fe_floating_stats_region
++					regions[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_FE_CDAF_NUM_WEIGHTS 8
++
++struct pisp_fe_cdaf_stats_config {
++	u16 noise_constant;
++	u16 noise_slope;
++	u16 offset_x;
++	u16 offset_y;
++	u16 size_x;
++	u16 size_y;
++	u16 skip_x;
++	u16 skip_y;
++	u32 mode;
++};
++
++struct pisp_fe_stats_buffer_config {
++	u32 addr_lo;
++	u32 addr_hi;
++};
++
++struct pisp_fe_crop_config {
++	u16 offset_x;
++	u16 offset_y;
++	u16 width;
++	u16 height;
++};
++
++enum pisp_fe_downscale_flags {
++	DOWNSCALE_BAYER =
++		1, /* downscale the four Bayer components independently... */
++	DOWNSCALE_BIN =
++		2 /* ...without trying to preserve their spatial relationship */
++};
++
++struct pisp_fe_downscale_config {
++	u8 xin;
++	u8 xout;
++	u8 yin;
++	u8 yout;
++	u8 flags; /* enum pisp_fe_downscale_flags */
++	u8 pad[3];
++	u16 output_width;
++	u16 output_height;
++};
++
++struct pisp_fe_output_buffer_config {
++	u32 addr_lo;
++	u32 addr_hi;
++};
++
++/* Each of the two output channels/branches: */
++struct pisp_fe_output_branch_config {
++	struct pisp_fe_crop_config crop;
++	struct pisp_fe_downscale_config downscale;
++	struct pisp_compress_config compress;
++	struct pisp_fe_output_config output;
++	u32 pad;
++};
++
++/* And finally one to rule them all: */
++struct pisp_fe_config {
++	/* I/O configuration: */
++	struct pisp_fe_stats_buffer_config stats_buffer;
++	struct pisp_fe_output_buffer_config output_buffer[PISP_FE_NUM_OUTPUTS];
++	struct pisp_fe_input_buffer_config input_buffer;
++	/* processing configuration: */
++	struct pisp_fe_global_config global;
++	struct pisp_fe_input_config input;
++	struct pisp_decompress_config decompress;
++	struct pisp_fe_decompand_config decompand;
++	struct pisp_bla_config bla;
++	struct pisp_fe_dpc_config dpc;
++	struct pisp_fe_crop_config stats_crop;
++	u32 spare1; /* placeholder for future decimate configuration */
++	struct pisp_bla_config blc;
++	struct pisp_fe_rgby_config rgby;
++	struct pisp_fe_lsc_config lsc;
++	struct pisp_fe_agc_stats_config agc_stats;
++	struct pisp_fe_awb_stats_config awb_stats;
++	struct pisp_fe_cdaf_stats_config cdaf_stats;
++	struct pisp_fe_floating_stats_config floating_stats;
++	struct pisp_fe_output_axi_config output_axi;
++	struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS];
++	/* non-register fields: */
++	u32 dirty_flags; /* these use pisp_fe_enable */
++	u32 dirty_flags_extra; /* these use pisp_fe_dirty */
++};
++
++#endif /* _PISP_FE_CONFIG_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+@@ -0,0 +1,62 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End statistics definitions
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_STATISTICS_H_
++#define _PISP_FE_STATISTICS_H_
++
++#define PISP_FLOATING_STATS_NUM_ZONES 4
++#define PISP_AGC_STATS_NUM_BINS 1024
++#define PISP_AGC_STATS_SIZE 16
++#define PISP_AGC_STATS_NUM_ZONES (PISP_AGC_STATS_SIZE * PISP_AGC_STATS_SIZE)
++#define PISP_AGC_STATS_NUM_ROW_SUMS 512
++
++struct pisp_agc_statistics_zone {
++	u64 Y_sum;
++	u32 counted;
++	u32 pad;
++};
++
++struct pisp_agc_statistics {
++	u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS];
++	/*
++	 * 32-bits per bin means an image (just less than) 16384x16384 pixels
++	 * in size can weight every pixel from 0 to 15.
++	 */
++	u32 histogram[PISP_AGC_STATS_NUM_BINS];
++	struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_AWB_STATS_SIZE 32
++#define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE)
++
++struct pisp_awb_statistics_zone {
++	u32 R_sum;
++	u32 G_sum;
++	u32 B_sum;
++	u32 counted;
++};
++
++struct pisp_awb_statistics {
++	struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES];
++	struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_CDAF_STATS_SIZE 8
++#define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE)
++
++struct pisp_cdaf_statistics {
++	u64 foms[PISP_CDAF_STATS_NUM_FOMS];
++	u64 floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++struct pisp_statistics {
++	struct pisp_awb_statistics awb;
++	struct pisp_agc_statistics agc;
++	struct pisp_cdaf_statistics cdaf;
++};
++
++#endif /* _PISP_FE_STATISTICS_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+@@ -0,0 +1,144 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End image definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_TYPES_H_
++#define _PISP_FE_TYPES_H_
++
++/* This definition must match the format description in the hardware exactly! */
++struct pisp_image_format_config {
++	/* size in pixels */
++	u16 width, height;
++	/* must match struct pisp_image_format below */
++	u32 format;
++	s32 stride;
++	/* some planar image formats will need a second stride */
++	s32 stride2;
++};
++
++static_assert(sizeof(struct pisp_image_format_config) == 16);
++
++enum pisp_bayer_order {
++	/*
++	 * Note how bayer_order&1 tells you if G is on the even pixels of the
++	 * checkerboard or not, and bayer_order&2 tells you if R is on the even
++	 * rows or is swapped with B. Note that if the top (of the 8) bits is
++	 * set, this denotes a monochrome or greyscale image, and the lower bits
++	 * should all be ignored.
++	 */
++	PISP_BAYER_ORDER_RGGB = 0,
++	PISP_BAYER_ORDER_GBRG = 1,
++	PISP_BAYER_ORDER_BGGR = 2,
++	PISP_BAYER_ORDER_GRBG = 3,
++	PISP_BAYER_ORDER_GREYSCALE = 128
++};
++
++enum pisp_image_format {
++	/*
++	 * Precise values are mostly tbd. Generally these will be portmanteau
++	 * values comprising bit fields and flags. This format must be shared
++	 * throughout the PiSP.
++	 */
++	PISP_IMAGE_FORMAT_BPS_8 = 0x00000000,
++	PISP_IMAGE_FORMAT_BPS_10 = 0x00000001,
++	PISP_IMAGE_FORMAT_BPS_12 = 0x00000002,
++	PISP_IMAGE_FORMAT_BPS_16 = 0x00000003,
++	PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003,
++
++	PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000,
++	PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010,
++	PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020,
++	PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030,
++
++	PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000,
++	PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100,
++	PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200,
++	PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300,
++
++	PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000,
++	PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000,
++
++	PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000,
++	PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000,
++	PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000,
++	PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000,
++	PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000,
++	PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000,
++	PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000,
++	PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000,
++	PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000,
++	PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000,
++
++	PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000,
++	PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000,
++
++	PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000,
++	PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000,
++	PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000,
++	PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000,
++	PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000,
++
++	/* Lastly a few specific instantiations of the above. */
++	PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16,
++	PISP_IMAGE_FORMAT_THREE_16 =
++		PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL
++};
++
++#define PISP_IMAGE_FORMAT_bps_8(fmt)                                           \
++	(((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8)
++#define PISP_IMAGE_FORMAT_bps_10(fmt)                                          \
++	(((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10)
++#define PISP_IMAGE_FORMAT_bps_12(fmt)                                          \
++	(((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12)
++#define PISP_IMAGE_FORMAT_bps_16(fmt)                                          \
++	(((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16)
++#define PISP_IMAGE_FORMAT_bps(fmt)                                             \
++	(((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ?                                \
++		       8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : \
++		       8)
++#define PISP_IMAGE_FORMAT_shift(fmt)                                           \
++	(((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1)
++#define PISP_IMAGE_FORMAT_three_channel(fmt)                                   \
++	((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)
++#define PISP_IMAGE_FORMAT_single_channel(fmt)                                  \
++	(!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL))
++#define PISP_IMAGE_FORMAT_compressed(fmt)                                      \
++	(((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) !=                       \
++	 PISP_IMAGE_FORMAT_UNCOMPRESSED)
++#define PISP_IMAGE_FORMAT_sampling_444(fmt)                                    \
++	(((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                          \
++	 PISP_IMAGE_FORMAT_SAMPLING_444)
++#define PISP_IMAGE_FORMAT_sampling_422(fmt)                                    \
++	(((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                          \
++	 PISP_IMAGE_FORMAT_SAMPLING_422)
++#define PISP_IMAGE_FORMAT_sampling_420(fmt)                                    \
++	(((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) ==                          \
++	 PISP_IMAGE_FORMAT_SAMPLING_420)
++#define PISP_IMAGE_FORMAT_order_normal(fmt)                                    \
++	(!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED))
++#define PISP_IMAGE_FORMAT_order_swapped(fmt)                                   \
++	((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)
++#define PISP_IMAGE_FORMAT_interleaved(fmt)                                     \
++	(((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                         \
++	 PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED)
++#define PISP_IMAGE_FORMAT_semiplanar(fmt)                                      \
++	(((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                         \
++	 PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR)
++#define PISP_IMAGE_FORMAT_planar(fmt)                                          \
++	(((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) ==                         \
++	 PISP_IMAGE_FORMAT_PLANARITY_PLANAR)
++#define PISP_IMAGE_FORMAT_wallpaper(fmt)                                       \
++	((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++#define PISP_IMAGE_FORMAT_HOG(fmt)                                             \
++	((fmt) &                                                               \
++	 (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED))
++
++#define PISP_WALLPAPER_WIDTH 128 // in bytes
++
++#endif /* _PISP_FE_TYPES_H_ */

+ 38 - 0
target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch

@@ -0,0 +1,38 @@
+From 2be65d1fd1f7d3cf6f59b58b53e285400f04a160 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 15 Feb 2023 09:46:35 +0000
+Subject: [PATCH] dt-bindings: net: cdns,macb: AXI tuning properties
+
+Add optional properties to tune the AXI interface -
+cdns,aw2w-max-pipe, cdns,ar2r-max-pipe and cdns,use-aw2b-fill.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ .../devicetree/bindings/net/cdns,macb.yaml       | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
+@@ -121,6 +121,22 @@ properties:
+       Node containing PHY children. If this node is not present, then PHYs will
+       be direct children.
+ 
++  cdns,aw2w-max-pipe:
++    $ref: /schemas/types.yaml#/definitions/uint32
++    description:
++      Maximum number of outstanding AXI write requests
++
++  cdns,ar2r-max-pipe:
++    $ref: /schemas/types.yaml#/definitions/uint32
++    description:
++      Maximum number of outstanding AXI read requests
++
++  cdns,use-aw2b-fill:
++    type: boolean
++    description:
++      If set, the maximum number of outstanding write transactions operates
++      between the AW to B AXI channel, instead of the AW to W AXI channel.
++
+ patternProperties:
+   "^ethernet-phy@[0-9a-f]$":
+     type: object

+ 29 - 0
target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch

@@ -0,0 +1,29 @@
+From 9ef0615a5c5f93cb72af8df3a2dae6d23b106eb5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 21 Feb 2023 21:26:16 +0000
+Subject: [PATCH] ASoC: dwc: list all supported sample sizes
+
+The hardware configuration determines the maximum-supported sample size
+for each channel, but TCRx allows smaller sizes to be specified at run
+time. Include the smaller supported sizes in the formats array.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/dwc/dwc-i2s.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -448,9 +448,9 @@ static const u32 bus_widths[COMP_MAX_DAT
+ static const u32 formats[COMP_MAX_WORDSIZE] = {
+ 	SNDRV_PCM_FMTBIT_S16_LE,
+ 	SNDRV_PCM_FMTBIT_S16_LE,
+-	SNDRV_PCM_FMTBIT_S24_LE,
+-	SNDRV_PCM_FMTBIT_S24_LE,
+-	SNDRV_PCM_FMTBIT_S32_LE,
++	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
++	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
++	SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ 	0,
+ 	0,
+ 	0

+ 59 - 0
target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch

@@ -0,0 +1,59 @@
+From 06f794e8cb227249e03893e4b4923ff58556eb60 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 4 Mar 2021 14:49:23 +0000
+Subject: [PATCH] ASoC: dwc: Support set_bclk_ratio
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/dwc/dwc-i2s.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -351,11 +351,46 @@ static int dw_i2s_set_fmt(struct snd_soc
+ 	return ret;
+ }
+ 
++static int dw_i2s_set_bclk_ratio(struct snd_soc_dai *cpu_dai,
++				 unsigned int ratio)
++{
++	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
++	struct i2s_clk_config_data *config = &dev->config;
++
++	dev_err(dev->dev, "%s(%d)\n", __func__, ratio);
++	switch (ratio) {
++	case 32:
++		config->data_width = 16;
++		dev->ccr = 0x00;
++		dev->xfer_resolution = 0x02;
++		break;
++
++	case 48:
++		config->data_width = 24;
++		dev->ccr = 0x08;
++		dev->xfer_resolution = 0x04;
++		break;
++
++	case 64:
++		config->data_width = 32;
++		dev->ccr = 0x10;
++		dev->xfer_resolution = 0x05;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
++
++	return 0;
++}
++
+ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+ 	.hw_params	= dw_i2s_hw_params,
+ 	.prepare	= dw_i2s_prepare,
+ 	.trigger	= dw_i2s_trigger,
+ 	.set_fmt	= dw_i2s_set_fmt,
++	.set_bclk_ratio	= dw_i2s_set_bclk_ratio,
+ };
+ 
+ #ifdef CONFIG_PM

+ 81 - 0
target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch

@@ -0,0 +1,81 @@
+From b3b1177092d4d2ba6df74042d39aa42c5055f687 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 3 Jul 2023 09:08:16 +0100
+Subject: [PATCH] ASoC: dwc: Add DMACR handling
+
+Add control of the DMACR register, which is required for paced DMA
+(i.e. DREQ) support.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/dwc/dwc-i2s.c | 13 ++++++++++---
+ sound/soc/dwc/local.h   | 13 +++++++++++++
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -185,9 +185,9 @@ static void i2s_stop(struct dw_i2s_dev *
+ 
+ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+ {
+-	u32 ch_reg;
+ 	struct i2s_clk_config_data *config = &dev->config;
+-
++	u32 ch_reg;
++	u32 dmacr = 0;
+ 
+ 	i2s_disable_channels(dev, stream);
+ 
+@@ -198,15 +198,22 @@ static void dw_i2s_config(struct dw_i2s_
+ 			i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+ 				      dev->fifo_th - 1);
+ 			i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
++			dmacr |= (DMACR_DMAEN_TXCH0 << ch_reg);
+ 		} else {
+ 			i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+ 				      dev->xfer_resolution);
+ 			i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+ 				      dev->fifo_th - 1);
+ 			i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
++			dmacr |= (DMACR_DMAEN_RXCH0 << ch_reg);
+ 		}
+-
+ 	}
++	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
++		dmacr |= DMACR_DMAEN_TX;
++	else if (stream == SNDRV_PCM_STREAM_CAPTURE)
++		dmacr |= DMACR_DMAEN_RX;
++
++	i2s_write_reg(dev->i2s_base, DMACR, dmacr);
+ }
+ 
+ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+--- a/sound/soc/dwc/local.h
++++ b/sound/soc/dwc/local.h
+@@ -25,6 +25,8 @@
+ #define RXFFR		0x014
+ #define TXFFR		0x018
+ 
++#define DMACR   0x200
++
+ /* Interrupt status register fields */
+ #define ISR_TXFO	BIT(5)
+ #define ISR_TXFE	BIT(4)
+@@ -47,6 +49,17 @@
+ #define RFF(x)		(0x40 * x + 0x050)
+ #define TFF(x)		(0x40 * x + 0x054)
+ 
++#define DMACR_DMAEN_TX		BIT(17)
++#define DMACR_DMAEN_RX		BIT(16)
++#define DMACR_DMAEN_TXCH3	BIT(11)
++#define DMACR_DMAEN_TXCH2	BIT(10)
++#define DMACR_DMAEN_TXCH1	BIT(9)
++#define DMACR_DMAEN_TXCH0	BIT(8)
++#define DMACR_DMAEN_RXCH3	BIT(3)
++#define DMACR_DMAEN_RXCH2	BIT(2)
++#define DMACR_DMAEN_RXCH1	BIT(1)
++#define DMACR_DMAEN_RXCH0	BIT(0)
++
+ /* I2SCOMPRegisters */
+ #define I2S_COMP_PARAM_2	0x01F0
+ #define I2S_COMP_PARAM_1	0x01F4

+ 128 - 0
target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch

@@ -0,0 +1,128 @@
+From e6baee4502c0228c79408b047096a1259a84353f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 3 Jul 2023 10:14:43 +0100
+Subject: [PATCH] ASOC: dwc: Improve DMA shutdown
+
+Disabling the I2S interface with outstanding transfers prevents the
+DMAC from shutting down, so keep it partially active after a stop.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/dwc/dwc-i2s.c | 72 ++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 64 insertions(+), 8 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -165,24 +165,26 @@ static void i2s_start(struct dw_i2s_dev
+ 	i2s_write_reg(dev->i2s_base, CER, 1);
+ }
+ 
+-static void i2s_stop(struct dw_i2s_dev *dev,
+-		struct snd_pcm_substream *substream)
++static void i2s_pause(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream)
+ {
+ 
+ 	i2s_clear_irqs(dev, substream->stream);
+-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+-		i2s_write_reg(dev->i2s_base, ITER, 0);
+-	else
+-		i2s_write_reg(dev->i2s_base, IRER, 0);
+ 
+ 	i2s_disable_irqs(dev, substream->stream, 8);
+ 
+ 	if (!dev->active) {
+ 		i2s_write_reg(dev->i2s_base, CER, 0);
+-		i2s_write_reg(dev->i2s_base, IER, 0);
++		/* Keep the device enabled until the shutdown - do not clear IER */
+ 	}
+ }
+ 
++static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream)
++{
++	i2s_clear_irqs(dev, substream->stream);
++
++	i2s_disable_irqs(dev, substream->stream, 8);
++}
++
+ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+ {
+ 	struct i2s_clk_config_data *config = &dev->config;
+@@ -288,6 +290,55 @@ static int dw_i2s_hw_params(struct snd_p
+ 	return 0;
+ }
+ 
++static int dw_i2s_startup(struct snd_pcm_substream *substream,
++			  struct snd_soc_dai *cpu_dai)
++{
++	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
++	union dw_i2s_snd_dma_data *dma_data = NULL;
++	u32 dmacr;
++
++	dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name);
++	if (!(dev->capability & DWC_I2S_RECORD) &&
++	    substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++		return -EINVAL;
++
++	if (!(dev->capability & DWC_I2S_PLAY) &&
++	    substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		return -EINVAL;
++
++	dw_i2s_config(dev, substream->stream);
++	dmacr = i2s_read_reg(dev->i2s_base, DMACR);
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		dma_data = &dev->play_dma_data;
++	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++		dma_data = &dev->capture_dma_data;
++
++	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
++	i2s_write_reg(dev->i2s_base, DMACR, dmacr);
++
++	return 0;
++}
++
++static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
++			    struct snd_soc_dai *dai)
++{
++	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
++
++	dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name);
++	i2s_disable_channels(dev, substream->stream);
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		i2s_write_reg(dev->i2s_base, ITER, 0);
++	else
++		i2s_write_reg(dev->i2s_base, IRER, 0);
++
++	i2s_disable_irqs(dev, substream->stream, 8);
++
++	if (!dev->active) {
++		i2s_write_reg(dev->i2s_base, CER, 0);
++		i2s_write_reg(dev->i2s_base, IER, 0);
++	}
++}
++
+ static int dw_i2s_prepare(struct snd_pcm_substream *substream,
+ 			  struct snd_soc_dai *dai)
+ {
+@@ -315,9 +366,12 @@ static int dw_i2s_trigger(struct snd_pcm
+ 		i2s_start(dev, substream);
+ 		break;
+ 
++	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++		dev->active--;
++		i2s_pause(dev, substream);
++		break;
+ 	case SNDRV_PCM_TRIGGER_STOP:
+ 	case SNDRV_PCM_TRIGGER_SUSPEND:
+-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ 		dev->active--;
+ 		i2s_stop(dev, substream);
+ 		break;
+@@ -394,6 +448,8 @@ static int dw_i2s_set_bclk_ratio(struct
+ 
+ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+ 	.hw_params	= dw_i2s_hw_params,
++	.startup	= dw_i2s_startup,
++	.shutdown	= dw_i2s_shutdown,
+ 	.prepare	= dw_i2s_prepare,
+ 	.trigger	= dw_i2s_trigger,
+ 	.set_fmt	= dw_i2s_set_fmt,

+ 88 - 0
target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch

@@ -0,0 +1,88 @@
+From 9c6694c24f26ea435165431d41c72451fadbd753 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 21 Jul 2023 12:07:16 +0100
+Subject: [PATCH] ASOC: dwc: Fix 16-bit audio handling
+
+IMO the Synopsys datasheet could be clearer in this area, but it seems
+that the DMA data ports (DMATX and DMARX) expect left and right samples
+in alternate writes; if a stereo pair is pushed in a single 32-bit
+write, the upper half is ignored, leading to double speed audio with a
+confused stereo image. Make sure the necessary changes happen by
+updating the DMA configuration data in the hw_params method.
+
+The set_bclk_ratio change was made at a time when it looked like it
+could be causing an error, but I think the division of responsibilities
+is clearer this way (and the kernel log clearer without the info-level
+message).
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/dwc/dwc-i2s.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -223,23 +223,34 @@ static int dw_i2s_hw_params(struct snd_p
+ {
+ 	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ 	struct i2s_clk_config_data *config = &dev->config;
++	union dw_i2s_snd_dma_data *dma_data = NULL;
+ 	int ret;
+ 
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++		dma_data = &dev->play_dma_data;
++	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++		dma_data = &dev->capture_dma_data;
++	else
++		return -1;
++
+ 	switch (params_format(params)) {
+ 	case SNDRV_PCM_FORMAT_S16_LE:
+ 		config->data_width = 16;
++		dma_data->dt.addr_width = 2;
+ 		dev->ccr = 0x00;
+ 		dev->xfer_resolution = 0x02;
+ 		break;
+ 
+ 	case SNDRV_PCM_FORMAT_S24_LE:
+ 		config->data_width = 24;
++		dma_data->dt.addr_width = 4;
+ 		dev->ccr = 0x08;
+ 		dev->xfer_resolution = 0x04;
+ 		break;
+ 
+ 	case SNDRV_PCM_FORMAT_S32_LE:
+ 		config->data_width = 32;
++		dma_data->dt.addr_width = 4;
+ 		dev->ccr = 0x10;
+ 		dev->xfer_resolution = 0x05;
+ 		break;
+@@ -418,24 +429,21 @@ static int dw_i2s_set_bclk_ratio(struct
+ 	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ 	struct i2s_clk_config_data *config = &dev->config;
+ 
+-	dev_err(dev->dev, "%s(%d)\n", __func__, ratio);
++	dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
++	if (ratio < config->data_width * 2)
++		return -EINVAL;
++
+ 	switch (ratio) {
+ 	case 32:
+-		config->data_width = 16;
+ 		dev->ccr = 0x00;
+-		dev->xfer_resolution = 0x02;
+ 		break;
+ 
+ 	case 48:
+-		config->data_width = 24;
+ 		dev->ccr = 0x08;
+-		dev->xfer_resolution = 0x04;
+ 		break;
+ 
+ 	case 64:
+-		config->data_width = 32;
+ 		dev->ccr = 0x10;
+-		dev->xfer_resolution = 0x05;
+ 		break;
+ 	default:
+ 		return -EINVAL;

+ 304 - 0
target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch

@@ -0,0 +1,304 @@
+From f476db1b71e8b82e5299168f963a2fefb7a395e2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 1 Sep 2023 14:07:48 +0100
+Subject: [PATCH] ASoC: bcm: Remove dependency on BCM2835 I2S
+
+These soundcard drivers don't rely on a specific I2S interface, so
+remove the dependency declarations.
+
+See: https://github.com/raspberrypi/linux-2712/issues/111
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/bcm/Kconfig | 40 +---------------------------------------
+ 1 file changed, 1 insertion(+), 39 deletions(-)
+
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -29,13 +29,11 @@ config SND_BCM63XX_I2S_WHISTLER
+ 
+ config SND_BCM2708_SOC_CHIPDIP_DAC
+          tristate "Support for the ChipDip DAC"
+-         depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+          help
+           Say Y or M if you want to add support for the ChipDip DAC soundcard
+ 
+ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
+ 	tristate "Support for Google voiceHAT soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_VOICEHAT
+ 	select SND_RPI_SIMPLE_SOUNDCARD
+ 	help
+@@ -43,7 +41,6 @@ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SO
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DAC
+         tristate "Support for HifiBerry DAC"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM5102A
+         select SND_RPI_SIMPLE_SOUNDCARD
+         help
+@@ -51,7 +48,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+         tristate "Support for HifiBerry DAC+"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM512x
+         select SND_SOC_TPA6130A2
+         select COMMON_CLK_HIFIBERRY_DACPRO
+@@ -60,7 +56,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD
+         tristate "Support for HifiBerry DAC+ HD"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM179X_I2C
+         select COMMON_CLK_HIFIBERRY_DACPLUSHD
+         help
+@@ -68,7 +63,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC
+         tristate "Support for HifiBerry DAC+ADC"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM512x_I2C
+ 	select SND_SOC_DMIC
+         select COMMON_CLK_HIFIBERRY_DACPRO
+@@ -77,7 +71,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO
+         tristate "Support for HifiBerry DAC+ADC PRO"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM512x_I2C
+         select SND_SOC_PCM186X_I2C
+         select SND_SOC_TPA6130A2
+@@ -87,29 +80,25 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP
+         tristate "Support for HifiBerry DAC+DSP"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_RPI_SIMPLE_SOUNDCARD
+         help
+          Say Y or M if you want to add support for HifiBerry DSP-DAC.
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_DIGI
+         tristate "Support for HifiBerry Digi"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_WM8804
+         help
+          Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board.
+ 
+ config SND_BCM2708_SOC_HIFIBERRY_AMP
+         tristate "Support for the HifiBerry Amp"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_TAS5713
+         select SND_RPI_SIMPLE_SOUNDCARD
+         help
+          Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
+ 
+- config SND_BCM2708_SOC_PIFI_40
++config SND_BCM2708_SOC_PIFI_40
+          tristate "Support for the PiFi-40 amp"
+-         depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+          select SND_SOC_TAS571X
+          select SND_PIFI_40
+          help
+@@ -117,7 +106,6 @@ config SND_BCM2708_SOC_HIFIBERRY_AMP
+ 
+ config SND_BCM2708_SOC_RPI_CIRRUS
+         tristate "Support for Cirrus Logic Audio Card"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_WM5102
+         select SND_SOC_WM8804
+         help
+@@ -126,7 +114,6 @@ config SND_BCM2708_SOC_RPI_CIRRUS
+ 
+ config SND_BCM2708_SOC_RPI_DAC
+         tristate "Support for RPi-DAC"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_PCM1794A
+         select SND_RPI_SIMPLE_SOUNDCARD
+         help
+@@ -134,14 +121,12 @@ config SND_BCM2708_SOC_RPI_DAC
+ 
+ config SND_BCM2708_SOC_RPI_PROTO
+ 	tristate "Support for Rpi-PROTO"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8731_I2C
+ 	help
+ 	  Say Y or M if you want to add support for Audio Codec Board PROTO (WM8731).
+ 
+ config SND_BCM2708_SOC_JUSTBOOM_BOTH
+ 	tristate "Support for simultaneous JustBoom Digi and JustBoom DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8804
+ 	select SND_SOC_PCM512x
+ 	help
+@@ -153,14 +138,12 @@ config SND_BCM2708_SOC_JUSTBOOM_BOTH
+ 
+ config SND_BCM2708_SOC_JUSTBOOM_DAC
+ 	tristate "Support for JustBoom DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x
+ 	help
+ 	  Say Y or M if you want to add support for JustBoom DAC.
+ 
+ config SND_BCM2708_SOC_JUSTBOOM_DIGI
+ 	tristate "Support for JustBoom Digi"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8804
+         select SND_RPI_WM8804_SOUNDCARD
+ 	help
+@@ -168,21 +151,18 @@ config SND_BCM2708_SOC_JUSTBOOM_DIGI
+ 
+ config SND_BCM2708_SOC_IQAUDIO_CODEC
+ 	tristate "Support for IQaudIO-CODEC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_DA7213
+ 	help
+ 	  Say Y or M if you want to add support for IQaudIO-CODEC.
+ 
+ config SND_BCM2708_SOC_IQAUDIO_DAC
+ 	tristate "Support for IQaudIO-DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x_I2C
+ 	help
+ 	  Say Y or M if you want to add support for IQaudIO-DAC.
+ 
+ config SND_BCM2708_SOC_IQAUDIO_DIGI
+ 	tristate "Support for IQAudIO Digi"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8804
+ 	select SND_RPI_WM8804_SOUNDCARD
+ 	help
+@@ -190,14 +170,12 @@ config SND_BCM2708_SOC_IQAUDIO_DIGI
+ 
+ config SND_BCM2708_SOC_I_SABRE_Q2M
+         tristate "Support for Audiophonics I-Sabre Q2M DAC"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_I_SABRE_CODEC
+         help
+         Say Y or M if you want to add support for Audiophonics I-SABRE Q2M DAC
+ 
+ config SND_BCM2708_SOC_ADAU1977_ADC
+ 	tristate "Support for ADAU1977 ADC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_ADAU1977_I2C
+ 	select SND_RPI_SIMPLE_SOUNDCARD
+ 	help
+@@ -205,35 +183,30 @@ config SND_BCM2708_SOC_ADAU1977_ADC
+ 
+ config SND_AUDIOINJECTOR_PI_SOUNDCARD
+ 	tristate "Support for audioinjector.net Pi add on soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8731_I2C
+ 	help
+ 	  Say Y or M if you want to add support for audioinjector.net Pi Hat
+ 
+ config SND_AUDIOINJECTOR_OCTO_SOUNDCARD
+ 	tristate "Support for audioinjector.net Octo channel (Hat) soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_CS42XX8_I2C
+ 	help
+ 	  Say Y or M if you want to add support for audioinjector.net octo add on
+ 
+ config SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD
+ 	tristate "Support for audioinjector.net isolated DAC and ADC soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_CS4271_I2C
+ 	help
+ 	  Say Y or M if you want to add support for audioinjector.net isolated soundcard
+ 
+ config SND_AUDIOSENSE_PI
+ 	tristate "Support for AudioSense Add-On Soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_TLV320AIC32X4_I2C
+ 	help
+ 	  Say Y or M if you want to add support for tlv320aic32x4 add-on
+ 
+ config SND_DIGIDAC1_SOUNDCARD
+         tristate "Support for Red Rocks Audio DigiDAC1"
+-        depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+         select SND_SOC_WM8804
+         select SND_SOC_WM8741
+         help
+@@ -241,35 +214,30 @@ config SND_DIGIDAC1_SOUNDCARD
+ 
+ config SND_BCM2708_SOC_DIONAUDIO_LOCO
+ 	tristate "Support for Dion Audio LOCO DAC-AMP"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM5102a
+ 	help
+ 	  Say Y or M if you want to add support for Dion Audio LOCO.
+ 
+ config SND_BCM2708_SOC_DIONAUDIO_LOCO_V2
+ 	tristate "Support for Dion Audio LOCO-V2 DAC-AMP"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM5122
+ 	help
+ 	  Say Y or M if you want to add support for Dion Audio LOCO-V2.
+ 
+ config SND_BCM2708_SOC_ALLO_PIANO_DAC
+ 	tristate "Support for Allo Piano DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x_I2C
+ 	help
+ 	  Say Y or M if you want to add support for Allo Piano DAC.
+ 
+ config SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS
+ 	tristate "Support for Allo Piano DAC Plus"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x_I2C
+ 	help
+ 	  Say Y or M if you want to add support for Allo Piano DAC Plus.
+ 
+ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+ 	tristate "Support for Allo Boss DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_PCM512x_I2C
+ 	select COMMON_CLK_HIFIBERRY_DACPRO
+ 	help
+@@ -277,7 +245,6 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+ 
+ config SND_BCM2708_SOC_ALLO_BOSS2_DAC
+ 	tristate "Support for Allo Boss2 DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	depends on I2C
+ 	select REGMAP_I2C
+ 	select SND_AUDIO_GRAPH_CARD
+@@ -286,7 +253,6 @@ config SND_BCM2708_SOC_ALLO_BOSS2_DAC
+ 
+ config SND_BCM2708_SOC_ALLO_DIGIONE
+ 	tristate "Support for Allo DigiOne"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_WM8804
+ 	select SND_RPI_WM8804_SOUNDCARD
+ 	help
+@@ -294,7 +260,6 @@ config SND_BCM2708_SOC_ALLO_DIGIONE
+ 
+ config SND_BCM2708_SOC_ALLO_KATANA_DAC
+ 	tristate "Support for Allo Katana DAC"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	depends on I2C
+ 	select REGMAP_I2C
+ 	select SND_AUDIO_GRAPH_CARD
+@@ -303,14 +268,12 @@ config SND_BCM2708_SOC_ALLO_KATANA_DAC
+ 
+ config SND_BCM2708_SOC_FE_PI_AUDIO
+ 	tristate "Support for Fe-Pi-Audio"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_SGTL5000
+ 	help
+ 	  Say Y or M if you want to add support for Fe-Pi-Audio.
+ 
+ config SND_PISOUND
+ 	tristate "Support for Blokas Labs pisound"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_RAWMIDI
+ 	help
+ 	  Say Y or M if you want to add support for Blokas Labs pisound.
+@@ -328,7 +291,6 @@ config SND_RPI_WM8804_SOUNDCARD
+ 
+ config SND_DACBERRY400
+ 	tristate "Support for DACBERRY400 Soundcard"
+-	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ 	select SND_SOC_TLV320AIC3X_I2C
+ 	help
+ 	  Say Y or M if you want to add support for tlv320aic3x add-on

+ 343 - 0
target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch

@@ -0,0 +1,343 @@
+From cad3c92ff0c1a5fa539d08b695b0f6b326924890 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 2 Mar 2023 18:04:42 +0000
+Subject: [PATCH] hwmon: Add RP1 ADC and temperature driver
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/hwmon/Kconfig   |   7 +
+ drivers/hwmon/Makefile  |   1 +
+ drivers/hwmon/rp1-adc.c | 301 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 309 insertions(+)
+ create mode 100644 drivers/hwmon/rp1-adc.c
+
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2331,6 +2331,13 @@ config SENSORS_INTEL_M10_BMC_HWMON
+ 	  sensors monitor various telemetry data of different components on the
+ 	  card, e.g. board temperature, FPGA core temperature/voltage/current.
+ 
++config SENSORS_RP1_ADC
++	tristate "RP1 ADC and temperature sensor driver"
++	depends on MFD_RP1
++	help
++	  Say yes here to enable support for the voltage and temperature
++	  sensors of the Raspberry Pi RP1 peripheral chip.
++
+ if ACPI
+ 
+ comment "ACPI drivers"
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -173,6 +173,7 @@ obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591
+ obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
+ obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
+ obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
++obj-$(CONFIG_SENSORS_RP1_ADC)	+= rp1-adc.o
+ obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
+ obj-$(CONFIG_SENSORS_SBTSI)	+= sbtsi_temp.o
+ obj-$(CONFIG_SENSORS_SBRMI)	+= sbrmi.o
+--- /dev/null
++++ b/drivers/hwmon/rp1-adc.c
+@@ -0,0 +1,301 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for the RP1 ADC and temperature sensor
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/mod_devicetable.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++
++#define MODULE_NAME	"rp1-adc"
++
++#define RP1_ADC_CS		0x00
++#define RP1_ADC_RESULT		0x04
++#define RP1_ADC_FCS		0x08
++#define RP1_ADC_FIFO		0x0c
++#define RP1_ADC_DIV		0x10
++
++#define RP1_ADC_INTR		0x14
++#define RP1_ADC_INTE		0x18
++#define RP1_ADC_INTF		0x1c
++#define RP1_ADC_INTS		0x20
++
++#define RP1_ADC_RWTYPE_SET	0x2000
++#define RP1_ADC_RWTYPE_CLR	0x3000
++
++#define RP1_ADC_CS_RROBIN_MASK	0x1f
++#define RP1_ADC_CS_RROBIN_SHIFT	16
++#define RP1_ADC_CS_AINSEL_MASK	0x7
++#define RP1_ADC_CS_AINSEL_SHIFT	12
++#define RP1_ADC_CS_ERR_STICKY	0x400
++#define RP1_ADC_CS_ERR		0x200
++#define RP1_ADC_CS_READY	0x100
++#define RP1_ADC_CS_START_MANY	0x8
++#define RP1_ADC_CS_START_ONCE	0x4
++#define RP1_ADC_CS_TS_EN	0x2
++#define RP1_ADC_CS_EN		0x1
++
++#define RP1_ADC_FCS_THRESH_MASK	0xf
++#define RP1_ADC_FCS_THRESH_SHIFT	24
++#define RP1_ADC_FCS_LEVEL_MASK	0xf
++#define RP1_ADC_FCS_LEVEL_SHIFT	16
++#define RP1_ADC_FCS_OVER	0x800
++#define RP1_ADC_FCS_UNDER	0x400
++#define RP1_ADC_FCS_FULL	0x200
++#define RP1_ADC_FCS_EMPTY	0x100
++#define RP1_ADC_FCS_DREQ_EN	0x8
++#define RP1_ADC_FCS_ERR		0x4
++#define RP1_ADC_FCS_SHIFR	0x2
++#define RP1_ADC_FCS_EN		0x1
++
++#define RP1_ADC_FIFO_ERR	0x8000
++#define RP1_ADC_FIFO_VAL_MASK	0xfff
++
++#define RP1_ADC_DIV_INT_MASK	0xffff
++#define RP1_ADC_DIV_INT_SHIFT	8
++#define RP1_ADC_DIV_FRAC_MASK	0xff
++#define RP1_ADC_DIV_FRAC_SHIFT	0
++
++struct rp1_adc_data {
++	void __iomem *base;
++	spinlock_t lock;
++	struct device *hwmon_dev;
++	int vref_mv;
++};
++
++static int rp1_adc_ready_wait(struct rp1_adc_data *data)
++{
++	int retries = 10;
++
++	while (retries && !(readl(data->base + RP1_ADC_CS) & RP1_ADC_CS_READY))
++		retries--;
++
++	return retries ? 0 : -EIO;
++}
++
++static int rp1_adc_read(struct rp1_adc_data *data,
++			struct device_attribute *devattr, unsigned int *val)
++{
++	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++	int channel = attr->index;
++	int ret;
++
++	spin_lock(&data->lock);
++
++	writel(RP1_ADC_CS_AINSEL_MASK << RP1_ADC_CS_AINSEL_SHIFT,
++	       data->base + RP1_ADC_RWTYPE_CLR + RP1_ADC_CS);
++	writel(channel << RP1_ADC_CS_AINSEL_SHIFT,
++	       data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++	writel(RP1_ADC_CS_START_ONCE,
++	       data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++
++	ret = rp1_adc_ready_wait(data);
++	if (!ret)
++		*val = readl(data->base + RP1_ADC_RESULT);
++
++	spin_unlock(&data->lock);
++
++	return ret;
++}
++
++static int rp1_adc_to_mv(struct rp1_adc_data *data, unsigned int val)
++{
++	return ((u64)data->vref_mv * val) / 0xfff;
++}
++
++static ssize_t rp1_adc_show(struct device *dev,
++			    struct device_attribute *devattr,
++			    char *buf)
++{
++	struct rp1_adc_data *data = dev_get_drvdata(dev);
++	unsigned int val;
++	int ret;
++
++	ret = rp1_adc_read(data, devattr, &val);
++	if (ret)
++		return ret;
++
++	return sprintf(buf, "%d\n", rp1_adc_to_mv(data, val));
++}
++
++static ssize_t rp1_adc_temp_show(struct device *dev,
++				 struct device_attribute *devattr,
++				 char *buf)
++{
++	struct rp1_adc_data *data = dev_get_drvdata(dev);
++	unsigned int val;
++	int ret, mv, mc;
++
++	writel(RP1_ADC_CS_TS_EN,
++	       data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++	ret = rp1_adc_read(data, devattr, &val);
++	if (ret)
++		return ret;
++
++	mv = rp1_adc_to_mv(data, val);
++
++	/* T = 27 - (ADC_voltage - 0.706)/0.001721 */
++
++	mc = 27000 - DIV_ROUND_CLOSEST((mv - 706) * (s64)1000000, 1721);
++
++	return sprintf(buf, "%d\n", mc);
++}
++
++static ssize_t rp1_adc_raw_show(struct device *dev,
++				struct device_attribute *devattr,
++				char *buf)
++{
++	struct rp1_adc_data *data = dev_get_drvdata(dev);
++	unsigned int val;
++	int ret = rp1_adc_read(data, devattr, &val);
++
++	if (ret)
++		return ret;
++
++	return sprintf(buf, "%u\n", val);
++}
++
++static ssize_t rp1_adc_temp_raw_show(struct device *dev,
++				     struct device_attribute *devattr,
++				     char *buf)
++{
++	struct rp1_adc_data *data = dev_get_drvdata(dev);
++	unsigned int val;
++	int ret = rp1_adc_read(data, devattr, &val);
++
++	if (ret)
++		return ret;
++
++	return sprintf(buf, "%u\n", val);
++}
++
++static SENSOR_DEVICE_ATTR_RO(in1_input, rp1_adc, 0);
++static SENSOR_DEVICE_ATTR_RO(in2_input, rp1_adc, 1);
++static SENSOR_DEVICE_ATTR_RO(in3_input, rp1_adc, 2);
++static SENSOR_DEVICE_ATTR_RO(in4_input, rp1_adc, 3);
++static SENSOR_DEVICE_ATTR_RO(temp1_input, rp1_adc_temp, 4);
++static SENSOR_DEVICE_ATTR_RO(in1_raw, rp1_adc_raw, 0);
++static SENSOR_DEVICE_ATTR_RO(in2_raw, rp1_adc_raw, 1);
++static SENSOR_DEVICE_ATTR_RO(in3_raw, rp1_adc_raw, 2);
++static SENSOR_DEVICE_ATTR_RO(in4_raw, rp1_adc_raw, 3);
++static SENSOR_DEVICE_ATTR_RO(temp1_raw, rp1_adc_temp_raw, 4);
++
++static struct attribute *rp1_adc_attrs[] = {
++	&sensor_dev_attr_in1_input.dev_attr.attr,
++	&sensor_dev_attr_in2_input.dev_attr.attr,
++	&sensor_dev_attr_in3_input.dev_attr.attr,
++	&sensor_dev_attr_in4_input.dev_attr.attr,
++	&sensor_dev_attr_temp1_input.dev_attr.attr,
++	&sensor_dev_attr_in1_raw.dev_attr.attr,
++	&sensor_dev_attr_in2_raw.dev_attr.attr,
++	&sensor_dev_attr_in3_raw.dev_attr.attr,
++	&sensor_dev_attr_in4_raw.dev_attr.attr,
++	&sensor_dev_attr_temp1_raw.dev_attr.attr,
++	NULL
++};
++
++static umode_t rp1_adc_is_visible(struct kobject *kobj,
++				  struct attribute *attr, int index)
++{
++	return 0444;
++}
++
++static const struct attribute_group rp1_adc_group = {
++	.attrs = rp1_adc_attrs,
++	.is_visible = rp1_adc_is_visible,
++};
++__ATTRIBUTE_GROUPS(rp1_adc);
++
++static int __init rp1_adc_probe(struct platform_device *pdev)
++{
++	struct rp1_adc_data *data;
++	struct regulator *reg;
++	struct clk *clk;
++	int vref_uv, ret;
++
++	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	spin_lock_init(&data->lock);
++
++	data->base = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(data->base))
++		return PTR_ERR(data->base);
++
++	platform_set_drvdata(pdev, data);
++
++	clk = devm_clk_get(&pdev->dev, NULL);
++	if (IS_ERR(clk))
++		return -ENODEV;
++
++	clk_set_rate(clk, 50000000);
++	clk_prepare_enable(clk);
++
++	reg = devm_regulator_get(&pdev->dev, "vref");
++	if (IS_ERR(reg))
++		return PTR_ERR(reg);
++
++	vref_uv = regulator_get_voltage(reg);
++	data->vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000);
++
++	data->hwmon_dev =
++	    devm_hwmon_device_register_with_groups(&pdev->dev,
++						   "rp1_adc",
++						   data,
++						   rp1_adc_groups);
++	if (IS_ERR(data->hwmon_dev)) {
++		ret = PTR_ERR(data->hwmon_dev);
++		dev_err(&pdev->dev, "hwmon_device_register failed with %d.\n", ret);
++		goto err_register;
++	}
++
++	/* Disable interrupts */
++	writel(0, data->base + RP1_ADC_INTE);
++
++	/* Enable the block, clearing any sticky error */
++	writel(RP1_ADC_CS_EN | RP1_ADC_CS_ERR_STICKY, data->base + RP1_ADC_CS);
++
++	return 0;
++
++err_register:
++	sysfs_remove_group(&pdev->dev.kobj, &rp1_adc_group);
++
++	return ret;
++}
++
++static int rp1_adc_remove(struct platform_device *pdev)
++{
++	struct rp1_adc_data *data = platform_get_drvdata(pdev);
++
++	hwmon_device_unregister(data->hwmon_dev);
++
++	return 0;
++}
++
++static const struct of_device_id rp1_adc_dt_ids[] = {
++	{ .compatible = "raspberrypi,rp1-adc", },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, rp1_adc_dt_ids);
++
++static struct platform_driver rp1_adc_driver = {
++	.remove		= rp1_adc_remove,
++	.driver		= {
++		.name	= MODULE_NAME,
++		.of_match_table = rp1_adc_dt_ids,
++	},
++};
++
++module_platform_driver_probe(rp1_adc_driver, rp1_adc_probe);
++
++MODULE_DESCRIPTION("RP1 ADC driver");
++MODULE_AUTHOR("Phil Elwell <[email protected]>");
++MODULE_LICENSE("GPL");

+ 69 - 0
target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch

@@ -0,0 +1,69 @@
+From 0c7aeb96fd3ab68011ba6c24239c501190890308 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 8 Mar 2023 14:27:58 +0000
+Subject: [PATCH] mfd: bcm2835-pm: Add support for BCM2712
+
+BCM2712 lacks the "asb" and "rpivid_asb" register ranges, but still
+requires the use of the bcm2835-power driver to reset the V3D block.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/mfd/bcm2835-pm.c | 28 +++++++++++++++++++---------
+ 1 file changed, 19 insertions(+), 9 deletions(-)
+
+--- a/drivers/mfd/bcm2835-pm.c
++++ b/drivers/mfd/bcm2835-pm.c
+@@ -69,12 +69,30 @@ static int bcm2835_pm_get_pdata(struct p
+ 	return 0;
+ }
+ 
++static const struct of_device_id bcm2835_pm_of_match[] = {
++	{ .compatible = "brcm,bcm2835-pm-wdt", },
++	{ .compatible = "brcm,bcm2835-pm", },
++	{ .compatible = "brcm,bcm2711-pm", },
++	{ .compatible = "brcm,bcm2712-pm", .data = (const void *)1},
++	{},
++};
++MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
++
+ static int bcm2835_pm_probe(struct platform_device *pdev)
+ {
++	const struct of_device_id *of_id;
+ 	struct device *dev = &pdev->dev;
+ 	struct bcm2835_pm *pm;
++	bool is_2712;
+ 	int ret;
+ 
++	of_id = of_match_node(bcm2835_pm_of_match, pdev->dev.of_node);
++	if (!of_id) {
++		dev_err(&pdev->dev, "Failed to match compatible string\n");
++		return -EINVAL;
++	}
++	is_2712 = !!of_id->data;
++
+ 	pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
+ 	if (!pm)
+ 		return -ENOMEM;
+@@ -97,21 +115,13 @@ static int bcm2835_pm_probe(struct platf
+ 	 * bcm2835-pm binding as the key for whether we can reference
+ 	 * the full PM register range and support power domains.
+ 	 */
+-	if (pm->asb)
++	if (pm->asb || is_2712)
+ 		return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
+ 					    ARRAY_SIZE(bcm2835_power_devs),
+ 					    NULL, 0, NULL);
+ 	return 0;
+ }
+ 
+-static const struct of_device_id bcm2835_pm_of_match[] = {
+-	{ .compatible = "brcm,bcm2835-pm-wdt", },
+-	{ .compatible = "brcm,bcm2835-pm", },
+-	{ .compatible = "brcm,bcm2711-pm", },
+-	{},
+-};
+-MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
+-
+ static struct platform_driver bcm2835_pm_driver = {
+ 	.probe		= bcm2835_pm_probe,
+ 	.driver = {

+ 76 - 0
target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch

@@ -0,0 +1,76 @@
+From 9cf85a95eeb239a079a3485bd1d0447431bdc7f1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 8 Mar 2023 14:42:48 +0000
+Subject: [PATCH] soc: bcm: bcm2835-power: Add support for BCM2712
+
+BCM2712 has a PM block but neither ASB nor RPIVID_ASB. Use the absence
+of the "asb" register range to indicate BCM2712 and its different PM
+register range.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/soc/bcm/bcm2835-power.c | 29 +++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+--- a/drivers/soc/bcm/bcm2835-power.c
++++ b/drivers/soc/bcm/bcm2835-power.c
+@@ -79,6 +79,7 @@
+ #define PM_IMAGE			0x108
+ #define PM_GRAFX			0x10c
+ #define PM_PROC				0x110
++#define PM_GRAFX_2712			0x304
+ #define PM_ENAB				BIT(12)
+ #define PM_ISPRSTN			BIT(8)
+ #define PM_H264RSTN			BIT(7)
+@@ -381,6 +382,9 @@ static int bcm2835_power_pd_power_on(str
+ 		return bcm2835_power_power_on(pd, PM_GRAFX);
+ 
+ 	case BCM2835_POWER_DOMAIN_GRAFX_V3D:
++		if (!power->asb)
++			return bcm2835_asb_power_on(pd, PM_GRAFX_2712,
++						    0, 0, PM_V3DRSTN);
+ 		return bcm2835_asb_power_on(pd, PM_GRAFX,
+ 					    ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
+ 					    PM_V3DRSTN);
+@@ -447,6 +451,9 @@ static int bcm2835_power_pd_power_off(st
+ 		return bcm2835_power_power_off(pd, PM_GRAFX);
+ 
+ 	case BCM2835_POWER_DOMAIN_GRAFX_V3D:
++		if (!power->asb)
++			return bcm2835_asb_power_off(pd, PM_GRAFX_2712,
++						    0, 0, PM_V3DRSTN);
+ 		return bcm2835_asb_power_off(pd, PM_GRAFX,
+ 					     ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
+ 					     PM_V3DRSTN);
+@@ -642,19 +649,21 @@ static int bcm2835_power_probe(struct pl
+ 	power->asb = pm->asb;
+ 	power->rpivid_asb = pm->rpivid_asb;
+ 
+-	id = readl(power->asb + ASB_AXI_BRDG_ID);
+-	if (id != BCM2835_BRDG_ID /* "BRDG" */) {
+-		dev_err(dev, "ASB register ID returned 0x%08x\n", id);
+-		return -ENODEV;
+-	}
+-
+-	if (power->rpivid_asb) {
+-		id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
++	if (power->asb) {
++		id = readl(power->asb + ASB_AXI_BRDG_ID);
+ 		if (id != BCM2835_BRDG_ID /* "BRDG" */) {
+-			dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
+-				     id);
++			dev_err(dev, "ASB register ID returned 0x%08x\n", id);
+ 			return -ENODEV;
+ 		}
++
++		if (power->rpivid_asb) {
++			id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
++			if (id != BCM2835_BRDG_ID /* "BRDG" */) {
++				dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
++					id);
++				return -ENODEV;
++			}
++		}
+ 	}
+ 
+ 	power->pd_xlate.domains = devm_kcalloc(dev,

+ 150 - 0
target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch

@@ -0,0 +1,150 @@
+From 380c336af070edf85826abbb0057bf92a03ec466 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Wed, 1 Mar 2023 17:57:11 +0000
+Subject: [PATCH] drivers: spi: Fix spi-gpio to correctly implement
+ sck-idle-input
+
+Formerly, if configured using DT, CS GPIOs were driven from spi.c
+and it was possible for CS to be asserted (low) *before* starting
+to drive SCK. CS GPIOs have been brought under control of this
+driver in both ACPI and DT cases, with a fixup for GPIO polarity.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/spi/spi-gpio.c | 74 +++++++++++++++++++++++++++++-------------
+ 1 file changed, 51 insertions(+), 23 deletions(-)
+
+--- a/drivers/spi/spi-gpio.c
++++ b/drivers/spi/spi-gpio.c
+@@ -37,6 +37,7 @@ struct spi_gpio {
+ 	struct gpio_desc		*mosi;
+ 	bool				sck_idle_input;
+ 	struct gpio_desc		**cs_gpios;
++	bool                            cs_dont_invert;
+ };
+ 
+ /*----------------------------------------------------------------------*/
+@@ -233,12 +234,18 @@ static void spi_gpio_chipselect(struct s
+ 			gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
+ 	}
+ 
+-	/* Drive chip select line, if we have one */
++	/*
++	 * Drive chip select line, if we have one.
++	 * SPI chip selects are normally active-low, but when
++	 * cs_dont_invert is set, we assume their polarity is
++	 * controlled by the GPIO, and write '1' to assert.
++	 */
+ 	if (spi_gpio->cs_gpios) {
+ 		struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select];
++		int val = ((spi->mode & SPI_CS_HIGH) || spi_gpio->cs_dont_invert) ?
++			is_active : !is_active;
+ 
+-		/* SPI chip selects are normally active-low */
+-		gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
++		gpiod_set_value_cansleep(cs, val);
+ 	}
+ 
+ 	if (spi_gpio->sck_idle_input && !is_active)
+@@ -254,12 +261,14 @@ static int spi_gpio_setup(struct spi_dev
+ 	/*
+ 	 * The CS GPIOs have already been
+ 	 * initialized from the descriptor lookup.
++	 * Here we set them to the non-asserted state.
+ 	 */
+ 	if (spi_gpio->cs_gpios) {
+ 		cs = spi_gpio->cs_gpios[spi->chip_select];
+ 		if (!spi->controller_state && cs)
+ 			status = gpiod_direction_output(cs,
+-						  !(spi->mode & SPI_CS_HIGH));
++							!((spi->mode & SPI_CS_HIGH) ||
++							   spi_gpio->cs_dont_invert));
+ 	}
+ 
+ 	if (!status)
+@@ -336,6 +345,38 @@ static int spi_gpio_request(struct devic
+ 	return PTR_ERR_OR_ZERO(spi_gpio->sck);
+ }
+ 
++/*
++ * In order to implement "sck-idle-input" (which requires SCK
++ * direction and CS level to be switched in a particular order),
++ * we need to control GPIO chip selects from within this driver.
++ */
++
++static int spi_gpio_probe_get_cs_gpios(struct device *dev,
++				       struct spi_master *master,
++				       bool gpio_defines_polarity)
++{
++	int i;
++	struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
++
++	spi_gpio->cs_dont_invert = gpio_defines_polarity;
++	spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
++					  sizeof(*spi_gpio->cs_gpios),
++					  GFP_KERNEL);
++	if (!spi_gpio->cs_gpios)
++		return -ENOMEM;
++
++	for (i = 0; i < master->num_chipselect; i++) {
++		spi_gpio->cs_gpios[i] =
++			devm_gpiod_get_index(dev, "cs", i,
++					     gpio_defines_polarity ?
++						GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
++		if (IS_ERR(spi_gpio->cs_gpios[i]))
++			return PTR_ERR(spi_gpio->cs_gpios[i]);
++	}
++
++	return 0;
++}
++
+ #ifdef CONFIG_OF
+ static const struct of_device_id spi_gpio_dt_ids[] = {
+ 	{ .compatible = "spi-gpio" },
+@@ -346,10 +387,12 @@ MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids)
+ static int spi_gpio_probe_dt(struct platform_device *pdev,
+ 			     struct spi_master *master)
+ {
+-	master->dev.of_node = pdev->dev.of_node;
+-	master->use_gpio_descriptors = true;
++	struct device *dev = &pdev->dev;
+ 
+-	return 0;
++	master->dev.of_node = dev->of_node;
++	master->num_chipselect = gpiod_count(dev, "cs");
++
++	return spi_gpio_probe_get_cs_gpios(dev, master, true);
+ }
+ #else
+ static inline int spi_gpio_probe_dt(struct platform_device *pdev,
+@@ -364,8 +407,6 @@ static int spi_gpio_probe_pdata(struct p
+ {
+ 	struct device *dev = &pdev->dev;
+ 	struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
+-	struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
+-	int i;
+ 
+ #ifdef GENERIC_BITBANG
+ 	if (!pdata || !pdata->num_chipselect)
+@@ -377,20 +418,7 @@ static int spi_gpio_probe_pdata(struct p
+ 	 */
+ 	master->num_chipselect = pdata->num_chipselect ?: 1;
+ 
+-	spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
+-					  sizeof(*spi_gpio->cs_gpios),
+-					  GFP_KERNEL);
+-	if (!spi_gpio->cs_gpios)
+-		return -ENOMEM;
+-
+-	for (i = 0; i < master->num_chipselect; i++) {
+-		spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
+-							     GPIOD_OUT_HIGH);
+-		if (IS_ERR(spi_gpio->cs_gpios[i]))
+-			return PTR_ERR(spi_gpio->cs_gpios[i]);
+-	}
+-
+-	return 0;
++	return spi_gpio_probe_get_cs_gpios(dev, master, false);
+ }
+ 
+ static int spi_gpio_probe(struct platform_device *pdev)

+ 55 - 0
target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch

@@ -0,0 +1,55 @@
+From 586f87307e75552292cfc6c76b81cd38d5ec31e2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Mon, 4 Sep 2023 10:57:47 +0100
+Subject: [PATCH] spi: spi-gpio: Implement spidelay when requested bit rate <=
+ 1 Mbps
+
+Formerly the delay was omitted as bit-banged SPI seldom achieved
+even one Mbit/s; but some modern platforms can run faster, and
+some SPI devices may need to be clocked slower.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/spi/spi-gpio.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-gpio.c
++++ b/drivers/spi/spi-gpio.c
+@@ -11,12 +11,12 @@
+ #include <linux/gpio/consumer.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
++#include <linux/delay.h>
+ 
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi_bitbang.h>
+ #include <linux/spi/spi_gpio.h>
+ 
+-
+ /*
+  * This bitbanging SPI master driver should help make systems usable
+  * when a native hardware SPI engine is not available, perhaps because
+@@ -111,12 +111,18 @@ static inline int getmiso(const struct s
+ }
+ 
+ /*
+- * NOTE:  this clocks "as fast as we can".  It "should" be a function of the
+- * requested device clock.  Software overhead means we usually have trouble
+- * reaching even one Mbit/sec (except when we can inline bitops), so for now
+- * we'll just assume we never need additional per-bit slowdowns.
++ * Generic bit-banged GPIO SPI might free-run at something in the range
++ * 1Mbps ~ 10Mbps (depending on the platform), and some SPI devices may
++ * need to be clocked at a lower rate. ndelay() is often implemented by
++ * udelay() with rounding up, so do the delay only for nsecs >= 500
++ * (<= 1Mbps). The conditional test adds a small overhead.
+  */
+-#define spidelay(nsecs)	do {} while (0)
++
++static inline void spidelay(unsigned long nsecs)
++{
++	if (nsecs >= 500)
++		ndelay(nsecs);
++}
+ 
+ #include "spi-bitbang-txrx.h"
+ 

+ 672 - 0
target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch

@@ -0,0 +1,672 @@
+From 3f949caeef21269afc67dd62ae9826204f215934 Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <[email protected]>
+Date: Thu, 2 Mar 2023 11:49:46 +0100
+Subject: [PATCH] drm/v3d: fix up register addresses for V3D 7.x
+
+v2: fix kernel panic with debug-fs interface to list registers
+---
+ drivers/gpu/drm/v3d/v3d_debugfs.c | 177 +++++++++++++++++-------------
+ drivers/gpu/drm/v3d/v3d_gem.c     |   3 +
+ drivers/gpu/drm/v3d/v3d_irq.c     |  47 ++++----
+ drivers/gpu/drm/v3d/v3d_regs.h    |  51 ++++++++-
+ drivers/gpu/drm/v3d/v3d_sched.c   |  41 ++++---
+ 5 files changed, 204 insertions(+), 115 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
++++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
+@@ -13,69 +13,83 @@
+ #include "v3d_drv.h"
+ #include "v3d_regs.h"
+ 
+-#define REGDEF(reg) { reg, #reg }
++#define REGDEF(min_ver, max_ver, reg) { min_ver, max_ver, reg, #reg }
+ struct v3d_reg_def {
++	u32 min_ver;
++	u32 max_ver;
+ 	u32 reg;
+ 	const char *name;
+ };
+ 
+ static const struct v3d_reg_def v3d_hub_reg_defs[] = {
+-	REGDEF(V3D_HUB_AXICFG),
+-	REGDEF(V3D_HUB_UIFCFG),
+-	REGDEF(V3D_HUB_IDENT0),
+-	REGDEF(V3D_HUB_IDENT1),
+-	REGDEF(V3D_HUB_IDENT2),
+-	REGDEF(V3D_HUB_IDENT3),
+-	REGDEF(V3D_HUB_INT_STS),
+-	REGDEF(V3D_HUB_INT_MSK_STS),
+-
+-	REGDEF(V3D_MMU_CTL),
+-	REGDEF(V3D_MMU_VIO_ADDR),
+-	REGDEF(V3D_MMU_VIO_ID),
+-	REGDEF(V3D_MMU_DEBUG_INFO),
++	REGDEF(33, 42, V3D_HUB_AXICFG),
++	REGDEF(33, 71, V3D_HUB_UIFCFG),
++	REGDEF(33, 71, V3D_HUB_IDENT0),
++	REGDEF(33, 71, V3D_HUB_IDENT1),
++	REGDEF(33, 71, V3D_HUB_IDENT2),
++	REGDEF(33, 71, V3D_HUB_IDENT3),
++	REGDEF(33, 71, V3D_HUB_INT_STS),
++	REGDEF(33, 71, V3D_HUB_INT_MSK_STS),
++
++	REGDEF(33, 71, V3D_MMU_CTL),
++	REGDEF(33, 71, V3D_MMU_VIO_ADDR),
++	REGDEF(33, 71, V3D_MMU_VIO_ID),
++	REGDEF(33, 71, V3D_MMU_DEBUG_INFO),
++
++	REGDEF(71, 71, V3D_V7_GMP_STATUS),
++	REGDEF(71, 71, V3D_V7_GMP_CFG),
++	REGDEF(71, 71, V3D_V7_GMP_VIO_ADDR),
+ };
+ 
+ static const struct v3d_reg_def v3d_gca_reg_defs[] = {
+-	REGDEF(V3D_GCA_SAFE_SHUTDOWN),
+-	REGDEF(V3D_GCA_SAFE_SHUTDOWN_ACK),
++	REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN),
++	REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN_ACK),
+ };
+ 
+ static const struct v3d_reg_def v3d_core_reg_defs[] = {
+-	REGDEF(V3D_CTL_IDENT0),
+-	REGDEF(V3D_CTL_IDENT1),
+-	REGDEF(V3D_CTL_IDENT2),
+-	REGDEF(V3D_CTL_MISCCFG),
+-	REGDEF(V3D_CTL_INT_STS),
+-	REGDEF(V3D_CTL_INT_MSK_STS),
+-	REGDEF(V3D_CLE_CT0CS),
+-	REGDEF(V3D_CLE_CT0CA),
+-	REGDEF(V3D_CLE_CT0EA),
+-	REGDEF(V3D_CLE_CT1CS),
+-	REGDEF(V3D_CLE_CT1CA),
+-	REGDEF(V3D_CLE_CT1EA),
+-
+-	REGDEF(V3D_PTB_BPCA),
+-	REGDEF(V3D_PTB_BPCS),
+-
+-	REGDEF(V3D_GMP_STATUS),
+-	REGDEF(V3D_GMP_CFG),
+-	REGDEF(V3D_GMP_VIO_ADDR),
+-
+-	REGDEF(V3D_ERR_FDBGO),
+-	REGDEF(V3D_ERR_FDBGB),
+-	REGDEF(V3D_ERR_FDBGS),
+-	REGDEF(V3D_ERR_STAT),
++	REGDEF(33, 71, V3D_CTL_IDENT0),
++	REGDEF(33, 71, V3D_CTL_IDENT1),
++	REGDEF(33, 71, V3D_CTL_IDENT2),
++	REGDEF(33, 71, V3D_CTL_MISCCFG),
++	REGDEF(33, 71, V3D_CTL_INT_STS),
++	REGDEF(33, 71, V3D_CTL_INT_MSK_STS),
++	REGDEF(33, 71, V3D_CLE_CT0CS),
++	REGDEF(33, 71, V3D_CLE_CT0CA),
++	REGDEF(33, 71, V3D_CLE_CT0EA),
++	REGDEF(33, 71, V3D_CLE_CT1CS),
++	REGDEF(33, 71, V3D_CLE_CT1CA),
++	REGDEF(33, 71, V3D_CLE_CT1EA),
++
++	REGDEF(33, 71, V3D_PTB_BPCA),
++	REGDEF(33, 71, V3D_PTB_BPCS),
++
++	REGDEF(33, 41, V3D_GMP_STATUS),
++	REGDEF(33, 41, V3D_GMP_CFG),
++	REGDEF(33, 41, V3D_GMP_VIO_ADDR),
++
++	REGDEF(33, 71, V3D_ERR_FDBGO),
++	REGDEF(33, 71, V3D_ERR_FDBGB),
++	REGDEF(33, 71, V3D_ERR_FDBGS),
++	REGDEF(33, 71, V3D_ERR_STAT),
+ };
+ 
+ static const struct v3d_reg_def v3d_csd_reg_defs[] = {
+-	REGDEF(V3D_CSD_STATUS),
+-	REGDEF(V3D_CSD_CURRENT_CFG0),
+-	REGDEF(V3D_CSD_CURRENT_CFG1),
+-	REGDEF(V3D_CSD_CURRENT_CFG2),
+-	REGDEF(V3D_CSD_CURRENT_CFG3),
+-	REGDEF(V3D_CSD_CURRENT_CFG4),
+-	REGDEF(V3D_CSD_CURRENT_CFG5),
+-	REGDEF(V3D_CSD_CURRENT_CFG6),
++	REGDEF(41, 71, V3D_CSD_STATUS),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG0),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG1),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG2),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG3),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG4),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG5),
++	REGDEF(41, 41, V3D_CSD_CURRENT_CFG6),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG0),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG1),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG2),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG3),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG4),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG5),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG6),
++	REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG7),
+ };
+ 
+ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
+@@ -86,38 +100,41 @@ static int v3d_v3d_debugfs_regs(struct s
+ 	int i, core;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) {
+-		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+-			   v3d_hub_reg_defs[i].name, v3d_hub_reg_defs[i].reg,
+-			   V3D_READ(v3d_hub_reg_defs[i].reg));
++		const struct v3d_reg_def *def = &v3d_hub_reg_defs[i];
++
++		if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
++			seq_printf(m, "%s (0x%04x): 0x%08x\n",
++				   def->name, def->reg, V3D_READ(def->reg));
++		}
+ 	}
+ 
+-	if (v3d->ver < 41) {
+-		for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) {
++	for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) {
++		const struct v3d_reg_def *def = &v3d_gca_reg_defs[i];
++
++		if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
+ 			seq_printf(m, "%s (0x%04x): 0x%08x\n",
+-				   v3d_gca_reg_defs[i].name,
+-				   v3d_gca_reg_defs[i].reg,
+-				   V3D_GCA_READ(v3d_gca_reg_defs[i].reg));
++				   def->name, def->reg, V3D_GCA_READ(def->reg));
+ 		}
+ 	}
+ 
+ 	for (core = 0; core < v3d->cores; core++) {
+ 		for (i = 0; i < ARRAY_SIZE(v3d_core_reg_defs); i++) {
+-			seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
+-				   core,
+-				   v3d_core_reg_defs[i].name,
+-				   v3d_core_reg_defs[i].reg,
+-				   V3D_CORE_READ(core,
+-						 v3d_core_reg_defs[i].reg));
++			const struct v3d_reg_def *def = &v3d_core_reg_defs[i];
++
++			if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
++				seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
++					   core, def->name, def->reg,
++					   V3D_CORE_READ(core, def->reg));
++			}
+ 		}
+ 
+-		if (v3d_has_csd(v3d)) {
+-			for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) {
++		for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) {
++			const struct v3d_reg_def *def = &v3d_csd_reg_defs[i];
++
++			if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
+ 				seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
+-					   core,
+-					   v3d_csd_reg_defs[i].name,
+-					   v3d_csd_reg_defs[i].reg,
+-					   V3D_CORE_READ(core,
+-							 v3d_csd_reg_defs[i].reg));
++					   core, def->name, def->reg,
++					   V3D_CORE_READ(core, def->reg));
+ 			}
+ 		}
+ 	}
+@@ -148,8 +165,10 @@ static int v3d_v3d_debugfs_ident(struct
+ 		   str_yes_no(ident2 & V3D_HUB_IDENT2_WITH_MMU));
+ 	seq_printf(m, "TFU:        %s\n",
+ 		   str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TFU));
+-	seq_printf(m, "TSY:        %s\n",
+-		   str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY));
++	if (v3d->ver <= 42) {
++		seq_printf(m, "TSY:        %s\n",
++			   str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY));
++	}
+ 	seq_printf(m, "MSO:        %s\n",
+ 		   str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_MSO));
+ 	seq_printf(m, "L3C:        %s (%dkb)\n",
+@@ -178,10 +197,14 @@ static int v3d_v3d_debugfs_ident(struct
+ 		seq_printf(m, "  QPUs:         %d\n", nslc * qups);
+ 		seq_printf(m, "  Semaphores:   %d\n",
+ 			   V3D_GET_FIELD(ident1, V3D_IDENT1_NSEM));
+-		seq_printf(m, "  BCG int:      %d\n",
+-			   (ident2 & V3D_IDENT2_BCG_INT) != 0);
+-		seq_printf(m, "  Override TMU: %d\n",
+-			   (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
++		if (v3d->ver <= 42) {
++			seq_printf(m, "  BCG int:      %d\n",
++				   (ident2 & V3D_IDENT2_BCG_INT) != 0);
++		}
++		if (v3d->ver < 40) {
++			seq_printf(m, "  Override TMU: %d\n",
++				   (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
++		}
+ 	}
+ 
+ 	return 0;
+@@ -289,8 +312,10 @@ static int v3d_measure_clock(struct seq_
+ 	int measure_ms = 1000;
+ 
+ 	if (v3d->ver >= 40) {
++		int cycle_count_reg = v3d->ver < 71 ?
++			V3D_PCTR_CYCLE_COUNT : V3D_V7_PCTR_CYCLE_COUNT;
+ 		V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3,
+-			       V3D_SET_FIELD(V3D_PCTR_CYCLE_COUNT,
++			       V3D_SET_FIELD(cycle_count_reg,
+ 					     V3D_PCTR_S0));
+ 		V3D_CORE_WRITE(core, V3D_V4_PCTR_0_CLR, 1);
+ 		V3D_CORE_WRITE(core, V3D_V4_PCTR_0_EN, 1);
+--- a/drivers/gpu/drm/v3d/v3d_gem.c
++++ b/drivers/gpu/drm/v3d/v3d_gem.c
+@@ -88,6 +88,9 @@ v3d_init_hw_state(struct v3d_dev *v3d)
+ static void
+ v3d_idle_axi(struct v3d_dev *v3d, int core)
+ {
++	if (v3d->ver >= 71)
++		return;
++
+ 	V3D_CORE_WRITE(core, V3D_GMP_CFG, V3D_GMP_CFG_STOP_REQ);
+ 
+ 	if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS) &
+--- a/drivers/gpu/drm/v3d/v3d_irq.c
++++ b/drivers/gpu/drm/v3d/v3d_irq.c
+@@ -20,16 +20,17 @@
+ #include "v3d_regs.h"
+ #include "v3d_trace.h"
+ 
+-#define V3D_CORE_IRQS ((u32)(V3D_INT_OUTOMEM |	\
+-			     V3D_INT_FLDONE |	\
+-			     V3D_INT_FRDONE |	\
+-			     V3D_INT_CSDDONE |	\
+-			     V3D_INT_GMPV))
+-
+-#define V3D_HUB_IRQS ((u32)(V3D_HUB_INT_MMU_WRV |	\
+-			    V3D_HUB_INT_MMU_PTI |	\
+-			    V3D_HUB_INT_MMU_CAP |	\
+-			    V3D_HUB_INT_TFUC))
++#define V3D_CORE_IRQS(ver) ((u32)(V3D_INT_OUTOMEM |	\
++				  V3D_INT_FLDONE |	\
++				  V3D_INT_FRDONE |	\
++				  (ver < 71 ? V3D_INT_CSDDONE : V3D_V7_INT_CSDDONE) |	\
++				  (ver < 71 ? V3D_INT_GMPV : 0)))
++
++#define V3D_HUB_IRQS(ver) ((u32)(V3D_HUB_INT_MMU_WRV |	\
++				 V3D_HUB_INT_MMU_PTI |	\
++				 V3D_HUB_INT_MMU_CAP |	\
++				 V3D_HUB_INT_TFUC |		\
++				 (ver >= 71 ? V3D_V7_HUB_INT_GMPV : 0)))
+ 
+ static irqreturn_t
+ v3d_hub_irq(int irq, void *arg);
+@@ -118,7 +119,8 @@ v3d_irq(int irq, void *arg)
+ 		status = IRQ_HANDLED;
+ 	}
+ 
+-	if (intsts & V3D_INT_CSDDONE) {
++	if ((v3d->ver < 71 && (intsts & V3D_INT_CSDDONE)) ||
++	    (v3d->ver >= 71 && (intsts & V3D_V7_INT_CSDDONE))) {
+ 		struct v3d_fence *fence =
+ 			to_v3d_fence(v3d->csd_job->base.irq_fence);
+ 		v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock();
+@@ -131,7 +133,7 @@ v3d_irq(int irq, void *arg)
+ 	/* We shouldn't be triggering these if we have GMP in
+ 	 * always-allowed mode.
+ 	 */
+-	if (intsts & V3D_INT_GMPV)
++	if (v3d->ver < 71 && (intsts & V3D_INT_GMPV))
+ 		dev_err(v3d->drm.dev, "GMP violation\n");
+ 
+ 	/* V3D 4.2 wires the hub and core IRQs together, so if we &
+@@ -205,6 +207,11 @@ v3d_hub_irq(int irq, void *arg)
+ 		status = IRQ_HANDLED;
+ 	}
+ 
++	if (v3d->ver >= 71 && intsts & V3D_V7_HUB_INT_GMPV) {
++		dev_err(v3d->drm.dev, "GMP Violation\n");
++		status = IRQ_HANDLED;
++	}
++
+ 	return status;
+ }
+ 
+@@ -219,8 +226,8 @@ v3d_irq_init(struct v3d_dev *v3d)
+ 	 * for us.
+ 	 */
+ 	for (core = 0; core < v3d->cores; core++)
+-		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
+-	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
++		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
++	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+ 
+ 	irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1);
+ 	if (irq1 == -EPROBE_DEFER)
+@@ -264,12 +271,12 @@ v3d_irq_enable(struct v3d_dev *v3d)
+ 
+ 	/* Enable our set of interrupts, masking out any others. */
+ 	for (core = 0; core < v3d->cores; core++) {
+-		V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS);
+-		V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS);
++		V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS(v3d->ver));
++		V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS(v3d->ver));
+ 	}
+ 
+-	V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS);
+-	V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS);
++	V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS(v3d->ver));
++	V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS(v3d->ver));
+ }
+ 
+ void
+@@ -284,8 +291,8 @@ v3d_irq_disable(struct v3d_dev *v3d)
+ 
+ 	/* Clear any pending interrupts we might have left. */
+ 	for (core = 0; core < v3d->cores; core++)
+-		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
+-	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
++		V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
++	V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+ 
+ 	cancel_work_sync(&v3d->overflow_mem_work);
+ }
+--- a/drivers/gpu/drm/v3d/v3d_regs.h
++++ b/drivers/gpu/drm/v3d/v3d_regs.h
+@@ -57,6 +57,7 @@
+ #define V3D_HUB_INT_MSK_STS                            0x0005c
+ #define V3D_HUB_INT_MSK_SET                            0x00060
+ #define V3D_HUB_INT_MSK_CLR                            0x00064
++# define V3D_V7_HUB_INT_GMPV                           BIT(6)
+ # define V3D_HUB_INT_MMU_WRV                           BIT(5)
+ # define V3D_HUB_INT_MMU_PTI                           BIT(4)
+ # define V3D_HUB_INT_MMU_CAP                           BIT(3)
+@@ -64,6 +65,7 @@
+ # define V3D_HUB_INT_TFUC                              BIT(1)
+ # define V3D_HUB_INT_TFUF                              BIT(0)
+ 
++/* GCA registers only exist in V3D < 41 */
+ #define V3D_GCA_CACHE_CTRL                             0x0000c
+ # define V3D_GCA_CACHE_CTRL_FLUSH                      BIT(0)
+ 
+@@ -87,6 +89,7 @@
+ # define V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT BIT(0)
+ 
+ #define V3D_TFU_CS                                     0x00400
++#define V3D_V7_TFU_CS                                  0x00700
+ /* Stops current job, empties input fifo. */
+ # define V3D_TFU_CS_TFURST                             BIT(31)
+ # define V3D_TFU_CS_CVTCT_MASK                         V3D_MASK(23, 16)
+@@ -96,6 +99,7 @@
+ # define V3D_TFU_CS_BUSY                               BIT(0)
+ 
+ #define V3D_TFU_SU                                     0x00404
++#define V3D_V7_TFU_SU                                  0x00704
+ /* Interrupt when FINTTHR input slots are free (0 = disabled) */
+ # define V3D_TFU_SU_FINTTHR_MASK                       V3D_MASK(13, 8)
+ # define V3D_TFU_SU_FINTTHR_SHIFT                      8
+@@ -107,38 +111,53 @@
+ # define V3D_TFU_SU_THROTTLE_SHIFT                     0
+ 
+ #define V3D_TFU_ICFG                                   0x00408
++#define V3D_V7_TFU_ICFG                                0x00708
+ /* Interrupt when the conversion is complete. */
+ # define V3D_TFU_ICFG_IOC                              BIT(0)
+ 
+ /* Input Image Address */
+ #define V3D_TFU_IIA                                    0x0040c
++#define V3D_V7_TFU_IIA                                 0x0070c
+ /* Input Chroma Address */
+ #define V3D_TFU_ICA                                    0x00410
++#define V3D_V7_TFU_ICA                                 0x00710
+ /* Input Image Stride */
+ #define V3D_TFU_IIS                                    0x00414
++#define V3D_V7_TFU_IIS                                 0x00714
+ /* Input Image U-Plane Address */
+ #define V3D_TFU_IUA                                    0x00418
++#define V3D_V7_TFU_IUA                                 0x00718
++/* Image output config (VD 7.x only) */
++#define V3D_V7_TFU_IOC                                 0x0071c
+ /* Output Image Address */
+ #define V3D_TFU_IOA                                    0x0041c
++#define V3D_V7_TFU_IOA                                 0x00720
+ /* Image Output Size */
+ #define V3D_TFU_IOS                                    0x00420
++#define V3D_V7_TFU_IOS                                 0x00724
+ /* TFU YUV Coefficient 0 */
+ #define V3D_TFU_COEF0                                  0x00424
+-/* Use these regs instead of the defaults. */
++#define V3D_V7_TFU_COEF0                               0x00728
++/* Use these regs instead of the defaults (V3D 4.x only) */
+ # define V3D_TFU_COEF0_USECOEF                         BIT(31)
+ /* TFU YUV Coefficient 1 */
+ #define V3D_TFU_COEF1                                  0x00428
++#define V3D_V7_TFU_COEF1                               0x0072c
+ /* TFU YUV Coefficient 2 */
+ #define V3D_TFU_COEF2                                  0x0042c
++#define V3D_V7_TFU_COEF2                               0x00730
+ /* TFU YUV Coefficient 3 */
+ #define V3D_TFU_COEF3                                  0x00430
++#define V3D_V7_TFU_COEF3                               0x00734
+ 
++/* V3D 4.x only */
+ #define V3D_TFU_CRC                                    0x00434
+ 
+ /* Per-MMU registers. */
+ 
+ #define V3D_MMUC_CONTROL                               0x01000
+ # define V3D_MMUC_CONTROL_CLEAR                        BIT(3)
++# define V3D_V7_MMUC_CONTROL_CLEAR                     BIT(11)
+ # define V3D_MMUC_CONTROL_FLUSHING                     BIT(2)
+ # define V3D_MMUC_CONTROL_FLUSH                        BIT(1)
+ # define V3D_MMUC_CONTROL_ENABLE                       BIT(0)
+@@ -246,7 +265,6 @@
+ 
+ #define V3D_CTL_L2TCACTL                               0x00030
+ # define V3D_L2TCACTL_TMUWCF                           BIT(8)
+-# define V3D_L2TCACTL_L2T_NO_WM                        BIT(4)
+ /* Invalidates cache lines. */
+ # define V3D_L2TCACTL_FLM_FLUSH                        0
+ /* Removes cachelines without writing dirty lines back. */
+@@ -268,7 +286,9 @@
+ # define V3D_INT_QPU_MASK                              V3D_MASK(27, 16)
+ # define V3D_INT_QPU_SHIFT                             16
+ # define V3D_INT_CSDDONE                               BIT(7)
++# define V3D_V7_INT_CSDDONE                            BIT(6)
+ # define V3D_INT_PCTR                                  BIT(6)
++# define V3D_V7_INT_PCTR                               BIT(5)
+ # define V3D_INT_GMPV                                  BIT(5)
+ # define V3D_INT_TRFB                                  BIT(4)
+ # define V3D_INT_SPILLUSE                              BIT(3)
+@@ -350,14 +370,19 @@
+ #define V3D_V4_PCTR_0_SRC_X(x)                         (V3D_V4_PCTR_0_SRC_0_3 + \
+ 							4 * (x))
+ # define V3D_PCTR_S0_MASK                              V3D_MASK(6, 0)
++# define V3D_V7_PCTR_S0_MASK                           V3D_MASK(7, 0)
+ # define V3D_PCTR_S0_SHIFT                             0
+ # define V3D_PCTR_S1_MASK                              V3D_MASK(14, 8)
++# define V3D_V7_PCTR_S1_MASK                           V3D_MASK(15, 8)
+ # define V3D_PCTR_S1_SHIFT                             8
+ # define V3D_PCTR_S2_MASK                              V3D_MASK(22, 16)
++# define V3D_V7_PCTR_S2_MASK                           V3D_MASK(23, 16)
+ # define V3D_PCTR_S2_SHIFT                             16
+ # define V3D_PCTR_S3_MASK                              V3D_MASK(30, 24)
++# define V3D_V7_PCTR_S3_MASK                           V3D_MASK(31, 24)
+ # define V3D_PCTR_S3_SHIFT                             24
+ # define V3D_PCTR_CYCLE_COUNT                          32
++# define V3D_V7_PCTR_CYCLE_COUNT                       0
+ 
+ /* Output values of the counters. */
+ #define V3D_PCTR_0_PCTR0                               0x00680
+@@ -365,6 +390,7 @@
+ #define V3D_PCTR_0_PCTRX(x)                            (V3D_PCTR_0_PCTR0 + \
+ 							4 * (x))
+ #define V3D_GMP_STATUS                                 0x00800
++#define V3D_V7_GMP_STATUS                              0x00600
+ # define V3D_GMP_STATUS_GMPRST                         BIT(31)
+ # define V3D_GMP_STATUS_WR_COUNT_MASK                  V3D_MASK(30, 24)
+ # define V3D_GMP_STATUS_WR_COUNT_SHIFT                 24
+@@ -378,12 +404,14 @@
+ # define V3D_GMP_STATUS_VIO                            BIT(0)
+ 
+ #define V3D_GMP_CFG                                    0x00804
++#define V3D_V7_GMP_CFG                                 0x00604
+ # define V3D_GMP_CFG_LBURSTEN                          BIT(3)
+ # define V3D_GMP_CFG_PGCRSEN                           BIT()
+ # define V3D_GMP_CFG_STOP_REQ                          BIT(1)
+ # define V3D_GMP_CFG_PROT_ENABLE                       BIT(0)
+ 
+ #define V3D_GMP_VIO_ADDR                               0x00808
++#define V3D_V7_GMP_VIO_ADDR                            0x00608
+ #define V3D_GMP_VIO_TYPE                               0x0080c
+ #define V3D_GMP_TABLE_ADDR                             0x00810
+ #define V3D_GMP_CLEAR_LOAD                             0x00814
+@@ -399,24 +427,28 @@
+ # define V3D_CSD_STATUS_HAVE_QUEUED_DISPATCH           BIT(0)
+ 
+ #define V3D_CSD_QUEUED_CFG0                            0x00904
++#define V3D_V7_CSD_QUEUED_CFG0                         0x00930
+ # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_MASK            V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_SHIFT           16
+ # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_MASK          V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_SHIFT         0
+ 
+ #define V3D_CSD_QUEUED_CFG1                            0x00908
++#define V3D_V7_CSD_QUEUED_CFG1                         0x00934
+ # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_MASK            V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_SHIFT           16
+ # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_MASK          V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_SHIFT         0
+ 
+ #define V3D_CSD_QUEUED_CFG2                            0x0090c
++#define V3D_V7_CSD_QUEUED_CFG2                         0x00938
+ # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_MASK            V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_SHIFT           16
+ # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_MASK          V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_SHIFT         0
+ 
+ #define V3D_CSD_QUEUED_CFG3                            0x00910
++#define V3D_V7_CSD_QUEUED_CFG3                         0x0093c
+ # define V3D_CSD_QUEUED_CFG3_OVERLAP_WITH_PREV         BIT(26)
+ # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_MASK            V3D_MASK(25, 20)
+ # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_SHIFT           20
+@@ -429,22 +461,36 @@
+ 
+ /* Number of batches, minus 1 */
+ #define V3D_CSD_QUEUED_CFG4                            0x00914
++#define V3D_V7_CSD_QUEUED_CFG4                         0x00940
+ 
+ /* Shader address, pnan, singleseg, threading, like a shader record. */
+ #define V3D_CSD_QUEUED_CFG5                            0x00918
++#define V3D_V7_CSD_QUEUED_CFG5                         0x00944
+ 
+ /* Uniforms address (4 byte aligned) */
+ #define V3D_CSD_QUEUED_CFG6                            0x0091c
++#define V3D_V7_CSD_QUEUED_CFG6                         0x00948
++
++#define V3D_V7_CSD_QUEUED_CFG7                         0x0094c
+ 
+ #define V3D_CSD_CURRENT_CFG0                          0x00920
++#define V3D_V7_CSD_CURRENT_CFG0                       0x00958
+ #define V3D_CSD_CURRENT_CFG1                          0x00924
++#define V3D_V7_CSD_CURRENT_CFG1                       0x0095c
+ #define V3D_CSD_CURRENT_CFG2                          0x00928
++#define V3D_V7_CSD_CURRENT_CFG2                       0x00960
+ #define V3D_CSD_CURRENT_CFG3                          0x0092c
++#define V3D_V7_CSD_CURRENT_CFG3                       0x00964
+ #define V3D_CSD_CURRENT_CFG4                          0x00930
++#define V3D_V7_CSD_CURRENT_CFG4                       0x00968
+ #define V3D_CSD_CURRENT_CFG5                          0x00934
++#define V3D_V7_CSD_CURRENT_CFG5                       0x0096c
+ #define V3D_CSD_CURRENT_CFG6                          0x00938
++#define V3D_V7_CSD_CURRENT_CFG6                       0x00970
++#define V3D_V7_CSD_CURRENT_CFG7                       0x00974
+ 
+ #define V3D_CSD_CURRENT_ID0                            0x0093c
++#define V3D_V7_CSD_CURRENT_ID0                         0x00978
+ # define V3D_CSD_CURRENT_ID0_WG_X_MASK                 V3D_MASK(31, 16)
+ # define V3D_CSD_CURRENT_ID0_WG_X_SHIFT                16
+ # define V3D_CSD_CURRENT_ID0_WG_IN_SG_MASK             V3D_MASK(11, 8)
+@@ -453,6 +499,7 @@
+ # define V3D_CSD_CURRENT_ID0_L_IDX_SHIFT               0
+ 
+ #define V3D_CSD_CURRENT_ID1                            0x00940
++#define V3D_V7_CSD_CURRENT_ID1                         0x0097c
+ # define V3D_CSD_CURRENT_ID0_WG_Z_MASK                 V3D_MASK(31, 16)
+ # define V3D_CSD_CURRENT_ID0_WG_Z_SHIFT                16
+ # define V3D_CSD_CURRENT_ID0_WG_Y_MASK                 V3D_MASK(15, 0)
+--- a/drivers/gpu/drm/v3d/v3d_sched.c
++++ b/drivers/gpu/drm/v3d/v3d_sched.c
+@@ -282,6 +282,8 @@ static struct dma_fence *v3d_render_job_
+ 	return fence;
+ }
+ 
++#define V3D_TFU_REG(name) ((v3d->ver < 71) ? V3D_TFU_ ## name : V3D_V7_TFU_ ## name)
++
+ static struct dma_fence *
+ v3d_tfu_job_run(struct drm_sched_job *sched_job)
+ {
+@@ -302,20 +304,22 @@ v3d_tfu_job_run(struct drm_sched_job *sc
+ 	trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno);
+ 
+ 	v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_TFU], sched_job);
+-	V3D_WRITE(V3D_TFU_IIA, job->args.iia);
+-	V3D_WRITE(V3D_TFU_IIS, job->args.iis);
+-	V3D_WRITE(V3D_TFU_ICA, job->args.ica);
+-	V3D_WRITE(V3D_TFU_IUA, job->args.iua);
+-	V3D_WRITE(V3D_TFU_IOA, job->args.ioa);
+-	V3D_WRITE(V3D_TFU_IOS, job->args.ios);
+-	V3D_WRITE(V3D_TFU_COEF0, job->args.coef[0]);
+-	if (job->args.coef[0] & V3D_TFU_COEF0_USECOEF) {
+-		V3D_WRITE(V3D_TFU_COEF1, job->args.coef[1]);
+-		V3D_WRITE(V3D_TFU_COEF2, job->args.coef[2]);
+-		V3D_WRITE(V3D_TFU_COEF3, job->args.coef[3]);
++	V3D_WRITE(V3D_TFU_REG(IIA), job->args.iia);
++	V3D_WRITE(V3D_TFU_REG(IIS), job->args.iis);
++	V3D_WRITE(V3D_TFU_REG(ICA), job->args.ica);
++	V3D_WRITE(V3D_TFU_REG(IUA), job->args.iua);
++	V3D_WRITE(V3D_TFU_REG(IOA), job->args.ioa);
++	if (v3d->ver >= 71)
++		V3D_WRITE(V3D_V7_TFU_IOC, job->args.v71.ioc);
++	V3D_WRITE(V3D_TFU_REG(IOS), job->args.ios);
++	V3D_WRITE(V3D_TFU_REG(COEF0), job->args.coef[0]);
++	if (v3d->ver >= 71 || (job->args.coef[0] & V3D_TFU_COEF0_USECOEF)) {
++		V3D_WRITE(V3D_TFU_REG(COEF1), job->args.coef[1]);
++		V3D_WRITE(V3D_TFU_REG(COEF2), job->args.coef[2]);
++		V3D_WRITE(V3D_TFU_REG(COEF3), job->args.coef[3]);
+ 	}
+ 	/* ICFG kicks off the job. */
+-	V3D_WRITE(V3D_TFU_ICFG, job->args.icfg | V3D_TFU_ICFG_IOC);
++	V3D_WRITE(V3D_TFU_REG(ICFG), job->args.icfg | V3D_TFU_ICFG_IOC);
+ 
+ 	return fence;
+ }
+@@ -327,7 +331,7 @@ v3d_csd_job_run(struct drm_sched_job *sc
+ 	struct v3d_dev *v3d = job->base.v3d;
+ 	struct drm_device *dev = &v3d->drm;
+ 	struct dma_fence *fence;
+-	int i;
++	int i, csd_cfg0_reg, csd_cfg_reg_count;
+ 
+ 	v3d->csd_job = job;
+ 
+@@ -346,10 +350,12 @@ v3d_csd_job_run(struct drm_sched_job *sc
+ 	v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_CSD], sched_job);
+ 	v3d_switch_perfmon(v3d, &job->base);
+ 
+-	for (i = 1; i <= 6; i++)
+-		V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0 + 4 * i, job->args.cfg[i]);
++	csd_cfg0_reg = v3d->ver < 71 ? V3D_CSD_QUEUED_CFG0 : V3D_V7_CSD_QUEUED_CFG0;
++	csd_cfg_reg_count = v3d->ver < 71 ? 6 : 7;
++	for (i = 1; i <= csd_cfg_reg_count; i++)
++		V3D_CORE_WRITE(0, csd_cfg0_reg + 4 * i, job->args.cfg[i]);
+ 	/* CFG0 write kicks off the job. */
+-	V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0, job->args.cfg[0]);
++	V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]);
+ 
+ 	return fence;
+ }
+@@ -452,7 +458,8 @@ v3d_csd_job_timedout(struct drm_sched_jo
+ {
+ 	struct v3d_csd_job *job = to_csd_job(sched_job);
+ 	struct v3d_dev *v3d = job->base.v3d;
+-	u32 batches = V3D_CORE_READ(0, V3D_CSD_CURRENT_CFG4);
++	u32 batches = V3D_CORE_READ(0, (v3d->ver < 71 ? V3D_CSD_CURRENT_CFG4 :
++							V3D_V7_CSD_CURRENT_CFG4));
+ 
+ 	/* If we've made progress, skip reset and let the timer get
+ 	 * rearmed.

+ 24 - 0
target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch

@@ -0,0 +1,24 @@
+From 22fb30936524ae96151789741885edbc45efb53d Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <[email protected]>
+Date: Thu, 2 Mar 2023 11:52:08 +0100
+Subject: [PATCH] drm/v3d: update UAPI to match user-space for V3D 7.x
+
+V3D t.x takes a new parameter to configure TFU jobs that needs
+to be provided by user space.
+---
+ include/uapi/drm/v3d_drm.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/include/uapi/drm/v3d_drm.h
++++ b/include/uapi/drm/v3d_drm.h
+@@ -319,6 +319,10 @@ struct drm_v3d_submit_tfu {
+ 
+ 	/* Pointer to an array of ioctl extensions*/
+ 	__u64 extensions;
++
++	struct {
++		__u32 ioc;
++	} v71;
+ };
+ 
+ /* Submits a compute shader for dispatch.  This job will block on any

+ 19 - 0
target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch

@@ -0,0 +1,19 @@
+From 18bc419d38eda06ded78c7b702c0e21e5af8f24c Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <[email protected]>
+Date: Thu, 2 Mar 2023 11:54:45 +0100
+Subject: [PATCH] drm/v3d: add brcm,2712-v3d as a compatible V3D device
+
+---
+ drivers/gpu/drm/v3d/v3d_drv.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/v3d/v3d_drv.c
++++ b/drivers/gpu/drm/v3d/v3d_drv.c
+@@ -193,6 +193,7 @@ static const struct drm_driver v3d_drm_d
+ };
+ 
+ static const struct of_device_id v3d_of_match[] = {
++	{ .compatible = "brcm,2712-v3d" },
+ 	{ .compatible = "brcm,2711-v3d" },
+ 	{ .compatible = "brcm,7268-v3d" },
+ 	{ .compatible = "brcm,7278-v3d" },

+ 64 - 0
target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch

@@ -0,0 +1,64 @@
+From 12c7ea43b930976f35ce75d11fd3f55438868e13 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 4 Aug 2023 11:26:10 +0100
+Subject: [PATCH] drm/v3d: Improve MMU support for larger pages
+
+The built-in MMU driver went most of the way towards supporting larger
+kernel pages, but dropped the ball when it comes to calculating indexes
+into the page table. Fix it.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/gpu/drm/v3d/v3d_mmu.c | 15 ++++++++-------
+ 1 file changed, 8 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_mmu.c
++++ b/drivers/gpu/drm/v3d/v3d_mmu.c
+@@ -22,6 +22,7 @@
+ #include "v3d_regs.h"
+ 
+ #define V3D_MMU_PAGE_SHIFT 12
++#define V3D_PAGE_FACTOR (PAGE_SIZE >> V3D_MMU_PAGE_SHIFT)
+ 
+ /* Note: All PTEs for the 1MB superpage must be filled with the
+  * superpage bit set.
+@@ -88,7 +89,7 @@ void v3d_mmu_insert_ptes(struct v3d_bo *
+ {
+ 	struct drm_gem_shmem_object *shmem_obj = &bo->base;
+ 	struct v3d_dev *v3d = to_v3d_dev(shmem_obj->base.dev);
+-	u32 page = bo->node.start;
++	u32 page = bo->node.start * V3D_PAGE_FACTOR;
+ 	u32 page_prot = V3D_PTE_WRITEABLE | V3D_PTE_VALID;
+ 	struct sg_dma_page_iter dma_iter;
+ 
+@@ -98,13 +99,13 @@ void v3d_mmu_insert_ptes(struct v3d_bo *
+ 		u32 pte = page_prot | page_address;
+ 		u32 i;
+ 
+-		BUG_ON(page_address + (PAGE_SIZE >> V3D_MMU_PAGE_SHIFT) >=
++		BUG_ON(page_address + V3D_PAGE_FACTOR >=
+ 		       BIT(24));
+-		for (i = 0; i < PAGE_SIZE >> V3D_MMU_PAGE_SHIFT; i++)
++		for (i = 0; i < V3D_PAGE_FACTOR; i++)
+ 			v3d->pt[page++] = pte + i;
+ 	}
+ 
+-	WARN_ON_ONCE(page - bo->node.start !=
++	WARN_ON_ONCE(page - (bo->node.start * V3D_PAGE_FACTOR) !=
+ 		     shmem_obj->base.size >> V3D_MMU_PAGE_SHIFT);
+ 
+ 	if (v3d_mmu_flush_all(v3d))
+@@ -115,10 +116,10 @@ void v3d_mmu_remove_ptes(struct v3d_bo *
+ {
+ 	struct v3d_dev *v3d = to_v3d_dev(bo->base.base.dev);
+ 	u32 npages = bo->base.base.size >> V3D_MMU_PAGE_SHIFT;
+-	u32 page;
++	u32 page = bo->node.start * V3D_PAGE_FACTOR;
+ 
+-	for (page = bo->node.start; page < bo->node.start + npages; page++)
+-		v3d->pt[page] = 0;
++	while (npages--)
++		v3d->pt[page++] = 0;
+ 
+ 	if (v3d_mmu_flush_all(v3d))
+ 		dev_err(v3d->drm.dev, "MMU flush timeout\n");

+ 19 - 0
target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch

@@ -0,0 +1,19 @@
+From 5970fa51663511d7f773db7109ff6fa2504f186a Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <[email protected]>
+Date: Thu, 2 Mar 2023 11:56:52 +0100
+Subject: [PATCH] dt-bindings: gpu: v3d: Add BCM2712 to compatibility list
+
+---
+ Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml
++++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml
+@@ -16,6 +16,7 @@ properties:
+ 
+   compatible:
+     enum:
++      - brcm,2712-v3d
+       - brcm,2711-v3d
+       - brcm,7268-v3d
+       - brcm,7278-v3d

+ 328 - 0
target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch

@@ -0,0 +1,328 @@
+From fdf9cab5eaa849e90b12e17718bc47130a91433c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Tue, 25 Apr 2023 15:52:13 +0100
+Subject: [PATCH] drivers: char: add generic gpiomem driver
+
+Based on bcm2835-gpiomem.
+
+We allow export of the "GPIO registers" to userspace via a chardev as
+this allows for finer access control (e.g. users must be group gpio, root
+not required).
+
+This driver allows access to either rp1-gpiomem or gpiomem, depending on
+which nodes are populated in devicetree.
+
+RP1 has a different look-and-feel to BCM283x SoCs as it has split ranges
+for IO controls and the parallel registered OE/IN/OUT access. To handle
+this, the driver concatenates the ranges for an IO bank and the
+corresponding RIO instance into a contiguous buffer.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/char/Kconfig               |   8 +
+ drivers/char/Makefile              |   1 +
+ drivers/char/raspberrypi-gpiomem.c | 276 +++++++++++++++++++++++++++++
+ 3 files changed, 285 insertions(+)
+ create mode 100644 drivers/char/raspberrypi-gpiomem.c
+
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -461,4 +461,12 @@ config RANDOM_TRUST_BOOTLOADER
+ 	  believe its RNG facilities may be faulty. This may also be configured
+ 	  at boot time with "random.trust_bootloader=on/off".
+ 
++config RASPBERRYPI_GPIOMEM
++        tristate "Rootless GPIO access via mmap() on Raspberry Pi boards"
++        default n
++        help
++                Provides users with root-free access to the GPIO registers
++                on the board. Calling mmap(/dev/gpiomem) will map the GPIO
++                register page to the user's pointer.
++
+ endmenu
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -46,3 +46,4 @@ obj-$(CONFIG_XILLYBUS_CLASS)	+= xillybus
+ obj-$(CONFIG_POWERNV_OP_PANEL)	+= powernv-op-panel.o
+ obj-$(CONFIG_ADI)		+= adi.o
+ obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/
++obj-$(CONFIG_RASPBERRYPI_GPIOMEM) += raspberrypi-gpiomem.o
+--- /dev/null
++++ b/drivers/char/raspberrypi-gpiomem.c
+@@ -0,0 +1,276 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/**
++ * raspberrypi-gpiomem.c
++ *
++ * Provides MMIO access to discontiguous section of Device memory as a linear
++ * user mapping. Successor to bcm2835-gpiomem.c.
++ *
++ * Copyright (c) 2023, Raspberry Pi Ltd.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/cdev.h>
++#include <linux/pagemap.h>
++#include <linux/io.h>
++
++#define DRIVER_NAME "rpi-gpiomem"
++#define DEVICE_MINOR 0
++
++/*
++ * Sensible max for a hypothetical "gpio" controller that splits pads,
++ * IO controls, GPIO in/out/enable, and function selection into different
++ * ranges. Most use only one or two.
++ */
++#define MAX_RANGES 4
++
++struct io_windows {
++	unsigned long phys_base;
++	unsigned long len;
++};
++
++struct rpi_gpiomem_priv {
++	dev_t devid;
++	struct class *class;
++	struct cdev rpi_gpiomem_cdev;
++	struct device *dev;
++	const char *name;
++	unsigned int nr_wins;
++	struct io_windows iowins[4];
++};
++
++static int rpi_gpiomem_open(struct inode *inode, struct file *file)
++{
++	int dev = iminor(inode);
++	int ret = 0;
++	struct rpi_gpiomem_priv *priv;
++
++	if (dev != DEVICE_MINOR)
++		ret = -ENXIO;
++
++	priv = container_of(inode->i_cdev, struct rpi_gpiomem_priv,
++				rpi_gpiomem_cdev);
++	if (!priv)
++		return -EINVAL;
++	file->private_data = priv;
++	return ret;
++}
++
++static int rpi_gpiomem_release(struct inode *inode, struct file *file)
++{
++	int dev = iminor(inode);
++	int ret = 0;
++
++	if (dev != DEVICE_MINOR)
++		ret = -ENXIO;
++
++	return ret;
++}
++
++static const struct vm_operations_struct rpi_gpiomem_vm_ops = {
++#ifdef CONFIG_HAVE_IOREMAP_PROT
++	.access = generic_access_phys
++#endif
++};
++
++static int rpi_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++	int i;
++	struct rpi_gpiomem_priv *priv;
++	unsigned long base;
++	unsigned long len = 0;
++	unsigned long offset;
++
++	priv = file->private_data;
++	/*
++	 * Userspace must provide a virtual address space at least
++	 * the size of the concatenated ranges.
++	 */
++	for (i = 0; i < priv->nr_wins; i++)
++		len += priv->iowins[i].len;
++	if (len > vma->vm_end - vma->vm_start + 1)
++		return -EINVAL;
++
++	vma->vm_ops = &rpi_gpiomem_vm_ops;
++	offset = vma->vm_start;
++	for (i = 0; i < priv->nr_wins; i++) {
++		base = priv->iowins[i].phys_base >> PAGE_SHIFT;
++		len = priv->iowins[i].len;
++		vma->vm_page_prot = phys_mem_access_prot(file, base, len,
++							 vma->vm_page_prot);
++		if (remap_pfn_range(vma, offset,
++			    base, len,
++			    vma->vm_page_prot))
++			break;
++		offset += len;
++	}
++
++	if (i < priv->nr_wins)
++		return -EAGAIN;
++
++	return 0;
++}
++
++static const struct file_operations rpi_gpiomem_fops = {
++	.owner = THIS_MODULE,
++	.open = rpi_gpiomem_open,
++	.release = rpi_gpiomem_release,
++	.mmap = rpi_gpiomem_mmap,
++};
++
++static const struct of_device_id rpi_gpiomem_of_match[];
++
++static int rpi_gpiomem_probe(struct platform_device *pdev)
++{
++	int err, i;
++	const struct of_device_id *id;
++	struct device *dev = &pdev->dev;
++	struct device_node *node = dev->of_node;
++	struct resource *ioresource;
++	struct rpi_gpiomem_priv *priv;
++
++	/* Allocate buffers and instance data */
++
++	priv = kzalloc(sizeof(struct rpi_gpiomem_priv), GFP_KERNEL);
++
++	if (!priv) {
++		err = -ENOMEM;
++		goto failed_inst_alloc;
++	}
++	platform_set_drvdata(pdev, priv);
++
++	priv->dev = dev;
++	id = of_match_device(rpi_gpiomem_of_match, dev);
++	if (!id)
++		return -EINVAL;
++
++	/*
++	 * Device node naming - for legacy (bcm2835) DT bindings, the driver
++	 * created the node based on a hardcoded name - for new bindings,
++	 * take the node name from DT.
++	 */
++	if (id == &rpi_gpiomem_of_match[0]) {
++		priv->name = "gpiomem";
++	} else {
++		err = of_property_read_string(node, "chardev-name", &priv->name);
++		if (err)
++			return -EINVAL;
++	}
++
++	/*
++	 * Go find the register ranges associated with this instance
++	 */
++	for (i = 0; i < MAX_RANGES; i++) {
++		ioresource = platform_get_resource(pdev, IORESOURCE_MEM, i);
++		if (!ioresource && i == 0) {
++			dev_err(priv->dev, "failed to get IO resource - no ranges available\n");
++			err = -ENOENT;
++			goto failed_get_resource;
++		}
++		if (!ioresource)
++			break;
++
++		priv->iowins[i].phys_base = ioresource->start;
++		priv->iowins[i].len = (ioresource->end + 1) - ioresource->start;
++		dev_info(&pdev->dev, "window base 0x%08lx size 0x%08lx\n",
++			 priv->iowins[i].phys_base, priv->iowins[i].len);
++		priv->nr_wins++;
++	}
++
++	/* Create character device entries */
++
++	err = alloc_chrdev_region(&priv->devid,
++				  DEVICE_MINOR, 1, priv->name);
++	if (err != 0) {
++		dev_err(priv->dev, "unable to allocate device number");
++		goto failed_alloc_chrdev;
++	}
++	cdev_init(&priv->rpi_gpiomem_cdev, &rpi_gpiomem_fops);
++	priv->rpi_gpiomem_cdev.owner = THIS_MODULE;
++	err = cdev_add(&priv->rpi_gpiomem_cdev, priv->devid, 1);
++	if (err != 0) {
++		dev_err(priv->dev, "unable to register device");
++		goto failed_cdev_add;
++	}
++
++	/* Create sysfs entries */
++
++	priv->class = class_create(THIS_MODULE, priv->name);
++	if (IS_ERR(priv->class)) {
++		err = PTR_ERR(priv->class);
++		goto failed_class_create;
++	}
++
++	dev = device_create(priv->class, NULL, priv->devid, NULL, priv->name);
++	if (IS_ERR(dev)) {
++		err = PTR_ERR(dev);
++		goto failed_device_create;
++	}
++
++	dev_info(priv->dev, "initialised %u regions as /dev/%s\n",
++		 priv->nr_wins, priv->name);
++
++	return 0;
++
++failed_device_create:
++	class_destroy(priv->class);
++failed_class_create:
++	cdev_del(&priv->rpi_gpiomem_cdev);
++failed_cdev_add:
++	unregister_chrdev_region(priv->devid, 1);
++failed_alloc_chrdev:
++failed_get_resource:
++	kfree(priv);
++failed_inst_alloc:
++	dev_err(&pdev->dev, "could not load rpi_gpiomem");
++	return err;
++}
++
++static int rpi_gpiomem_remove(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct rpi_gpiomem_priv *priv = platform_get_drvdata(pdev);
++
++	device_destroy(priv->class, priv->devid);
++	class_destroy(priv->class);
++	cdev_del(&priv->rpi_gpiomem_cdev);
++	unregister_chrdev_region(priv->devid, 1);
++	kfree(priv);
++
++	dev_info(dev, "%s driver removed - OK", priv->name);
++	return 0;
++}
++
++static const struct of_device_id rpi_gpiomem_of_match[] = {
++	{
++		.compatible = "brcm,bcm2835-gpiomem",
++	},
++	{
++		.compatible = "raspberrypi,gpiomem",
++	},
++	{ /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rpi_gpiomem_of_match);
++
++static struct platform_driver rpi_gpiomem_driver = {
++	.probe = rpi_gpiomem_probe,
++	.remove = rpi_gpiomem_remove,
++	.driver = {
++		   .name = DRIVER_NAME,
++		   .owner = THIS_MODULE,
++		   .of_match_table = rpi_gpiomem_of_match,
++		   },
++};
++
++module_platform_driver(rpi_gpiomem_driver);
++
++MODULE_ALIAS("platform:rpi-gpiomem");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("Driver for accessing GPIOs from userspace");
++MODULE_AUTHOR("Jonathan Bell <[email protected]>");

部分文件因文件數量過多而無法顯示