bcm27xx: update 6.1 patches to latest version
authorMarty Jones <mj8263788@gmail.com>
Thu, 18 Jan 2024 21:23:52 +0000 (16:23 -0500)
committerÁlvaro Fernández Rojas <noltari@gmail.com>
Thu, 25 Jan 2024 16:46:45 +0000 (17:46 +0100)
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 <mj8263788@gmail.com>
[Remove applied and reverted patches, squash patches and config commits]
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
341 files changed:
target/linux/bcm27xx/bcm2708/config-6.1
target/linux/bcm27xx/bcm2709/config-6.1
target/linux/bcm27xx/bcm2710/config-6.1
target/linux/bcm27xx/bcm2711/config-6.1
target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch [deleted file]
target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch [deleted file]
target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0885-drm-Add-RP1-DSI-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-6.1/960-hwrng-iproc-set-quality-to-1000.patch
target/linux/generic/config-6.1

index c5d604e6bc259616090c986fef25ea846ebdf8d2..cef17940a35ad1ab1bcbe61843df004a9c631dc2 100644 (file)
@@ -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
index ec2e7f3fa610b07997ffa27724e62d43df83e0bf..15bbaabd74c07816a7c8382d2f8ee321fa010206 100644 (file)
@@ -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
index b0abd49c7f12b4c64cfe51561b9461b60ca4ba1e..50b3f87d7dff6819e8bb270985be0c48aae848b5 100644 (file)
@@ -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
index 1912b653e50885c678e84528ce4b11a39375689d..04413050672662eee7864a53186557806e2ca76f 100644 (file)
@@ -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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch b/target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch
deleted file mode 100644 (file)
index 9a36dd1..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-From b642f64d629df5515f3a01fc5b2e17c3fa7b404c Mon Sep 17 00:00:00 2001
-From: Stefan Wahren <wahrenst@gmx.net>
-Date: Sat, 4 May 2019 17:06:15 +0200
-Subject: [PATCH] hwrng: iproc-rng200: Add BCM2838 support
-
-The HWRNG on the BCM2838 is compatible to iproc-rng200, so add the
-support to this driver instead of bcm2835-rng.
-
-Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
-
-hwrng: iproc-rng200: Correct SoC name
-
-The Pi 4 SoC is called BCM2711, not BCM2838.
-
-Fixes: "hwrng: iproc-rng200: Add BCM2838 support"
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/char/hw_random/Kconfig        |  2 +-
- drivers/char/hw_random/iproc-rng200.c | 78 +++++++++++++++++++++++++--
- 2 files changed, 76 insertions(+), 4 deletions(-)
-
---- a/drivers/char/hw_random/Kconfig
-+++ b/drivers/char/hw_random/Kconfig
-@@ -104,7 +104,7 @@ config HW_RANDOM_IPROC_RNG200
-       default HW_RANDOM
-       help
-         This driver provides kernel-side support for the RNG200
--        hardware found on the Broadcom iProc and STB SoCs.
-+        hardware found on the Broadcom iProc, BCM2711 and STB SoCs.
-         To compile this driver as a module, choose M here: the
-         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 @@
- #define RNG_CTRL_OFFSET                                       0x00
- #define RNG_CTRL_RNG_RBGEN_MASK                               0x00001FFF
- #define RNG_CTRL_RNG_RBGEN_ENABLE                     0x00000001
-+#define RNG_CTRL_RNG_DIV_CTRL_SHIFT                   13
- #define RNG_SOFT_RESET_OFFSET                         0x04
- #define RNG_SOFT_RESET                                        0x00000001
-@@ -28,16 +29,23 @@
- #define RBG_SOFT_RESET_OFFSET                         0x08
- #define RBG_SOFT_RESET                                        0x00000001
-+#define RNG_TOTAL_BIT_COUNT_OFFSET                    0x0C
-+
-+#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET          0x10
-+
- #define RNG_INT_STATUS_OFFSET                         0x18
- #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK   0x80000000
- #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK       0x00020000
- #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK             0x00000020
- #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK      0x00000001
-+#define RNG_INT_ENABLE_OFFSET                         0x1C
-+
- #define RNG_FIFO_DATA_OFFSET                          0x20
- #define RNG_FIFO_COUNT_OFFSET                         0x24
- #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK            0x000000FF
-+#define RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT               8
- struct iproc_rng200_dev {
-       struct hwrng rng;
-@@ -158,6 +166,64 @@ static int iproc_rng200_init(struct hwrn
-       return 0;
- }
-+static int bcm2711_rng200_read(struct hwrng *rng, void *buf, size_t max,
-+                             bool wait)
-+{
-+      struct iproc_rng200_dev *priv = to_rng_priv(rng);
-+      u32 max_words = max / sizeof(u32);
-+      u32 num_words, count, val;
-+
-+      /* ensure warm up period has elapsed */
-+      while (1) {
-+              val = ioread32(priv->base + RNG_TOTAL_BIT_COUNT_OFFSET);
-+              if (val > 16)
-+                      break;
-+              cpu_relax();
-+      }
-+
-+      /* ensure fifo is not empty */
-+      while (1) {
-+              num_words = ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
-+                          RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK;
-+              if (num_words)
-+                      break;
-+              if (!wait)
-+                      return 0;
-+              cpu_relax();
-+      }
-+
-+      if (num_words > max_words)
-+              num_words = max_words;
-+
-+      for (count = 0; count < num_words; count++) {
-+              ((u32 *)buf)[count] = ioread32(priv->base +
-+                                             RNG_FIFO_DATA_OFFSET);
-+      }
-+
-+      return num_words * sizeof(u32);
-+}
-+
-+static int bcm2711_rng200_init(struct hwrng *rng)
-+{
-+      struct iproc_rng200_dev *priv = to_rng_priv(rng);
-+      uint32_t val;
-+
-+      if (ioread32(priv->base + RNG_CTRL_OFFSET) & RNG_CTRL_RNG_RBGEN_MASK)
-+              return 0;
-+
-+      /* initial numbers generated are "less random" so will be discarded */
-+      val = 0x40000;
-+      iowrite32(val, priv->base + RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET);
-+      /* min fifo count to generate full interrupt */
-+      val = 2 << RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT;
-+      iowrite32(val, priv->base + RNG_FIFO_COUNT_OFFSET);
-+      /* enable the rng - 1Mhz sample rate */
-+      val = (0x3 << RNG_CTRL_RNG_DIV_CTRL_SHIFT) | RNG_CTRL_RNG_RBGEN_MASK;
-+      iowrite32(val, priv->base + RNG_CTRL_OFFSET);
-+
-+      return 0;
-+}
-+
- 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
-       dev_set_drvdata(dev, priv);
--      priv->rng.name = "iproc-rng200";
--      priv->rng.read = iproc_rng200_read;
--      priv->rng.init = iproc_rng200_init;
-+      priv->rng.name = pdev->name;
-       priv->rng.cleanup = iproc_rng200_cleanup;
-+      if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-rng200")) {
-+              priv->rng.init = bcm2711_rng200_init;
-+              priv->rng.read = bcm2711_rng200_read;
-+      } else {
-+              priv->rng.init = iproc_rng200_init;
-+              priv->rng.read = iproc_rng200_read;
-+      }
-+
-       /* Register driver */
-       ret = devm_hwrng_register(dev, &priv->rng);
-       if (ret) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch b/target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch
deleted file mode 100644 (file)
index d4c5a01..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-From dffb648dffeab7246040a30b7d1669387d1e767e Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-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 <phil@raspberrypi.com>
----
- 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 ||
diff --git a/target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch b/target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch
new file mode 100644 (file)
index 0000000..276e51d
--- /dev/null
@@ -0,0 +1,306 @@
+From 3ad8e28669e0058e3cec482a47215e50e33f2574 Mon Sep 17 00:00:00 2001
+From: Vinay Varma <varmavinaym@gmail.com>
+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 <varmavinaym@gmail.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch b/target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch
new file mode 100644 (file)
index 0000000..c93ebf3
--- /dev/null
@@ -0,0 +1,28 @@
+From 52039b6ffb6e78c2f77319b167dceab9aa51d13f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <schmidt.earl.f@gmail.com>
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch b/target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch
new file mode 100644 (file)
index 0000000..6de340c
--- /dev/null
@@ -0,0 +1,79 @@
+From 6ef818eed60db70e9caf6bdf74cc1f9943994226 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ .../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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch b/target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch
new file mode 100644 (file)
index 0000000..d8307f9
--- /dev/null
@@ -0,0 +1,41 @@
+From bd8e59b0456870997fb917bcd3b3b696e78d4ac2 Mon Sep 17 00:00:00 2001
+From: 6by9 <6by9@users.noreply.github.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch b/target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch
new file mode 100644 (file)
index 0000000..2255efd
--- /dev/null
@@ -0,0 +1,23 @@
+From 713a7ef9d73fca0f7fed122cb854d930b7a6ba5a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ 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},
diff --git a/target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch
new file mode 100644 (file)
index 0000000..43f63a1
--- /dev/null
@@ -0,0 +1,25 @@
+From d4c3133378b377ee519ea50247339cd61221fc47 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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>;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch
new file mode 100644 (file)
index 0000000..66c1478
--- /dev/null
@@ -0,0 +1,308 @@
+From 76c457e7e2920342637b1955fbaadf2aae282f05 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch b/target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch
new file mode 100644 (file)
index 0000000..a35e7f4
--- /dev/null
@@ -0,0 +1,137 @@
+From e866f9fc7c6dd6af1e74ce6fa50db9ba21acae5e Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+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 <hias@horus.com>
+Link: https://lore.kernel.org/r/20230624165216.5719-1-hias@horus.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+---
+ 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));
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch b/target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch
new file mode 100644 (file)
index 0000000..31a351a
--- /dev/null
@@ -0,0 +1,86 @@
+From 3f388718331b5ce2acd34730448db001759868aa Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+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 <hias@horus.com>
+Link: https://lore.kernel.org/r/20230624165232.5751-1-hias@horus.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+---
+ 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;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch b/target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch
new file mode 100644 (file)
index 0000000..a423b80
--- /dev/null
@@ -0,0 +1,44 @@
+From 9c5a7f04cab6b020389d7c5af155b1ee7f46537d Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+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 <lee.jackson@arducam.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch b/target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch
new file mode 100644 (file)
index 0000000..475ae63
--- /dev/null
@@ -0,0 +1,105 @@
+From 7b3d0124c5cf462d5be0b0d4e558002b74750911 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+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 <lee.jackson@arducam.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch b/target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch
new file mode 100644 (file)
index 0000000..8f96b33
--- /dev/null
@@ -0,0 +1,163 @@
+From b9d2d1862aa5b798cecb87a95d970ad34a4aebc0 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+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 <lee.jackson@arducam.com>
+---
+ 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch b/target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch
new file mode 100644 (file)
index 0000000..bf16463
--- /dev/null
@@ -0,0 +1,24 @@
+From 6f4106f7a7fdcbc03290008713915b4122988c90 Mon Sep 17 00:00:00 2001
+From: James Hughes <JamesH65@users.noreply.github.com>
+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 (james.hughes@raspberrypi.com)
+---
+ 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)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch b/target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch
new file mode 100644 (file)
index 0000000..9265e93
--- /dev/null
@@ -0,0 +1,120 @@
+From 1d15e6a34222cc8d8eb1050e7a3e276b0348be41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <p.rosenberger@kunbus.com>
+[lukas: fix netif_dbg() indentation as well, wordsmith commit message]
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+
+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 <lukas@wunner.de>
+
+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 <p.rosenberger@kunbus.com>
+[lukas: leave 2nd function parameter unchanged, wordsmith commit message]
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch b/target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch
new file mode 100644 (file)
index 0000000..593c95e
--- /dev/null
@@ -0,0 +1,1455 @@
+From a2d2745c311baa588fb0fffbe38076294f06b7c0 Mon Sep 17 00:00:00 2001
+From: Nicolai Buchwitz <n.buchwitz@kunbus.com>
+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 <n.buchwitz@kunbus.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch b/target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch
new file mode 100644 (file)
index 0000000..87097d2
--- /dev/null
@@ -0,0 +1,329 @@
+From 3ece03b1575b0c3a0989e372aaaa4557ae74dc89 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <jaikumar@cem-solutions.net>
+- *            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.
diff --git a/target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch b/target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch
new file mode 100644 (file)
index 0000000..22bca6c
--- /dev/null
@@ -0,0 +1,21 @@
+From 2addf7045f2b4866ab819f48e4d32f5734a32134 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch b/target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch
new file mode 100644 (file)
index 0000000..82a3c9e
--- /dev/null
@@ -0,0 +1,48 @@
+From b84b8a9ad2046a855a7044b6368def01ddd5de6e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch b/target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch
new file mode 100644 (file)
index 0000000..0539b19
--- /dev/null
@@ -0,0 +1,68 @@
+From 31822340129e3c4030500d7f30ce4d19bbf9dd40 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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?",
diff --git a/target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch b/target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch
new file mode 100644 (file)
index 0000000..f10c0f0
--- /dev/null
@@ -0,0 +1,25 @@
+From 5fb3b300557d6a6902e7321f42fdabb8c09eef54 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ 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",
diff --git a/target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch
new file mode 100644 (file)
index 0000000..535511e
--- /dev/null
@@ -0,0 +1,78 @@
+From e1016d61e3dcb058932e8ec5072f2c4bbb05fcb7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
diff --git a/target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch b/target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch
new file mode 100644 (file)
index 0000000..d5cce9a
--- /dev/null
@@ -0,0 +1,162 @@
+From 4b729a06b15fc5ee3694dcc62346dcb718ae4290 Mon Sep 17 00:00:00 2001
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+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/20230313172510.3851-1-socketcan@hartkopp.net
+- use ARRAY_SIZE() to reference DEFAULT_MAX_PDU_SIZE only at one place
+
+changes since v1: https://lore.kernel.org/all/20230311143446.3183-1-socketcan@hartkopp.net
+- 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 <socketcan@hartkopp.net>
+Link: https://lore.kernel.org/all/20230326115911.15094-1-socketcan@hartkopp.net
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+---
+ 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)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch b/target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch
new file mode 100644 (file)
index 0000000..ee4079c
--- /dev/null
@@ -0,0 +1,40 @@
+From e1b03ea9e84320e6bf36a1486abaebbceadd7fc7 Mon Sep 17 00:00:00 2001
+From: Ben Benson <ben.benson@raspberrypi.com>
+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 <ben.benson@raspberrypi.com>
+---
+ 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch b/target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch
new file mode 100644 (file)
index 0000000..f45b68f
--- /dev/null
@@ -0,0 +1,26 @@
+From 74bc238e86e62109c74d8f229dc105bf3818b4a7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch b/target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch
new file mode 100644 (file)
index 0000000..4328378
--- /dev/null
@@ -0,0 +1,21 @@
+From 282819aead0166af415b780241dc2def4caee7f4 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:12:01 +0000
+Subject: [PATCH] char: broadcom: vc_mem: Fix preprocessor conditional
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch b/target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch
new file mode 100644 (file)
index 0000000..fe74c75
--- /dev/null
@@ -0,0 +1,32 @@
+From ec61075a786c455444a1d5df338a41bacfce0bb1 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:23:02 +0000
+Subject: [PATCH] drivers: dwc_otg: Fix fallthrough warnings
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ 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:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch b/target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch
new file mode 100644 (file)
index 0000000..edd117c
--- /dev/null
@@ -0,0 +1,61 @@
+From 2dd2f36d10961e3819ff0525ae2567e601973826 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+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 <dereference23@outlook.com>
+---
+ 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch b/target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch
new file mode 100644 (file)
index 0000000..d6fd3a9
--- /dev/null
@@ -0,0 +1,21 @@
+From 3333d45347d313ea589b8b8da1193d342060a946 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:36:45 +0000
+Subject: [PATCH] media: i2c: ov2311: Fix uninitialized variable usage
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch b/target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch
new file mode 100644 (file)
index 0000000..035b5b0
--- /dev/null
@@ -0,0 +1,29 @@
+From e89e7655a197d28df49da2be7e2003436cf52197 Mon Sep 17 00:00:00 2001
+From: Ignacio Larrain <ilarrain@gmail.com>
+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",
diff --git a/target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch b/target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch
new file mode 100644 (file)
index 0000000..cf703b0
--- /dev/null
@@ -0,0 +1,102 @@
+From 3fa2fbb7f6e60b85086e454403c5eab1af63b1aa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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";
diff --git a/target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch b/target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch
new file mode 100644 (file)
index 0000000..23953c9
--- /dev/null
@@ -0,0 +1,25 @@
+From 9d9586dc0c0deecb90675bd70862fe262f7376ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 14 Jun 2023 14:25:21 +0100
+Subject: [PATCH] dtoverlays: Fix README text for i2c-fan
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch b/target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch
new file mode 100644 (file)
index 0000000..48e2fcd
--- /dev/null
@@ -0,0 +1,50 @@
+From e804bd1843236a63815e9acfb1a38ebf9a28ef5b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch b/target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch
new file mode 100644 (file)
index 0000000..10b1f15
--- /dev/null
@@ -0,0 +1,58 @@
+From 5e54398e1b61335883dff1be46a6c8b3ca973926 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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>;
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch b/target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch
new file mode 100644 (file)
index 0000000..0bce9f9
--- /dev/null
@@ -0,0 +1,36 @@
+From f59fe2d1bd056af117eb512bb0e9210a943c6d47 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch b/target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch
new file mode 100644 (file)
index 0000000..71f1063
--- /dev/null
@@ -0,0 +1,50 @@
+From 50c5a8558f4aaa54a3c4f5a8c2b6053f641d94eb Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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,
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch b/target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch
new file mode 100644 (file)
index 0000000..5af2382
--- /dev/null
@@ -0,0 +1,27 @@
+From 65742d7116e89b08858fcd7d67bd521ee19ee837 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ .../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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch b/target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch
new file mode 100644 (file)
index 0000000..873077f
--- /dev/null
@@ -0,0 +1,286 @@
+From cee471c3ada3215d6dfc53fb0f1b97548444dea7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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>/
diff --git a/target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch b/target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch
new file mode 100644 (file)
index 0000000..ae3c019
--- /dev/null
@@ -0,0 +1,192 @@
+From f0061ffc98c6e027c5774e2a24ceadcfee4167ea Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch b/target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch
new file mode 100644 (file)
index 0000000..3315055
--- /dev/null
@@ -0,0 +1,63 @@
+From bf9fb25f3265605572f04e5c7836bb83ee345236 Mon Sep 17 00:00:00 2001
+From: Chao Yu <chao@kernel.org>
+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 <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+---
+ 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)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch b/target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch
new file mode 100644 (file)
index 0000000..a179d93
--- /dev/null
@@ -0,0 +1,161 @@
+From e079555a4c68356e58249cfc041b28f6eb455bd5 Mon Sep 17 00:00:00 2001
+From: Stefan Wahren <wahrenst@gmx.net>
+Date: Sat, 4 May 2019 17:06:15 +0200
+Subject: [PATCH] hwrng: iproc-rng200: Add BCM2838 support
+
+The HWRNG on the BCM2838 is compatible to iproc-rng200, so add the
+support to this driver instead of bcm2835-rng.
+
+Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
+
+hwrng: iproc-rng200: Correct SoC name
+
+The Pi 4 SoC is called BCM2711, not BCM2838.
+
+Fixes: "hwrng: iproc-rng200: Add BCM2838 support"
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/hw_random/Kconfig        |  2 +-
+ 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
+@@ -104,7 +104,7 @@ config HW_RANDOM_IPROC_RNG200
+       default HW_RANDOM
+       help
+         This driver provides kernel-side support for the RNG200
+-        hardware found on the Broadcom iProc and STB SoCs.
++        hardware found on the Broadcom iProc, BCM2711 and STB SoCs.
+         To compile this driver as a module, choose M here: the
+         module will be called iproc-rng200
+--- a/drivers/char/hw_random/iproc-rng200.c
++++ b/drivers/char/hw_random/iproc-rng200.c
+@@ -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
++#define RNG_CTRL_RNG_DIV_CTRL_SHIFT                   13
+ #define RNG_SOFT_RESET_OFFSET                         0x04
+ #define RNG_SOFT_RESET                                        0x00000001
+@@ -28,16 +30,23 @@
+ #define RBG_SOFT_RESET_OFFSET                         0x08
+ #define RBG_SOFT_RESET                                        0x00000001
++#define RNG_TOTAL_BIT_COUNT_OFFSET                    0x0C
++
++#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET          0x10
++
+ #define RNG_INT_STATUS_OFFSET                         0x18
+ #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK   0x80000000
+ #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK       0x00020000
+ #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK             0x00000020
+ #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK      0x00000001
++#define RNG_INT_ENABLE_OFFSET                         0x1C
++
+ #define RNG_FIFO_DATA_OFFSET                          0x20
+ #define RNG_FIFO_COUNT_OFFSET                         0x24
+ #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK            0x000000FF
++#define RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT               8
+ struct iproc_rng200_dev {
+       struct hwrng rng;
+@@ -158,6 +167,64 @@ static int iproc_rng200_init(struct hwrn
+       return 0;
+ }
++static int bcm2711_rng200_read(struct hwrng *rng, void *buf, size_t max,
++                             bool wait)
++{
++      struct iproc_rng200_dev *priv = to_rng_priv(rng);
++      u32 max_words = max / sizeof(u32);
++      u32 num_words, count, val;
++
++      /* ensure warm up period has elapsed */
++      while (1) {
++              val = ioread32(priv->base + RNG_TOTAL_BIT_COUNT_OFFSET);
++              if (val > 16)
++                      break;
++              cpu_relax();
++      }
++
++      /* ensure fifo is not empty */
++      while (1) {
++              num_words = ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
++                          RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK;
++              if (num_words)
++                      break;
++              if (!wait)
++                      return 0;
++              cpu_relax();
++      }
++
++      if (num_words > max_words)
++              num_words = max_words;
++
++      for (count = 0; count < num_words; count++) {
++              ((u32 *)buf)[count] = ioread32(priv->base +
++                                             RNG_FIFO_DATA_OFFSET);
++      }
++
++      return num_words * sizeof(u32);
++}
++
++static int bcm2711_rng200_init(struct hwrng *rng)
++{
++      struct iproc_rng200_dev *priv = to_rng_priv(rng);
++      uint32_t val;
++
++      if (ioread32(priv->base + RNG_CTRL_OFFSET) & RNG_CTRL_RNG_RBGEN_MASK)
++              return 0;
++
++      /* initial numbers generated are "less random" so will be discarded */
++      val = 0x40000;
++      iowrite32(val, priv->base + RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET);
++      /* min fifo count to generate full interrupt */
++      val = 2 << RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT;
++      iowrite32(val, priv->base + RNG_FIFO_COUNT_OFFSET);
++      /* enable the rng - 1Mhz sample rate */
++      val = (0x3 << RNG_CTRL_RNG_DIV_CTRL_SHIFT) | RNG_CTRL_RNG_RBGEN_MASK;
++      iowrite32(val, priv->base + RNG_CTRL_OFFSET);
++
++      return 0;
++}
++
+ static void iproc_rng200_cleanup(struct hwrng *rng)
+ {
+       struct iproc_rng200_dev *priv = to_rng_priv(rng);
+@@ -184,11 +251,17 @@ static int iproc_rng200_probe(struct pla
+       dev_set_drvdata(dev, priv);
+-      priv->rng.name = "iproc-rng200";
+-      priv->rng.read = iproc_rng200_read;
+-      priv->rng.init = iproc_rng200_init;
++      priv->rng.name = pdev->name;
+       priv->rng.cleanup = iproc_rng200_cleanup;
++      if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-rng200")) {
++              priv->rng.init = bcm2711_rng200_init;
++              priv->rng.read = bcm2711_rng200_read;
++      } else {
++              priv->rng.init = iproc_rng200_init;
++              priv->rng.read = iproc_rng200_read;
++      }
++
+       /* Register driver */
+       ret = devm_hwrng_register(dev, &priv->rng);
+       if (ret) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch b/target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch
new file mode 100644 (file)
index 0000000..b086762
--- /dev/null
@@ -0,0 +1,39 @@
+From 6f634d7efb8876e5953c30c0a613aaa5f575fe05 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <jim2101024@gmail.com>
+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/20221011184211.18128-3-jim2101024@gmail.com
+Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
+Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch b/target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch
new file mode 100644 (file)
index 0000000..19550b6
--- /dev/null
@@ -0,0 +1,47 @@
+From cc08810f89e52337a99cc6ae5f53f08588357c5f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
++      };
++};
diff --git a/target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch b/target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch
new file mode 100644 (file)
index 0000000..aaf49e6
--- /dev/null
@@ -0,0 +1,26 @@
+From 406e7dc82be6ce1b81c88b418640daeef6c2be42 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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";
diff --git a/target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch b/target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch
new file mode 100644 (file)
index 0000000..6a5c40e
--- /dev/null
@@ -0,0 +1,7672 @@
+From 1196bf1a7736ff0ab79f5012fa84082e298031a7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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"
diff --git a/target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch b/target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch
new file mode 100644 (file)
index 0000000..7e39ac8
--- /dev/null
@@ -0,0 +1,282 @@
+From fa18902ee1e53ad391a455a01be3ab2ea1c5af5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch b/target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch
new file mode 100644 (file)
index 0000000..7e0886d
--- /dev/null
@@ -0,0 +1,20 @@
+From 22ae3b2ee3293278e647877b269a5aebad3f077d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch
new file mode 100644 (file)
index 0000000..429c3e2
--- /dev/null
@@ -0,0 +1,1324 @@
+From af7e60a33f0b5ce84bffb69ba084ba1edd180195 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+pinctrl: bcm2712: on C0 the regular GPIO pad control register moves too
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+pinctrl: bcm2712: Implement (partially) pinconf_get
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+pinctrl: bcm2712: Guard against bad func numbers
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch
new file mode 100644 (file)
index 0000000..05d403a
--- /dev/null
@@ -0,0 +1,498 @@
+From b627647c4500d39cb026924b608841fdf4d4d7e9 Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch b/target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch
new file mode 100644 (file)
index 0000000..1aea0b3
--- /dev/null
@@ -0,0 +1,90 @@
+From 9564939f1a92e5f9807461539de28c50e5bee440 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch b/target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch
new file mode 100644 (file)
index 0000000..2ac7b78
--- /dev/null
@@ -0,0 +1,3788 @@
+From ce14be51d71bf39893786d380cbb82e81d2a10d5 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.org>
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+media: pisp_be: Advertise the meta output format explictily.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+v4l2: pisp_be: Move format definitions into v4l2 core
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: raspberrypi: Move PiSP common headers to a single location
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: raspberrypi: Remove old pispbe driver.
+
+This is now supersede by the driver in drivers/media/platform/raspberrypi/
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+media: pisp: Checkpatch and coding style fixups
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+
+media: pisp_be: More coding style fixups
+
+media: platform: bcm2712: pisp_be: Fix crash when buffer format not set
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+
+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 <david.plowman@raspberrypi.com>
+
+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 <nick.hollinghurst@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+bcm2712: Use BIT() macro
+
+Use the BIT() macro instead of plain bit shifting.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Invert condition in pispbe_schedule_internal()
+
+Return earlier and save one indentation level
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Invert condition in for loop
+
+Save one indentation level by continuing if the node is not streaming.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Do not declare a local variable
+
+There already is a truct pispbe_node *node in the function scope.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm21712: Siplify pispbe_schedule_one()
+
+A little more verbose but easier to follow ?
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <nick.hollinghurst@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Demote info message
+
+Demote info message about clock enablement to dev_dbg()
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <naush@raspberrypi.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Create v4l2_subdev for ISP entity
+
+Create a v4l2 subdevice to represent the PISPBE ISP entity.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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/linux-media@vger.kernel.org/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 <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <jacopo.mondi@ideasonboard.com>
+
+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 <nick.hollinghurst@raspberrypi.com>
+
+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 <naush@raspberrypi.com>
+
+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 <tomi.valkeinen@ideasonboard.com>
+
+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 <naush@raspberrypi.com>
+---
+ 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 <david.plowman@raspberrypi.com>");
++MODULE_AUTHOR("Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>");
++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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch b/target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch
new file mode 100644 (file)
index 0000000..022a94a
--- /dev/null
@@ -0,0 +1,386 @@
+From 89b748416358e4e04765b9a4f20e1c3d256b9d9e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+---
+ 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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch b/target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch
new file mode 100644 (file)
index 0000000..a4328e5
--- /dev/null
@@ -0,0 +1,47 @@
+From 87b1126181f79fb2558652af0d7fafd9deaab5f3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch b/target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch
new file mode 100644 (file)
index 0000000..88d92fb
--- /dev/null
@@ -0,0 +1,418 @@
+From bd36586dd9e05bde8e23dc3d99771269b48b65f8 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+---
+ 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),
diff --git a/target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch b/target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch
new file mode 100644 (file)
index 0000000..f6944e2
--- /dev/null
@@ -0,0 +1,384 @@
+From 4ffa5f2c5fc7854683964bb2f2bf23907c18213f Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+drivers: usb: dwc3: remove downstream quirk dis-in-autoretry
+
+Upstream have unilaterally disabled the feature.
+
+Partially reverts 6e9142a26ee0fdc3a5adc49ed6cedc0b16ec2ed1 (downstream)
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch b/target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch
new file mode 100644 (file)
index 0000000..a6f0c19
--- /dev/null
@@ -0,0 +1,37 @@
+From 480c8e9f48f8a96c457eb3dc0079a73598fb7477 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.org>
+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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch b/target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch
new file mode 100644 (file)
index 0000000..179d037
--- /dev/null
@@ -0,0 +1,1108 @@
+From 29702857d1ab71243ea6c247dfe9b5bc43dd422f Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <phil@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+
+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 <jonathan@raspberrypi.com>
+---
+ 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:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch b/target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch
new file mode 100644 (file)
index 0000000..3b50329
--- /dev/null
@@ -0,0 +1,42 @@
+From 9a11300e46344917226b986a8740e7581d66adf3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 7 Feb 2022 09:20:49 +0000
+Subject: [PATCH] V4L2: Add PiSP opaque formats to V4L2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch b/target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch
new file mode 100644 (file)
index 0000000..6836922
--- /dev/null
@@ -0,0 +1,39 @@
+From 01f31f4145d49a30eb553c65ea755dde8dba1de0 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 2 Mar 2022 16:10:50 +0000
+Subject: [PATCH] V4L2: Add PiSP compressed formats to V4L2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch
new file mode 100644 (file)
index 0000000..9e98dd0
--- /dev/null
@@ -0,0 +1,249 @@
+From c93f469dabdbed822e5abeb5283d79fc9faa858c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch
new file mode 100644 (file)
index 0000000..dd252cb
--- /dev/null
@@ -0,0 +1,442 @@
+From 7196a12b94e90225686e6c34cdf65a583214f7a5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <phil@raspberrypi.com>");
++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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch
new file mode 100644 (file)
index 0000000..f947824
--- /dev/null
@@ -0,0 +1,65 @@
+From 00ff2819eb852b54fe22e7181646e40d560576dc Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch
new file mode 100644 (file)
index 0000000..2a861d1
--- /dev/null
@@ -0,0 +1,2208 @@
+From 66517cdfea750b89d86f78af55ef773cbd3e005f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 <naush@raspberrypi.com>");
++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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch b/target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch
new file mode 100644 (file)
index 0000000..d0180a6
--- /dev/null
@@ -0,0 +1,60 @@
+From 19b934ce3763c9465c5c80302f7c142d30b75869 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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__ */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch
new file mode 100644 (file)
index 0000000..c824cdd
--- /dev/null
@@ -0,0 +1,1666 @@
+From 4d4cc5be473a7767052122a87393a83d10f9ed41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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__ */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch b/target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch
new file mode 100644 (file)
index 0000000..b0b9897
--- /dev/null
@@ -0,0 +1,129 @@
+From f88da9e21d8eff58eeb9280ae96bf9593121d8eb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 13:24:51 +0100
+Subject: [PATCH] serial: pl011: rp1 uart support
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch b/target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch
new file mode 100644 (file)
index 0000000..6bc21a6
--- /dev/null
@@ -0,0 +1,83 @@
+From 4a5ac516ca0a820e7c006ae408872009e37e114b Mon Sep 17 00:00:00 2001
+From: Liam Fraser <liam@raspberrypi.com>
+Date: Thu, 14 Mar 2019 16:01:26 +0000
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: define sdio timeout clocks
+
+Signed-off-by: Liam Fraser <liam@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch b/target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch
new file mode 100644 (file)
index 0000000..12d614f
--- /dev/null
@@ -0,0 +1,83 @@
+From 14a43b3fd43bf9b230f93d1eba276d40aac969ba Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 14:07:32 +0100
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: rp1 sdio changes
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch
new file mode 100644 (file)
index 0000000..7db5202
--- /dev/null
@@ -0,0 +1,641 @@
+From b427cc1a83404f48b12dec2efbef076b38df6218 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 14:20:07 +0100
+Subject: [PATCH] clk: rp1: Add sdio-clk driver
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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 <liam@raspberrypi.com>");
++MODULE_DESCRIPTION("RP1 SDIO clock driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch b/target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch
new file mode 100644 (file)
index 0000000..ea3b315
--- /dev/null
@@ -0,0 +1,76 @@
+From 50adadfaf324ed5cbb59ce2b85eda59de4e3801a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch
new file mode 100644 (file)
index 0000000..fff6cfd
--- /dev/null
@@ -0,0 +1,355 @@
+From 0a1cd70189daec3baf4b4a233dd8e25ffbb9d512 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch b/target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch
new file mode 100644 (file)
index 0000000..68eca79
--- /dev/null
@@ -0,0 +1,64 @@
+From 8a9c0607ce0daa91c48faefd70ea73bda54ed0ae Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 29 Nov 2022 10:09:54 +0000
+Subject: [PATCH] spi: dw: Handle combined tx and rx messages
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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))
diff --git a/target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch b/target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch
new file mode 100644 (file)
index 0000000..241d6c4
--- /dev/null
@@ -0,0 +1,292 @@
+From 824f18efc8ad59e2783570ae2df83e2cd16b9f04 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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 <naush@raspberrypi.com>
++
++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 (naush@raspberrypi.com)
++ *
++ * Based on the pwm-bcm2835 driver by:
++ * Bart Tanghe <bart.tanghe@thomasmore.be>
++ */
++
++#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 <naush@raspberrypi.com");
++MODULE_DESCRIPTION("RP1 PWM driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0885-drm-Add-RP1-DSI-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0885-drm-Add-RP1-DSI-driver.patch
new file mode 100644 (file)
index 0000000..b82d53e
--- /dev/null
@@ -0,0 +1,2678 @@
+From f93caa69a9af6476cd4d93944a83acd227e68fd4 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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);
++}
diff --git a/target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch
new file mode 100644 (file)
index 0000000..2023bf7
--- /dev/null
@@ -0,0 +1,1552 @@
+From 61c3065f89d4447c7e4cf61a466ebc3c4a834ad2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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;
++}
diff --git a/target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch
new file mode 100644 (file)
index 0000000..5bf657a
--- /dev/null
@@ -0,0 +1,3078 @@
+From 09c2c6aad0fed44182defecd274579770feb0ae2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch b/target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch
new file mode 100644 (file)
index 0000000..cf0b02c
--- /dev/null
@@ -0,0 +1,75 @@
+From 3a419974ba02d32795a5ecfaf3c020f23173b6a1 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 14 Feb 2023 20:58:59 +0000
+Subject: [PATCH] v4l2: Add pisp compression format support to v4l2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch b/target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch
new file mode 100644 (file)
index 0000000..038633b
--- /dev/null
@@ -0,0 +1,4527 @@
+From 8a31623de7f034f6521b348e9a510e78a6e7e493 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 14 Feb 2023 17:30:12 +0000
+Subject: [PATCH] media: rp1: Add CFE (Camera Front End) support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ 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 <naush@raspberrypi.com>");
++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_ */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch b/target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch
new file mode 100644 (file)
index 0000000..7d81e03
--- /dev/null
@@ -0,0 +1,38 @@
+From 2be65d1fd1f7d3cf6f59b58b53e285400f04a160 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ .../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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch b/target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch
new file mode 100644 (file)
index 0000000..06d69c7
--- /dev/null
@@ -0,0 +1,29 @@
+From 9ef0615a5c5f93cb72af8df3a2dae6d23b106eb5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch b/target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch
new file mode 100644 (file)
index 0000000..b18e5f8
--- /dev/null
@@ -0,0 +1,59 @@
+From 06f794e8cb227249e03893e4b4923ff58556eb60 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Mar 2021 14:49:23 +0000
+Subject: [PATCH] ASoC: dwc: Support set_bclk_ratio
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch b/target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch
new file mode 100644 (file)
index 0000000..474903c
--- /dev/null
@@ -0,0 +1,81 @@
+From b3b1177092d4d2ba6df74042d39aa42c5055f687 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch b/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch
new file mode 100644 (file)
index 0000000..73704a8
--- /dev/null
@@ -0,0 +1,128 @@
+From e6baee4502c0228c79408b047096a1259a84353f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch b/target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch
new file mode 100644 (file)
index 0000000..fa5eb61
--- /dev/null
@@ -0,0 +1,88 @@
+From 9c6694c24f26ea435165431d41c72451fadbd753 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch b/target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch
new file mode 100644 (file)
index 0000000..9f4f7a0
--- /dev/null
@@ -0,0 +1,304 @@
+From f476db1b71e8b82e5299168f963a2fefb7a395e2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch
new file mode 100644 (file)
index 0000000..c3cf214
--- /dev/null
@@ -0,0 +1,343 @@
+From cad3c92ff0c1a5fa539d08b695b0f6b326924890 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Mar 2023 18:04:42 +0000
+Subject: [PATCH] hwmon: Add RP1 ADC and temperature driver
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ 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 <phil@raspberrypi.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch
new file mode 100644 (file)
index 0000000..3721017
--- /dev/null
@@ -0,0 +1,69 @@
+From 0c7aeb96fd3ab68011ba6c24239c501190890308 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch
new file mode 100644 (file)
index 0000000..e7e3652
--- /dev/null
@@ -0,0 +1,76 @@
+From 9cf85a95eeb239a079a3485bd1d0447431bdc7f1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch b/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch
new file mode 100644 (file)
index 0000000..ff84714
--- /dev/null
@@ -0,0 +1,150 @@
+From 380c336af070edf85826abbb0057bf92a03ec466 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch b/target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch
new file mode 100644 (file)
index 0000000..1b7952a
--- /dev/null
@@ -0,0 +1,55 @@
+From 586f87307e75552292cfc6c76b81cd38d5ec31e2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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"
diff --git a/target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch b/target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch
new file mode 100644 (file)
index 0000000..1a51a85
--- /dev/null
@@ -0,0 +1,672 @@
+From 3f949caeef21269afc67dd62ae9826204f215934 Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+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.
diff --git a/target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch b/target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch
new file mode 100644 (file)
index 0000000..ed1d5f0
--- /dev/null
@@ -0,0 +1,24 @@
+From 22fb30936524ae96151789741885edbc45efb53d Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch b/target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch
new file mode 100644 (file)
index 0000000..6418933
--- /dev/null
@@ -0,0 +1,19 @@
+From 18bc419d38eda06ded78c7b702c0e21e5af8f24c Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+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" },
diff --git a/target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch b/target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch
new file mode 100644 (file)
index 0000000..3c53996
--- /dev/null
@@ -0,0 +1,64 @@
+From 12c7ea43b930976f35ce75d11fd3f55438868e13 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch b/target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch
new file mode 100644 (file)
index 0000000..43c5c39
--- /dev/null
@@ -0,0 +1,19 @@
+From 5970fa51663511d7f773db7109ff6fa2504f186a Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+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
diff --git a/target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch
new file mode 100644 (file)
index 0000000..14e6b0a
--- /dev/null
@@ -0,0 +1,328 @@
+From fdf9cab5eaa849e90b12e17718bc47130a91433c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 <jonathan@raspberrypi.com>");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch b/target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch
new file mode 100644 (file)
index 0000000..5d6eb16
--- /dev/null
@@ -0,0 +1,301 @@
+From 27bda80061b46e18fe83be11228df5365363b377 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 26 Apr 2023 13:44:15 +0100
+Subject: [PATCH] drivers: char: delete bcm2835-gpiomem
+
+This functionality is now provided by raspberrypi-gpiomem.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/char/broadcom/Kconfig           |   8 -
+ drivers/char/broadcom/Makefile          |   1 -
+ drivers/char/broadcom/bcm2835-gpiomem.c | 258 ------------------------
+ 3 files changed, 267 deletions(-)
+ delete mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c
+
+--- a/drivers/char/broadcom/Kconfig
++++ b/drivers/char/broadcom/Kconfig
+@@ -23,14 +23,6 @@ config BCM_VCIO
+ endif
+-config BCM2835_DEVGPIOMEM
+-      tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
+-      default m
+-      help
+-              Provides users with root-free access to the GPIO registers
+-              on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
+-              register page to the user's pointer.
+-
+ config BCM2835_SMI_DEV
+       tristate "Character device driver for BCM2835 Secondary Memory Interface"
+       depends on BCM2835_SMI
+--- a/drivers/char/broadcom/Makefile
++++ b/drivers/char/broadcom/Makefile
+@@ -1,5 +1,4 @@
+ obj-$(CONFIG_BCM2708_VCMEM)   += vc_mem.o
+ obj-$(CONFIG_BCM_VCIO)                += vcio.o
+-obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
+ obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o
+ obj-$(CONFIG_RPIVID_MEM)      += rpivid-mem.o
+--- a/drivers/char/broadcom/bcm2835-gpiomem.c
++++ /dev/null
+@@ -1,258 +0,0 @@
+-/**
+- * GPIO memory device driver
+- *
+- * Creates a chardev /dev/gpiomem which will provide user access to
+- * the BCM2835's GPIO registers when it is mmap()'d.
+- * No longer need root for user GPIO access, but without relaxing permissions
+- * on /dev/mem.
+- *
+- * Written by Luke Wren <luke@raspberrypi.org>
+- * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+- *
+- * Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions
+- * are met:
+- * 1. Redistributions of source code must retain the above copyright
+- *    notice, this list of conditions, and the following disclaimer,
+- *    without modification.
+- * 2. Redistributions in binary form must reproduce the above copyright
+- *    notice, this list of conditions and the following disclaimer in the
+- *    documentation and/or other materials provided with the distribution.
+- * 3. The names of the above-listed copyright holders may not be used
+- *    to endorse or promote products derived from this software without
+- *    specific prior written permission.
+- *
+- * ALTERNATIVELY, this software may be distributed under the terms of the
+- * GNU General Public License ("GPL") version 2, as published by the Free
+- * Software Foundation.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/of.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 DEVICE_NAME "bcm2835-gpiomem"
+-#define DRIVER_NAME "gpiomem-bcm2835"
+-#define DEVICE_MINOR 0
+-
+-struct bcm2835_gpiomem_instance {
+-      unsigned long gpio_regs_phys;
+-      struct device *dev;
+-};
+-
+-static struct cdev bcm2835_gpiomem_cdev;
+-static dev_t bcm2835_gpiomem_devid;
+-static struct class *bcm2835_gpiomem_class;
+-static struct device *bcm2835_gpiomem_dev;
+-static struct bcm2835_gpiomem_instance *inst;
+-
+-
+-/****************************************************************************
+-*
+-*   GPIO mem chardev file ops
+-*
+-***************************************************************************/
+-
+-static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
+-{
+-      int dev = iminor(inode);
+-      int ret = 0;
+-
+-      if (dev != DEVICE_MINOR) {
+-              dev_err(inst->dev, "Unknown minor device: %d", dev);
+-              ret = -ENXIO;
+-      }
+-      return ret;
+-}
+-
+-static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
+-{
+-      int dev = iminor(inode);
+-      int ret = 0;
+-
+-      if (dev != DEVICE_MINOR) {
+-              dev_err(inst->dev, "Unknown minor device %d", dev);
+-              ret = -ENXIO;
+-      }
+-      return ret;
+-}
+-
+-static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
+-#ifdef CONFIG_HAVE_IOREMAP_PROT
+-      .access = generic_access_phys
+-#endif
+-};
+-
+-static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
+-{
+-      /* Ignore what the user says - they're getting the GPIO regs
+-         whether they like it or not! */
+-      unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
+-
+-      vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
+-                                               PAGE_SIZE,
+-                                               vma->vm_page_prot);
+-      vma->vm_ops = &bcm2835_gpiomem_vm_ops;
+-      if (remap_pfn_range(vma, vma->vm_start,
+-                      gpio_page,
+-                      PAGE_SIZE,
+-                      vma->vm_page_prot)) {
+-              return -EAGAIN;
+-      }
+-      return 0;
+-}
+-
+-static const struct file_operations
+-bcm2835_gpiomem_fops = {
+-      .owner = THIS_MODULE,
+-      .open = bcm2835_gpiomem_open,
+-      .release = bcm2835_gpiomem_release,
+-      .mmap = bcm2835_gpiomem_mmap,
+-};
+-
+-
+- /****************************************************************************
+-*
+-*   Probe and remove functions
+-*
+-***************************************************************************/
+-
+-
+-static int bcm2835_gpiomem_probe(struct platform_device *pdev)
+-{
+-      int err;
+-      void *ptr_err;
+-      struct device *dev = &pdev->dev;
+-      struct resource *ioresource;
+-
+-      /* Allocate buffers and instance data */
+-
+-      inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
+-
+-      if (!inst) {
+-              err = -ENOMEM;
+-              goto failed_inst_alloc;
+-      }
+-
+-      inst->dev = dev;
+-
+-      ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-      if (ioresource) {
+-              inst->gpio_regs_phys = ioresource->start;
+-      } else {
+-              dev_err(inst->dev, "failed to get IO resource");
+-              err = -ENOENT;
+-              goto failed_get_resource;
+-      }
+-
+-      /* Create character device entries */
+-
+-      err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
+-                                DEVICE_MINOR, 1, DEVICE_NAME);
+-      if (err != 0) {
+-              dev_err(inst->dev, "unable to allocate device number");
+-              goto failed_alloc_chrdev;
+-      }
+-      cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
+-      bcm2835_gpiomem_cdev.owner = THIS_MODULE;
+-      err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
+-      if (err != 0) {
+-              dev_err(inst->dev, "unable to register device");
+-              goto failed_cdev_add;
+-      }
+-
+-      /* Create sysfs entries */
+-
+-      bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
+-      ptr_err = bcm2835_gpiomem_class;
+-      if (IS_ERR(ptr_err))
+-              goto failed_class_create;
+-
+-      bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
+-                                      bcm2835_gpiomem_devid, NULL,
+-                                      "gpiomem");
+-      ptr_err = bcm2835_gpiomem_dev;
+-      if (IS_ERR(ptr_err))
+-              goto failed_device_create;
+-
+-      dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
+-              inst->gpio_regs_phys);
+-
+-      return 0;
+-
+-failed_device_create:
+-      class_destroy(bcm2835_gpiomem_class);
+-failed_class_create:
+-      cdev_del(&bcm2835_gpiomem_cdev);
+-      err = PTR_ERR(ptr_err);
+-failed_cdev_add:
+-      unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+-failed_alloc_chrdev:
+-failed_get_resource:
+-      kfree(inst);
+-failed_inst_alloc:
+-      dev_err(inst->dev, "could not load bcm2835_gpiomem");
+-      return err;
+-}
+-
+-static int bcm2835_gpiomem_remove(struct platform_device *pdev)
+-{
+-      struct device *dev = inst->dev;
+-
+-      kfree(inst);
+-      device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
+-      class_destroy(bcm2835_gpiomem_class);
+-      cdev_del(&bcm2835_gpiomem_cdev);
+-      unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+-
+-      dev_info(dev, "GPIO mem driver removed - OK");
+-      return 0;
+-}
+-
+- /****************************************************************************
+-*
+-*   Register the driver with device tree
+-*
+-***************************************************************************/
+-
+-static const struct of_device_id bcm2835_gpiomem_of_match[] = {
+-      {.compatible = "brcm,bcm2835-gpiomem",},
+-      { /* sentinel */ },
+-};
+-
+-MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
+-
+-static struct platform_driver bcm2835_gpiomem_driver = {
+-      .probe = bcm2835_gpiomem_probe,
+-      .remove = bcm2835_gpiomem_remove,
+-      .driver = {
+-                 .name = DRIVER_NAME,
+-                 .owner = THIS_MODULE,
+-                 .of_match_table = bcm2835_gpiomem_of_match,
+-                 },
+-};
+-
+-module_platform_driver(bcm2835_gpiomem_driver);
+-
+-MODULE_ALIAS("platform:gpiomem-bcm2835");
+-MODULE_LICENSE("GPL");
+-MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
+-MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch b/target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch
new file mode 100644 (file)
index 0000000..557b7ac
--- /dev/null
@@ -0,0 +1,34 @@
+From 3cafcfbab9b5f3f1357b415b6ca09911eeb405d6 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Thu, 4 May 2023 15:48:53 +0100
+Subject: [PATCH] drivers: hwmon: rp1-adc: check conversion validity before
+ supplying value
+
+The SAR ADC architecture may complete a conversion but instability in the
+comparator can corrupt the result. Such corruption is signalled in the CS
+ERR bit, asserted alongside each conversion result.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/hwmon/rp1-adc.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/hwmon/rp1-adc.c
++++ b/drivers/hwmon/rp1-adc.c
+@@ -97,8 +97,14 @@ static int rp1_adc_read(struct rp1_adc_d
+              data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
+       ret = rp1_adc_ready_wait(data);
+-      if (!ret)
+-              *val = readl(data->base + RP1_ADC_RESULT);
++      if (ret)
++              return ret;
++
++      /* Asserted if the completed conversion had a convergence error */
++      if (readl(data->base + RP1_ADC_CS) & RP1_ADC_CS_ERR)
++              return -EIO;
++
++      *val = readl(data->base + RP1_ADC_RESULT);
+       spin_unlock(&data->lock);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch b/target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch
new file mode 100644 (file)
index 0000000..58095cf
--- /dev/null
@@ -0,0 +1,36 @@
+From 87c5545f9a66984894384da5f8c2eeb60983732a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Mar 2023 16:53:38 +0000
+Subject: [PATCH] dmaengine: bcm2835: Add BCM2712 support
+
+BCM2712 has 6 40-bit channels - DMA6 to DMA11. Add a new compatible
+string to indicate that the current platform is BCM2712.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/dma/bcm2835-dma.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -331,6 +331,12 @@ static const struct bcm2835_dma_cfg_data
+       .dma_mask = DMA_BIT_MASK(36),
+ };
++static const struct bcm2835_dma_cfg_data bcm2712_dma_cfg = {
++      .chan_40bit_mask = BIT(6) | BIT(7) | BIT(8) | BIT(9) |
++                               BIT(10) | BIT(11),
++      .dma_mask = DMA_BIT_MASK(40),
++};
++
+ static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
+ {
+       /* lite and normal channels have different max frame length */
+@@ -1260,6 +1266,7 @@ EXPORT_SYMBOL(bcm2711_dma40_memcpy);
+ static const struct of_device_id bcm2835_dma_of_match[] = {
+       { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
+       { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
++      { .compatible = "brcm,bcm2712-dma", .data = &bcm2712_dma_cfg },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch b/target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch
new file mode 100644 (file)
index 0000000..6f651a6
--- /dev/null
@@ -0,0 +1,57 @@
+From a671a2774cb3bcfb144622149757f6821aa0604c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Apr 2023 16:52:19 +0200
+Subject: [PATCH] dmaengine: bcm2835: HACK: Support DMA-Lite channels
+
+The BCM2712 has a DMA-Lite controller that is basically a BCM2835-style
+DMA controller that supports 40 bits DMA addresses.
+
+We need it for HDMI audio to work, but this breaks BCM2835-38 so we
+should rework this later.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/dma/bcm2835-dma.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -550,7 +550,7 @@ static struct bcm2835_desc *bcm2835_dma_
+                       control_block->info = info;
+                       control_block->src = src;
+                       control_block->dst = dst;
+-                      control_block->stride = 0;
++                      control_block->stride = (upper_32_bits(dst) << 8) | upper_32_bits(src);
+                       control_block->next = 0;
+               }
+@@ -575,7 +575,7 @@ static struct bcm2835_desc *bcm2835_dma_
+                        d->cb_list[frame - 1].cb)->next_cb =
+                               to_bcm2711_cbaddr(cb_entry->paddr);
+               if (frame && !c->is_40bit_channel)
+-                      d->cb_list[frame - 1].cb->next = cb_entry->paddr;
++                      d->cb_list[frame - 1].cb->next = to_bcm2711_cbaddr(cb_entry->paddr);
+               /* update src and dst and length */
+               if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -760,7 +760,10 @@ static void bcm2835_dma_start_desc(struc
+               writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | BCM2711_DMA40_CS_FLAGS(c->dreq),
+                      c->chan_base + BCM2711_DMA40_CS);
+       } else {
+-              writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
++              writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
++
++              writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++                     c->chan_base + BCM2835_DMA_ADDR);
+               writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+                      c->chan_base + BCM2835_DMA_CS);
+       }
+@@ -1129,7 +1132,7 @@ static struct dma_async_tx_descriptor *b
+                d->cb_list[frames - 1].cb)->next_cb =
+                       to_bcm2711_cbaddr(d->cb_list[0].paddr);
+       else
+-              d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
++              d->cb_list[d->frames - 1].cb->next = to_bcm2711_cbaddr(d->cb_list[0].paddr);
+       return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch
new file mode 100644 (file)
index 0000000..6f5e55d
--- /dev/null
@@ -0,0 +1,46 @@
+From c8fd69c6f567bd43ba084b95a987532940465ef5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Feb 2023 14:12:50 +0100
+Subject: [PATCH] clk: bcm: rpi: Add disp clock
+
+BCM2712 has an extra clock exposed by the firmware called DISP, and used
+by (at least) the HVS. Let's add it to the list of clocks to register in
+Linux.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c          | 5 +++++
+ include/soc/bcm2835/raspberrypi-firmware.h | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -34,6 +34,7 @@ static char *rpi_firmware_clk_names[] =
+       [RPI_FIRMWARE_M2MC_CLK_ID]      = "m2mc",
+       [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
+       [RPI_FIRMWARE_VEC_CLK_ID]       = "vec",
++      [RPI_FIRMWARE_DISP_CLK_ID]      = "disp",
+ };
+ #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
+@@ -139,6 +140,10 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NU
+               .export = true,
+               .minimize = true,
+       },
++      [RPI_FIRMWARE_DISP_CLK_ID] = {
++              .export = true,
++              .minimize = true,
++      },
+ };
+ /*
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -176,6 +176,7 @@ enum rpi_firmware_clk_id {
+       RPI_FIRMWARE_M2MC_CLK_ID,
+       RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
+       RPI_FIRMWARE_VEC_CLK_ID,
++      RPI_FIRMWARE_DISP_CLK_ID,
+       RPI_FIRMWARE_NUM_CLK_ID,
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch b/target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch
new file mode 100644 (file)
index 0000000..2df4829
--- /dev/null
@@ -0,0 +1,26 @@
+From 6370a6cd16a5aa9726bf209c0f0a3179f4011cb1 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 22 May 2023 15:31:17 +0100
+Subject: [PATCH] net: phy: broadcom: optionally enable link-down powersave
+ based on DT
+
+It's really a function of the board whether or not to use this feature
+as it may require MAC compatibility as well as interop testing.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/net/phy/broadcom.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -370,6 +370,9 @@ static int bcm54xx_config_init(struct ph
+           (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
+               bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
++      if (of_property_read_bool(np, "brcm,powerdown-enable"))
++              phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE;
++
+       bcm54xx_adjust_rxrefclk(phydev);
+       switch (BRCM_PHY_MODEL(phydev)) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch b/target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch
new file mode 100644 (file)
index 0000000..ce59f60
--- /dev/null
@@ -0,0 +1,75 @@
+From cfad3f71fc450639fc259d576d0903e9132fe34a Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 25 May 2023 14:48:28 +0100
+Subject: [PATCH] dmaengine: bcm2835: Rename to_bcm2711_cbaddr to
+ to_40bit_cbaddr
+
+As the shifted address also applies to bcm2712,
+give the function a more specific name.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/dma/bcm2835-dma.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -390,7 +390,7 @@ static inline uint32_t to_bcm2711_dsti(u
+              BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info));
+ }
+-static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr)
++static inline uint32_t to_40bit_cbaddr(dma_addr_t addr)
+ {
+       BUG_ON(addr & 0x1f);
+       return (addr >> 5);
+@@ -573,9 +573,9 @@ static struct bcm2835_desc *bcm2835_dma_
+               if (frame && c->is_40bit_channel)
+                       ((struct bcm2711_dma40_scb *)
+                        d->cb_list[frame - 1].cb)->next_cb =
+-                              to_bcm2711_cbaddr(cb_entry->paddr);
++                              to_40bit_cbaddr(cb_entry->paddr);
+               if (frame && !c->is_40bit_channel)
+-                      d->cb_list[frame - 1].cb->next = to_bcm2711_cbaddr(cb_entry->paddr);
++                      d->cb_list[frame - 1].cb->next = to_40bit_cbaddr(cb_entry->paddr);
+               /* update src and dst and length */
+               if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -755,14 +755,14 @@ static void bcm2835_dma_start_desc(struc
+       c->desc = d = to_bcm2835_dma_desc(&vd->tx);
+       if (c->is_40bit_channel) {
+-              writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++              writel(to_40bit_cbaddr(d->cb_list[0].paddr),
+                      c->chan_base + BCM2711_DMA40_CB);
+               writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | BCM2711_DMA40_CS_FLAGS(c->dreq),
+                      c->chan_base + BCM2711_DMA40_CS);
+       } else {
+               writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
+-              writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++              writel(to_40bit_cbaddr(d->cb_list[0].paddr),
+                      c->chan_base + BCM2835_DMA_ADDR);
+               writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+                      c->chan_base + BCM2835_DMA_CS);
+@@ -1130,9 +1130,9 @@ static struct dma_async_tx_descriptor *b
+       if (c->is_40bit_channel)
+               ((struct bcm2711_dma40_scb *)
+                d->cb_list[frames - 1].cb)->next_cb =
+-                      to_bcm2711_cbaddr(d->cb_list[0].paddr);
++                      to_40bit_cbaddr(d->cb_list[0].paddr);
+       else
+-              d->cb_list[d->frames - 1].cb->next = to_bcm2711_cbaddr(d->cb_list[0].paddr);
++              d->cb_list[d->frames - 1].cb->next = to_40bit_cbaddr(d->cb_list[0].paddr);
+       return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
+@@ -1252,7 +1252,7 @@ void bcm2711_dma40_memcpy(dma_addr_t dst
+       scb->len = size;
+       scb->next_cb = 0;
+-      writel(to_bcm2711_cbaddr(memcpy_scb_dma), memcpy_chan + BCM2711_DMA40_CB);
++      writel(to_40bit_cbaddr(memcpy_scb_dma), memcpy_chan + BCM2711_DMA40_CB);
+       writel(BCM2711_DMA40_MEMCPY_FLAGS | BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT,
+              memcpy_chan + BCM2711_DMA40_CS);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch b/target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch
new file mode 100644 (file)
index 0000000..a2ff2a6
--- /dev/null
@@ -0,0 +1,77 @@
+From 75f44d1416c5de17865247d6d012c37f7650437c Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 24 May 2023 19:32:16 +0100
+Subject: [PATCH] dmaengine: bcm2835: Fix dma driver for BCM2835-38
+
+The previous commit broke support on older devices.
+Make the breaking parts of patch conditional on
+the device being used.
+
+Fixes: 6e1856ac7c39 ("dmaengine: bcm2835: HACK: Support DMA-Lite channels")
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/dma/bcm2835-dma.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -102,6 +102,7 @@ struct bcm2835_chan {
+       bool is_lite_channel;
+       bool is_40bit_channel;
++      bool is_2712;
+ };
+ struct bcm2835_desc {
+@@ -550,7 +551,11 @@ static struct bcm2835_desc *bcm2835_dma_
+                       control_block->info = info;
+                       control_block->src = src;
+                       control_block->dst = dst;
+-                      control_block->stride = (upper_32_bits(dst) << 8) | upper_32_bits(src);
++                      if (c->is_2712)
++                              control_block->stride = (upper_32_bits(dst) << 8) |
++                                                      upper_32_bits(src);
++                      else
++                              control_block->stride = 0;
+                       control_block->next = 0;
+               }
+@@ -575,7 +580,8 @@ static struct bcm2835_desc *bcm2835_dma_
+                        d->cb_list[frame - 1].cb)->next_cb =
+                               to_40bit_cbaddr(cb_entry->paddr);
+               if (frame && !c->is_40bit_channel)
+-                      d->cb_list[frame - 1].cb->next = to_40bit_cbaddr(cb_entry->paddr);
++                      d->cb_list[frame - 1].cb->next = c->is_2712 ?
++                      to_40bit_cbaddr(cb_entry->paddr) : cb_entry->paddr;
+               /* update src and dst and length */
+               if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -762,7 +768,7 @@ static void bcm2835_dma_start_desc(struc
+       } else {
+               writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
+-              writel(to_40bit_cbaddr(d->cb_list[0].paddr),
++              writel(c->is_2712 ? to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr,
+                      c->chan_base + BCM2835_DMA_ADDR);
+               writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+                      c->chan_base + BCM2835_DMA_CS);
+@@ -1132,7 +1138,8 @@ static struct dma_async_tx_descriptor *b
+                d->cb_list[frames - 1].cb)->next_cb =
+                       to_40bit_cbaddr(d->cb_list[0].paddr);
+       else
+-              d->cb_list[d->frames - 1].cb->next = to_40bit_cbaddr(d->cb_list[0].paddr);
++              d->cb_list[d->frames - 1].cb->next = c->is_2712 ?
++              to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr;
+       return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
+@@ -1199,6 +1206,8 @@ static int bcm2835_dma_chan_init(struct
+       else if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
+                BCM2835_DMA_DEBUG_LITE)
+               c->is_lite_channel = true;
++      if (d->cfg_data->dma_mask == DMA_BIT_MASK(40))
++              c->is_2712 = true;
+       return 0;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch b/target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch
new file mode 100644 (file)
index 0000000..92f43fa
--- /dev/null
@@ -0,0 +1,855 @@
+From 5fd6ee7fd084838e09d4e463ae53cd9aaa7fce70 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Thu, 11 May 2023 16:37:34 +0100
+Subject: [PATCH] drivers: iommu: Add BCM2712 IOMMU
+
+Add a driver for BCM2712 IOMMUs.
+There is a small driver for the Shared IOMMU TLB Cache.
+Each IOMMU instance is a separate device.
+
+IOMMUs are set up with a "pass-through" range covering
+the lowest 40BGytes (which should cover all of SDRAM)
+for the benefit of non-IOMMU-aware devices that share
+a physical IOMMU; and translation for addresses in the
+range 40GB to 42GB.
+
+An optional parameter adds a DMA offset (which otherwise
+would be lost?) to virtual addresses for DMA masters on a
+bus such as PCIe.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/iommu/Kconfig               |   7 +
+ drivers/iommu/Makefile              |   1 +
+ drivers/iommu/bcm2712-iommu-cache.c |  77 ++++
+ drivers/iommu/bcm2712-iommu.c       | 672 ++++++++++++++++++++++++++++
+ drivers/iommu/bcm2712-iommu.h       |  45 ++
+ 5 files changed, 802 insertions(+)
+ create mode 100644 drivers/iommu/bcm2712-iommu-cache.c
+ create mode 100644 drivers/iommu/bcm2712-iommu.c
+ create mode 100644 drivers/iommu/bcm2712-iommu.h
+
+--- a/drivers/iommu/Kconfig
++++ b/drivers/iommu/Kconfig
+@@ -506,4 +506,11 @@ config SPRD_IOMMU
+         Say Y here if you want to use the multimedia devices listed above.
++config BCM2712_IOMMU
++       tristate "BCM2712 IOMMU driver"
++       depends on ARM64 && ARCH_BCM
++       select IOMMU_API
++       help
++       IOMMU driver for BCM2712
++
+ endif # IOMMU_SUPPORT
+--- a/drivers/iommu/Makefile
++++ b/drivers/iommu/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iom
+ obj-$(CONFIG_IOMMU_SVA) += iommu-sva-lib.o io-pgfault.o
+ obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
+ obj-$(CONFIG_APPLE_DART) += apple-dart.o
++obj-$(CONFIG_BCM2712_IOMMU) += bcm2712-iommu.o bcm2712-iommu-cache.o
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu-cache.c
+@@ -0,0 +1,77 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#include "bcm2712-iommu.h"
++
++#include <linux/err.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define MMUC_CONTROL_ENABLE   1
++#define MMUC_CONTROL_FLUSH    2
++#define MMUC_CONTROL_FLUSHING 4
++
++void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache)
++{
++      unsigned long flags;
++      int i;
++
++      spin_lock_irqsave(&cache->hw_lock, flags);
++      if (cache->reg_base) {
++              /* Enable and flush the TLB cache */
++              writel(MMUC_CONTROL_ENABLE | MMUC_CONTROL_FLUSH,
++                     cache->reg_base);
++
++              /* Wait for flush to complete: it should be very quick */
++              for (i = 0; i < 1024; i++) {
++                      if (!(MMUC_CONTROL_FLUSHING & readl(cache->reg_base)))
++                              break;
++                      cpu_relax();
++              }
++      }
++      spin_unlock_irqrestore(&cache->hw_lock, flags);
++}
++
++static int bcm2712_iommu_cache_probe(struct platform_device *pdev)
++{
++      struct bcm2712_iommu_cache *cache;
++
++      dev_info(&pdev->dev, __func__);
++      cache = devm_kzalloc(&pdev->dev, sizeof(*cache), GFP_KERNEL);
++      if (!cache)
++              return -ENOMEM;
++
++      cache->dev = &pdev->dev;
++      platform_set_drvdata(pdev, cache);
++      spin_lock_init(&cache->hw_lock);
++
++      /* Get IOMMUC registers; we only use the first register (IOMMUC_CTRL) */
++      cache->reg_base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(cache->reg_base)) {
++              dev_err(&pdev->dev, "Failed to get IOMMU Cache registers address\n");
++              cache->reg_base = NULL;
++      }
++      return 0;
++}
++
++static const struct of_device_id bcm2712_iommu_cache_of_match[] = {
++      {
++              . compatible = "brcm,bcm2712-iommuc"
++      },
++      { /* sentinel */ },
++};
++
++static struct platform_driver bcm2712_iommu_cache_driver = {
++      .probe = bcm2712_iommu_cache_probe,
++      .driver = {
++              .name = "bcm2712-iommu-cache",
++              .of_match_table = bcm2712_iommu_cache_of_match
++      },
++};
++
++builtin_platform_driver(bcm2712_iommu_cache_driver);
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu.c
+@@ -0,0 +1,672 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#include "bcm2712-iommu.h"
++
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/iommu.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define MMU_WR(off, val)   writel(val, mmu->reg_base + (off))
++#define MMU_RD(off)        readl(mmu->reg_base + (off))
++
++#define domain_to_mmu(d) (container_of(d, struct bcm2712_iommu_domain, base)->mmu)
++
++#define MMMU_CTRL_OFFSET                       0x00
++#define MMMU_CTRL_CAP_EXCEEDED                 BIT(27)
++#define MMMU_CTRL_CAP_EXCEEDED_ABORT_EN        BIT(26)
++#define MMMU_CTRL_CAP_EXCEEDED_INT_EN          BIT(25)
++#define MMMU_CTRL_CAP_EXCEEDED_EXCEPTION_EN    BIT(24)
++#define MMMU_CTRL_PT_INVALID                   BIT(20)
++#define MMMU_CTRL_PT_INVALID_ABORT_EN          BIT(19)
++#define MMMU_CTRL_PT_INVALID_EXCEPTION_EN      BIT(18)
++#define MMMU_CTRL_PT_INVALID_EN                BIT(17)
++#define MMMU_CTRL_WRITE_VIOLATION              BIT(12)
++#define MMMU_CTRL_WRITE_VIOLATION_ABORT_EN     BIT(11)
++#define MMMU_CTRL_WRITE_VIOLATION_INT_EN       BIT(10)
++#define MMMU_CTRL_WRITE_VIOLATION_EXCEPTION_EN BIT(9)
++#define MMMU_CTRL_BYPASS                       BIT(8)
++#define MMMU_CTRL_TLB_CLEARING                 BIT(7)
++#define MMMU_CTRL_STATS_CLEAR                  BIT(3)
++#define MMMU_CTRL_TLB_CLEAR                    BIT(2)
++#define MMMU_CTRL_STATS_ENABLE                 BIT(1)
++#define MMMU_CTRL_ENABLE                       BIT(0)
++
++#define MMMU_PT_PA_BASE_OFFSET                 0x04
++
++#define MMMU_HIT_OFFSET                        0x08
++#define MMMU_MISS_OFFSET                       0x0C
++#define MMMU_STALL_OFFSET                      0x10
++
++#define MMMU_ADDR_CAP_OFFSET                   0x14
++#define MMMU_ADDR_CAP_ENABLE                   BIT(31)
++#define ADDR_CAP_SHIFT 28 /* ADDR_CAP is defined to be in 256 MByte units */
++
++#define MMMU_SHOOT_DOWN_OFFSET                 0x18
++#define MMMU_SHOOT_DOWN_SHOOTING               BIT(31)
++#define MMMU_SHOOT_DOWN_SHOOT                  BIT(30)
++
++#define MMMU_BYPASS_START_OFFSET               0x1C
++#define MMMU_BYPASS_START_ENABLE               BIT(31)
++#define MMMU_BYPASS_START_INVERT               BIT(30)
++
++#define MMMU_BYPASS_END_OFFSET                 0x20
++#define MMMU_BYPASS_END_ENABLE                 BIT(31)
++
++#define MMMU_MISC_OFFSET                       0x24
++#define MMMU_MISC_SINGLE_TABLE                 BIT(31)
++
++#define MMMU_ILLEGAL_ADR_OFFSET                0x30
++#define MMMU_ILLEGAL_ADR_ENABLE                BIT(31)
++
++#define MMMU_DEBUG_INFO_OFFSET                 0x38
++#define MMMU_DEBUG_INFO_VERSION_MASK           0x0000000Fu
++#define MMMU_DEBUG_INFO_VA_WIDTH_MASK          0x000000F0u
++#define MMMU_DEBUG_INFO_PA_WIDTH_MASK          0x00000F00u
++#define MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK     0x000FF000u
++#define MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK   0x0FF00000u
++#define MMMU_DEBUG_INFO_BYPASS_4M              BIT(28)
++#define MMMU_DEBUG_INFO_BYPASS                 BIT(29)
++
++#define MMMU_PTE_PAGESIZE_MASK                 0xC0000000u
++#define MMMU_PTE_WRITEABLE                     BIT(29)
++#define MMMU_PTE_VALID                         BIT(28)
++
++/*
++ * BCM2712 IOMMU is organized around 4Kbyte pages (MMU_PAGE_SIZE).
++ * Linux PAGE_SIZE must not be smaller but may be larger (e.g. 4K, 16K).
++ *
++ * Unlike many larger MMUs, this one uses a 4-byte word size, allowing
++ * 1024 entries within each 4K table page, and two-level translation.
++ *
++ * Let's allocate enough table space for 2GB of translated memory (IOVA).
++ * This requires 512 4K pages (2MB) of level-2 tables, one page of
++ * top-level table (only half-filled in this particular configuration),
++ * plus one "default" page to catch illegal requests.
++ *
++ * The translated virtual address region is between 40GB and 42GB;
++ * addresses below this range pass straight through to the SDRAM.
++ *
++ * Currently we assume a 1:1:1 correspondence of IOMMU, group and domain.
++ */
++
++#define MMU_PAGE_SHIFT    12
++#define MMU_PAGE_SIZE     BIT(MMU_PAGE_SHIFT)
++
++#define PAGEWORDS_SHIFT   (MMU_PAGE_SHIFT - 2)
++#define HUGEPAGE_SHIFT    (MMU_PAGE_SHIFT + PAGEWORDS_SHIFT)
++#define L1_CHUNK_SHIFT    (MMU_PAGE_SHIFT + 2 * PAGEWORDS_SHIFT)
++
++#define APERTURE_BASE     (40ul << 30)
++#define APERTURE_SIZE     (2ul << 30)
++#define APERTURE_TOP      (APERTURE_BASE + APERTURE_SIZE)
++#define TRANSLATED_PAGES  (APERTURE_SIZE >> MMU_PAGE_SHIFT)
++#define L2_PAGES          (TRANSLATED_PAGES >> PAGEWORDS_SHIFT)
++#define TABLES_ALLOC_SIZE (L2_PAGES * MMU_PAGE_SIZE + 2 * PAGE_SIZE)
++
++static void bcm2712_iommu_init(struct bcm2712_iommu *mmu)
++{
++      unsigned int i, bypass_shift;
++      struct sg_dma_page_iter it;
++      u32 u = MMU_RD(MMMU_DEBUG_INFO_OFFSET);
++
++      /*
++       * Check IOMMU version and hardware configuration.
++       * This driver is for VC IOMMU version >= 4 (with 2-level tables)
++       * and assumes at least 36 bits of virtual and physical address space.
++       * Bigpage and superpage sizes are typically 64K and 1M, but may vary
++       * (hugepage size is fixed at 4M, the range covered by an L2 page).
++       */
++      dev_info(mmu->dev, "%s: DEBUG_INFO = 0x%08x\n", __func__, u);
++      WARN_ON(FIELD_GET(MMMU_DEBUG_INFO_VERSION_MASK, u) < 4 ||
++              FIELD_GET(MMMU_DEBUG_INFO_VA_WIDTH_MASK, u) < 6 ||
++              FIELD_GET(MMMU_DEBUG_INFO_PA_WIDTH_MASK, u) < 6 ||
++              !(u & MMMU_DEBUG_INFO_BYPASS));
++
++      mmu->bigpage_mask =
++              ((1u << FIELD_GET(MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT;
++      mmu->superpage_mask =
++              ((1u << FIELD_GET(MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT;
++      bypass_shift = (u & MMMU_DEBUG_INFO_BYPASS_4M) ?
++              HUGEPAGE_SHIFT : ADDR_CAP_SHIFT;
++
++      /* Disable MMU and clear sticky flags; meanwhile flush the TLB */
++      MMU_WR(MMMU_CTRL_OFFSET,
++             MMMU_CTRL_CAP_EXCEEDED    |
++             MMMU_CTRL_PT_INVALID      |
++             MMMU_CTRL_WRITE_VIOLATION |
++             MMMU_CTRL_STATS_CLEAR     |
++             MMMU_CTRL_TLB_CLEAR);
++
++      /*
++       * Put MMU into 2-level mode; set address cap and "bypass" range
++       * (note that some of these registers have unintuitive off-by-ones).
++       * Addresses below APERTURE_BASE are passed unchanged: this is
++       * useful for blocks which share an IOMMU with other blocks
++       * whose drivers are not IOMMU-aware.
++       */
++      MMU_WR(MMMU_MISC_OFFSET,
++             MMU_RD(MMMU_MISC_OFFSET) & ~MMMU_MISC_SINGLE_TABLE);
++      MMU_WR(MMMU_ADDR_CAP_OFFSET,
++             MMMU_ADDR_CAP_ENABLE +
++             (APERTURE_TOP >> ADDR_CAP_SHIFT) - 1);
++      if (APERTURE_BASE > 0) {
++              MMU_WR(MMMU_BYPASS_START_OFFSET,
++                     MMMU_BYPASS_START_ENABLE + MMMU_BYPASS_START_INVERT +
++                     (APERTURE_BASE >> bypass_shift) - 1);
++              MMU_WR(MMMU_BYPASS_END_OFFSET,
++                     MMMU_BYPASS_END_ENABLE +
++                     (APERTURE_TOP >> bypass_shift));
++      } else {
++              MMU_WR(MMMU_BYPASS_START_OFFSET, 0);
++              MMU_WR(MMMU_BYPASS_END_OFFSET, 0);
++      }
++
++      /* Ensure tables are zeroed (which marks all pages as invalid) */
++      dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++      memset(mmu->tables, 0, TABLES_ALLOC_SIZE);
++      mmu->nmapped_pages = 0;
++
++      /* Initialize the high-level table to point to the low-level pages */
++      __sg_page_iter_start(&it.base, mmu->sgt->sgl, mmu->sgt->nents, 0);
++      for (i = 0; i < L2_PAGES; i++) {
++              if (!(i % (PAGE_SIZE / MMU_PAGE_SIZE))) {
++                      __sg_page_iter_dma_next(&it);
++                      u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++              } else {
++                      u++;
++              }
++              mmu->tables[TRANSLATED_PAGES + i] = MMMU_PTE_VALID + u;
++      }
++
++      /*
++       * Configure the addresses of the top-level table (offset because
++       * the aperture does not start from zero), and of the default page.
++       * For simplicity, both these regions are whole Linux pages.
++       */
++      __sg_page_iter_dma_next(&it);
++      u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++      MMU_WR(MMMU_PT_PA_BASE_OFFSET, u - (APERTURE_BASE >> L1_CHUNK_SHIFT));
++      __sg_page_iter_dma_next(&it);
++      u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++      MMU_WR(MMMU_ILLEGAL_ADR_OFFSET, MMMU_ILLEGAL_ADR_ENABLE + u);
++      dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++      mmu->dirty = false;
++
++      /* Flush (and enable) the shared TLB cache; enable this MMU. */
++      if (mmu->cache)
++              bcm2712_iommu_cache_flush(mmu->cache);
++      MMU_WR(MMMU_CTRL_OFFSET,
++             MMMU_CTRL_CAP_EXCEEDED_ABORT_EN    |
++             MMMU_CTRL_PT_INVALID_ABORT_EN      |
++             MMMU_CTRL_WRITE_VIOLATION_ABORT_EN |
++             MMMU_CTRL_STATS_ENABLE             |
++             MMMU_CTRL_ENABLE);
++}
++
++static int bcm2712_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
++{
++      struct bcm2712_iommu *mmu = dev ? dev_iommu_priv_get(dev) : 0;
++      struct bcm2712_iommu_domain *mydomain =
++              container_of(domain, struct bcm2712_iommu_domain, base);
++
++      dev_info(dev, "%s: MMU %s\n",
++               __func__, mmu ? dev_name(mmu->dev) : "");
++
++      if (mmu) {
++              mydomain->mmu = mmu;
++              mmu->domain = mydomain;
++
++              if (mmu->dma_iova_offset) {
++                      domain->geometry.aperture_start =
++                              mmu->dma_iova_offset + APERTURE_BASE;
++                      domain->geometry.aperture_end =
++                              mmu->dma_iova_offset + APERTURE_TOP - 1ul;
++              }
++
++              return 0;
++      }
++      return -EINVAL;
++}
++
++static void bcm2712_iommu_detach_dev(struct iommu_domain *domain, struct device *dev)
++{
++      (void)domain;
++      (void)dev;
++}
++
++static int bcm2712_iommu_map(struct iommu_domain *domain, unsigned long iova,
++                           phys_addr_t pa, size_t bytes, int prot, gfp_t gfp)
++{
++      struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++
++      (void)gfp;
++      iova -= mmu->dma_iova_offset;
++      if (iova >= APERTURE_BASE && iova + bytes <= APERTURE_TOP) {
++              unsigned int p;
++              u32 entry = MMMU_PTE_VALID | (pa >> MMU_PAGE_SHIFT);
++              u32 align = (u32)(iova | pa | bytes);
++
++              /* large page and write enable flags */
++              if (!(align & ((1 << HUGEPAGE_SHIFT) - 1)))
++                      entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 3);
++              else if (!(align & mmu->superpage_mask) && mmu->superpage_mask)
++                      entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 2);
++              else if (!(align &  mmu->bigpage_mask) && mmu->bigpage_mask)
++                      entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 1);
++              if (prot & IOMMU_WRITE)
++                      entry |= MMMU_PTE_WRITEABLE;
++
++              /* Ensure tables are cache-coherent with CPU */
++              if (!mmu->dirty) {
++                      dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++                      mmu->dirty = true;
++              }
++
++              iova -= APERTURE_BASE;
++              for (p = iova >> MMU_PAGE_SHIFT;
++                   p < (iova + bytes) >> MMU_PAGE_SHIFT; p++) {
++                      mmu->nmapped_pages += !(mmu->tables[p]);
++                      mmu->tables[p] = entry++;
++              }
++      } else if (iova + bytes > APERTURE_BASE || iova != pa) {
++              dev_warn(mmu->dev, "%s: iova=0x%lx pa=0x%llx size=0x%llx OUT OF RANGE!\n",
++                       __func__, iova,
++                       (unsigned long long)pa, (unsigned long long)bytes);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static size_t bcm2712_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
++                                size_t bytes, struct iommu_iotlb_gather *gather)
++{
++      struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++
++      if (iova >= mmu->dma_iova_offset + APERTURE_BASE &&
++          iova + bytes <= mmu->dma_iova_offset + APERTURE_TOP) {
++              unsigned int p;
++
++              /* Record just the lower and upper bounds in "gather" */
++              if (gather) {
++                      bool empty = (gather->end <= gather->start);
++
++                      if (empty || gather->start < iova)
++                              gather->start = iova;
++                      if (empty || gather->end < iova + bytes)
++                              gather->end = iova + bytes;
++              }
++
++              /* Ensure tables are cache-coherent with CPU */
++              if (!mmu->dirty) {
++                      dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++                      mmu->dirty = true;
++              }
++
++              /* Clear table entries, this marks the addresses as illegal */
++              iova -= (mmu->dma_iova_offset + APERTURE_BASE);
++              for (p = iova >> MMU_PAGE_SHIFT;
++                   p < (iova + bytes) >> MMU_PAGE_SHIFT;
++                   p++) {
++                      mmu->nmapped_pages -= !!(mmu->tables[p]);
++                      mmu->tables[p] = 0;
++              }
++      }
++
++      return bytes;
++}
++
++static void bcm2712_iommu_sync_range(struct iommu_domain *domain,
++                                   unsigned long iova, size_t size)
++{
++      struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++      unsigned long iova_end;
++      unsigned int i, p4;
++
++      if (!mmu || !mmu->dirty)
++              return;
++
++      /* Ensure tables are cleaned from CPU cache or write-buffer */
++      dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++      mmu->dirty = false;
++
++      /* Flush the shared TLB cache */
++      if (mmu->cache)
++              bcm2712_iommu_cache_flush(mmu->cache);
++
++      /*
++       * When flushing a large range or when nothing needs to be kept,
++       * it's quicker to use the"TLB_CLEAR" flag. Otherwise, invalidate
++       * TLB entries in lines of 4 words each. Each flush/clear operation
++       * should complete almost instantaneously.
++       */
++      iova -= mmu->dma_iova_offset;
++      iova_end = min(APERTURE_TOP, iova + size);
++      iova = max(APERTURE_BASE, iova);
++      if (mmu->nmapped_pages == 0 || iova_end - iova >= APERTURE_SIZE / 8) {
++              MMU_WR(MMMU_CTRL_OFFSET,
++                     MMMU_CTRL_CAP_EXCEEDED_ABORT_EN    |
++                     MMMU_CTRL_PT_INVALID_ABORT_EN      |
++                     MMMU_CTRL_WRITE_VIOLATION_ABORT_EN |
++                     MMMU_CTRL_TLB_CLEAR                |
++                     MMMU_CTRL_STATS_ENABLE             |
++                     MMMU_CTRL_ENABLE);
++              for (i = 0; i < 1024; i++) {
++                      if (!(MMMU_CTRL_TLB_CLEARING & MMU_RD(MMMU_CTRL_OFFSET)))
++                              break;
++                      cpu_relax();
++              }
++      } else {
++              for (p4 = iova >> (MMU_PAGE_SHIFT + 2);
++                   p4 < (iova_end + 3 * MMU_PAGE_SIZE) >> (MMU_PAGE_SHIFT + 2);
++                   p4++) {
++                      MMU_WR(MMMU_SHOOT_DOWN_OFFSET,
++                             MMMU_SHOOT_DOWN_SHOOT + (p4 << 2));
++                      for (i = 0; i < 1024; i++) {
++                              if (!(MMMU_SHOOT_DOWN_SHOOTING & MMU_RD(MMMU_SHOOT_DOWN_OFFSET)))
++                                      break;
++                              cpu_relax();
++                      }
++              }
++      }
++}
++
++static void bcm2712_iommu_sync(struct iommu_domain *domain,
++                             struct iommu_iotlb_gather *gather)
++{
++      bcm2712_iommu_sync_range(domain, gather->start,
++                               gather->end - gather->start);
++}
++
++static void bcm2712_iommu_sync_all(struct iommu_domain *domain)
++{
++      bcm2712_iommu_sync_range(domain, APERTURE_BASE, APERTURE_SIZE);
++}
++
++static phys_addr_t bcm2712_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
++{
++      struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++      u32 p;
++
++      iova -= mmu->dma_iova_offset;
++      if (iova  >= APERTURE_BASE && iova < APERTURE_TOP) {
++              p = (iova - APERTURE_BASE) >> MMU_PAGE_SHIFT;
++              p = mmu->tables[p] & 0x0FFFFFFFu;
++              return (((phys_addr_t)p) << MMU_PAGE_SHIFT) + (iova & (MMU_PAGE_SIZE - 1u));
++      } else if (iova < APERTURE_BASE) {
++              return (phys_addr_t)iova;
++      } else {
++              return (phys_addr_t)-EINVAL;
++      }
++}
++
++static void bcm2712_iommu_domain_free(struct iommu_domain *domain)
++{
++      struct bcm2712_iommu_domain *mydomain =
++              container_of(domain, struct bcm2712_iommu_domain, base);
++
++      kfree(mydomain);
++}
++
++static const struct iommu_domain_ops bcm2712_iommu_domain_ops = {
++      .attach_dev      = bcm2712_iommu_attach_dev,
++      .detach_dev      = bcm2712_iommu_detach_dev,
++      .map             = bcm2712_iommu_map,
++      .unmap           = bcm2712_iommu_unmap,
++      .iotlb_sync      = bcm2712_iommu_sync,
++      .iotlb_sync_map  = bcm2712_iommu_sync_range,
++      .flush_iotlb_all = bcm2712_iommu_sync_all,
++      .iova_to_phys    = bcm2712_iommu_iova_to_phys,
++      .free            = bcm2712_iommu_domain_free,
++};
++
++static struct iommu_domain *bcm2712_iommu_domain_alloc(unsigned int type)
++{
++      struct bcm2712_iommu_domain *domain;
++
++      if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
++              return NULL;
++
++      domain = kzalloc(sizeof(*domain), GFP_KERNEL);
++      if (!domain)
++              return NULL;
++
++      domain->base.type = type;
++      domain->base.ops  = &bcm2712_iommu_domain_ops;
++      domain->base.geometry.aperture_start = APERTURE_BASE;
++      domain->base.geometry.aperture_end   = APERTURE_TOP - 1ul;
++      domain->base.geometry.force_aperture = true;
++      return &domain->base;
++}
++
++static struct iommu_device *bcm2712_iommu_probe_device(struct device *dev)
++{
++      struct bcm2712_iommu *mmu;
++
++      /*
++       * For reasons I don't fully understand, we need to try both
++       * cases (dev_iommu_priv_get() and platform_get_drvdata())
++       * in order to get both GPU and ISP-BE to probe successfully.
++       */
++      mmu = dev_iommu_priv_get(dev);
++      if (!mmu) {
++              struct device_node *np;
++              struct platform_device *pdev;
++
++              /* Ignore devices that don't have an "iommus" property with exactly one phandle */
++              if (!dev->of_node ||
++                  of_property_count_elems_of_size(dev->of_node, "iommus", sizeof(phandle)) != 1)
++                      return ERR_PTR(-ENODEV);
++
++              np = of_parse_phandle(dev->of_node, "iommus", 0);
++              if (!np)
++                      return ERR_PTR(-EINVAL);
++
++              pdev = of_find_device_by_node(np);
++              of_node_put(np);
++              if (pdev)
++                      mmu = platform_get_drvdata(pdev);
++
++              if (!mmu)
++                      return ERR_PTR(-ENODEV);
++      }
++
++      dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++      dev_iommu_priv_set(dev, mmu);
++      return &mmu->iommu;
++}
++
++static void bcm2712_iommu_release_device(struct device *dev)
++{
++      dev_iommu_priv_set(dev, NULL);
++}
++
++static struct iommu_group *bcm2712_iommu_device_group(struct device *dev)
++{
++      struct bcm2712_iommu *mmu = dev_iommu_priv_get(dev);
++
++      if (!mmu || !mmu->group)
++              return ERR_PTR(-EINVAL);
++
++      dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++      return iommu_group_ref_get(mmu->group);
++}
++
++static int bcm2712_iommu_of_xlate(struct device *dev,
++                                struct of_phandle_args *args)
++{
++      struct platform_device *iommu_dev;
++      struct bcm2712_iommu *mmu;
++
++      iommu_dev = of_find_device_by_node(args->np);
++      mmu = platform_get_drvdata(iommu_dev);
++      dev_iommu_priv_set(dev, mmu);
++      dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++
++      return 0;
++}
++
++static bool bcm2712_iommu_capable(struct device *dev, enum iommu_cap cap)
++{
++      return false;
++}
++
++static const struct iommu_ops bcm2712_iommu_ops = {
++      .capable        = bcm2712_iommu_capable,
++      .domain_alloc   = bcm2712_iommu_domain_alloc,
++      .probe_device   = bcm2712_iommu_probe_device,
++      .release_device = bcm2712_iommu_release_device,
++      .device_group   = bcm2712_iommu_device_group,
++      /* Advertise native page sizes as well as 2M, 16K which Linux may prefer */
++      .pgsize_bitmap  = (SZ_4M | SZ_2M | SZ_1M | SZ_64K | SZ_16K | SZ_4K),
++      .default_domain_ops = &bcm2712_iommu_domain_ops,
++      .of_xlate = bcm2712_iommu_of_xlate,
++};
++
++static int bcm2712_iommu_probe(struct platform_device *pdev)
++{
++      struct bcm2712_iommu *mmu;
++      struct bcm2712_iommu_cache *cache = NULL;
++      int ret;
++
++      /* First of all, check for an IOMMU shared cache */
++      if (pdev->dev.of_node) {
++              struct device_node *cache_np;
++              struct platform_device *cache_pdev;
++
++              cache_np = of_parse_phandle(pdev->dev.of_node, "cache", 0);
++              if (cache_np) {
++                      cache_pdev = of_find_device_by_node(cache_np);
++                      of_node_put(cache_np);
++                      if (cache_pdev && !IS_ERR(cache_pdev))
++                              cache = platform_get_drvdata(cache_pdev);
++                      if (!cache)
++                              return -EPROBE_DEFER;
++              }
++      }
++
++      /* Allocate private data */
++      mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL);
++      if (!mmu)
++              return -ENOMEM;
++
++      mmu->name = dev_name(&pdev->dev);
++      mmu->dev = &pdev->dev;
++      mmu->cache = cache;
++      platform_set_drvdata(pdev, mmu);
++      spin_lock_init(&mmu->hw_lock);
++
++      /*
++       * XXX When an IOMMU is downstream of a PCIe RC or some other chip/bus
++       * and serves some of the masters thereon (others using pass-through),
++       * we seem to fumble and lose the "dma-ranges" address offset for
++       * masters using IOMMU. This property restores it, where needed.
++       */
++      if (!pdev->dev.of_node ||
++          of_property_read_u64(pdev->dev.of_node, "dma-iova-offset",
++                               &mmu->dma_iova_offset))
++              mmu->dma_iova_offset = 0;
++
++      /*
++       * The IOMMU is itself a device that allocates DMA-able memory
++       * to hold its translation tables. Provided the IOVA aperture
++       * is no larger than 4 GBytes (so that the L1 table fits within
++       * a single 4K page), we don't need the tables to be contiguous.
++       * Assume we can address at least 36 bits (64 GB).
++       */
++      ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
++      WARN_ON(ret);
++      mmu->sgt = dma_alloc_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++                                         DMA_TO_DEVICE, GFP_KERNEL,
++                                         DMA_ATTR_ALLOC_SINGLE_PAGES);
++      if (!mmu->sgt) {
++              ret = -ENOMEM;
++              goto done_err;
++      }
++      mmu->tables = dma_vmap_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++                                           mmu->sgt);
++      if (!mmu->tables) {
++              ret = -ENOMEM;
++              goto done_err;
++      }
++
++      /* Get IOMMU registers */
++      mmu->reg_base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(mmu->reg_base)) {
++              dev_err(&pdev->dev, "Failed to get IOMMU registers address\n");
++              ret = PTR_ERR(mmu->reg_base);
++              goto done_err;
++      }
++
++      /* Stuff */
++      mmu->group = iommu_group_alloc();
++      if (IS_ERR(mmu->group)) {
++              ret = PTR_ERR(mmu->group);
++              mmu->group = NULL;
++              goto done_err;
++      }
++      ret = iommu_device_sysfs_add(&mmu->iommu, mmu->dev, NULL, mmu->name);
++      if (ret)
++              goto done_err;
++
++      /* Initialize table and hardware */
++      bcm2712_iommu_init(mmu);
++      ret = iommu_device_register(&mmu->iommu, &bcm2712_iommu_ops, &pdev->dev);
++
++      dev_info(&pdev->dev, "%s: Success\n", __func__);
++      return 0;
++
++done_err:
++      dev_info(&pdev->dev, "%s: Failure %d\n", __func__, ret);
++      if (mmu->group)
++              iommu_group_put(mmu->group);
++      if (mmu->tables)
++              dma_vunmap_noncontiguous(&pdev->dev,
++                                       (void *)(mmu->tables));
++      mmu->tables = NULL;
++      if (mmu->sgt)
++              dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++                                     mmu->sgt, DMA_TO_DEVICE);
++      mmu->sgt = NULL;
++      kfree(mmu);
++      return ret;
++}
++
++static int bcm2712_iommu_remove(struct platform_device *pdev)
++{
++      struct bcm2712_iommu *mmu = platform_get_drvdata(pdev);
++
++      if (mmu->reg_base)
++              MMU_WR(MMMU_CTRL_OFFSET, 0); /* disable the MMU */
++      if (mmu->sgt)
++              dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++                                     mmu->sgt, DMA_TO_DEVICE);
++
++      return 0;
++}
++
++static const struct of_device_id bcm2712_iommu_of_match[] = {
++      {
++              . compatible = "brcm,bcm2712-iommu"
++      },
++      { /* sentinel */ },
++};
++
++static struct platform_driver bcm2712_iommu_driver = {
++      .probe = bcm2712_iommu_probe,
++      .remove = bcm2712_iommu_remove,
++      .driver = {
++              .name = "bcm2712-iommu",
++              .of_match_table = bcm2712_iommu_of_match
++      },
++};
++
++builtin_platform_driver(bcm2712_iommu_driver);
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu.h
+@@ -0,0 +1,45 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#ifndef _BCM2712_IOMMU_H
++#define _BCM2712_IOMMU_H
++
++#include <linux/iommu.h>
++#include <linux/scatterlist.h>
++
++struct bcm2712_iommu_cache {
++      struct device *dev;
++      spinlock_t hw_lock; /* to protect HW registers */
++      void __iomem *reg_base;
++};
++
++void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache);
++
++struct bcm2712_iommu {
++      struct device *dev;
++      struct iommu_device iommu;
++      struct iommu_group *group;
++      struct bcm2712_iommu_domain *domain;
++      char const *name;
++      struct sg_table *sgt; /* allocated memory for page tables */
++      u32 *tables;          /* kernel mapping for page tables */
++      struct bcm2712_iommu_cache *cache;
++      spinlock_t hw_lock;   /* to protect HW registers */
++      void __iomem *reg_base;
++      u64 dma_iova_offset; /* Hack for IOMMU attached to PCIe RC */
++      u32 bigpage_mask;
++      u32 superpage_mask;
++      unsigned int nmapped_pages;
++      bool dirty; /* true when tables are oriented towards CPU */
++};
++
++struct bcm2712_iommu_domain {
++      struct iommu_domain base;
++      struct bcm2712_iommu *mmu;
++};
++
++#endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch b/target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch
new file mode 100644 (file)
index 0000000..5ea212d
--- /dev/null
@@ -0,0 +1,73 @@
+From fa4d4ed28c92cf4470e518f1a7362dc7941632d7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 28 Jun 2023 16:24:29 +0100
+Subject: [PATCH] irqchip/irq-brcmstb-l2: Add config for 2711 controller
+
+We currently see these regularly:
+[   25.157560] irq 31, desc: 00000000c15e6d2c, depth: 0, count: 0, unhandled: 0
+[   25.164658] ->handle_irq():  00000000b1775675, brcmstb_l2_intc_irq_handle+0x0/0x1a8
+[   25.172352] ->irq_data.chip(): 00000000fea59f1c, gic_chip_mode1+0x0/0x108
+[   25.179166] ->action(): 000000003eda6d6f
+[   25.183096] ->action->handler(): 000000002c09e646, bad_chained_irq+0x0/0x58
+[   25.190084]      IRQ_LEVEL set
+[   25.193142]    IRQ_NOPROBE set
+[   25.196198]  IRQ_NOREQUEST set
+[   25.199255]   IRQ_NOTHREAD set
+
+with:
+$ cat /proc/interrupts  | grep 31:
+ 31:          1          0          0          0     GICv2 129 Level     (null)
+
+The interrupt is described in DT with IRQ_TYPE_LEVEL_HIGH
+
+But the current compatible string uses the controller in edge triggered mode
+(as that config matches our register layout).
+
+Add a new compatible structure for level driven interrupt with our register layout.
+
+We had already been using this compatible string in device tree, so no change needed
+there.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/irqchip/irq-brcmstb-l2.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/irqchip/irq-brcmstb-l2.c
++++ b/drivers/irqchip/irq-brcmstb-l2.c
+@@ -52,6 +52,16 @@ static const struct brcmstb_intc_init_pa
+       .cpu_mask_clear         = 0x0C
+ };
++/* Register offsets in the 2711 L2 level interrupt controller */
++static const struct brcmstb_intc_init_params l2_2711_lvl_intc_init = {
++      .handler                = handle_level_irq,
++      .cpu_status             = 0x00,
++      .cpu_clear              = 0x08,
++      .cpu_mask_status        = 0x0c,
++      .cpu_mask_set           = 0x10,
++      .cpu_mask_clear         = 0x14
++};
++
+ /* L2 intc private data structure */
+ struct brcmstb_l2_intc_data {
+       struct irq_domain *domain;
+@@ -286,11 +296,18 @@ static int __init brcmstb_l2_lvl_intc_of
+       return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
+ }
++static int __init brcmstb_l2_2711_lvl_intc_of_init(struct device_node *np,
++      struct device_node *parent)
++{
++      return brcmstb_l2_intc_of_init(np, parent, &l2_2711_lvl_intc_init);
++}
++
+ IRQCHIP_PLATFORM_DRIVER_BEGIN(brcmstb_l2)
+ IRQCHIP_MATCH("brcm,l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,hif-spi-l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,upg-aux-aon-l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,bcm7271-l2-intc", brcmstb_l2_lvl_intc_of_init)
++IRQCHIP_MATCH("brcm,bcm2711-l2-intc", brcmstb_l2_2711_lvl_intc_of_init)
+ IRQCHIP_PLATFORM_DRIVER_END(brcmstb_l2)
+ MODULE_DESCRIPTION("Broadcom STB generic L2 interrupt controller");
+ MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch b/target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch
new file mode 100644 (file)
index 0000000..e2b8c6c
--- /dev/null
@@ -0,0 +1,237 @@
+From 222dedcdc09247126d39364a614ff2019789f52a Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 7 Jul 2023 20:00:45 +0100
+Subject: [PATCH] rtc: rtc-rpi: Add simple RTC driver for Raspberry Pi
+
+This supports setting and reading the real time clock
+and supports wakeup alarms.
+
+To support wake up alarms you want this bootloader config:
+ POWER_OFF_ON_HALT=1
+ WAKE_ON_GPIO=0
+
+You can test with:
+  echo +600 | sudo tee /sys/class/rtc/rtc0/wakealarm
+  sudo halt
+
+That will halt (in an almost no power state),
+then wake and restart after 10 minutes.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/rtc/Kconfig   |  11 +++
+ drivers/rtc/Makefile  |   1 +
+ drivers/rtc/rtc-rpi.c | 177 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 189 insertions(+)
+ create mode 100644 drivers/rtc/rtc-rpi.c
+
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -223,6 +223,17 @@ config RTC_DRV_AC100
+         This driver can also be built as a module. If so, the module
+         will be called rtc-ac100.
++config RTC_DRV_RPI
++      tristate "Raspberry Pi RTC"
++      depends on ARCH_BRCMSTB || COMPILE_TEST
++      default ARCH_BRCMSTB
++      help
++        If you say yes here you get support for the RTC found on
++        Raspberry Pi devices.
++
++        This driver can also be built as a module. If so, the module
++        will be called rtc-rpi.
++
+ config RTC_DRV_BRCMSTB
+       tristate "Broadcom STB wake-timer"
+       depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+--- a/drivers/rtc/Makefile
++++ b/drivers/rtc/Makefile
+@@ -140,6 +140,7 @@ obj-$(CONFIG_RTC_DRV_RC5T583)      += rtc-rc5
+ obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
+ obj-$(CONFIG_RTC_DRV_RK808)   += rtc-rk808.o
+ obj-$(CONFIG_RTC_DRV_RP5C01)  += rtc-rp5c01.o
++obj-$(CONFIG_RTC_DRV_RPI)     += rtc-rpi.o
+ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
+ obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
+ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+--- /dev/null
++++ b/drivers/rtc/rtc-rpi.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/**
++ * rtc-rpi.c
++ *
++ * RTC driver using firmware mailbox
++ * Supports battery backed RTC and wake alarms
++ *
++ * Based on rtc-meson-vrtc by Neil Armstrong
++ *
++ * Copyright (c) 2023, Raspberry Pi Ltd.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/rtc.h>
++#include <linux/of.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++
++struct rpi_rtc_data {
++      struct rtc_device *rtc;
++      struct rpi_firmware *fw;
++};
++
++#define RPI_FIRMWARE_GET_RTC_REG 0x00030087
++#define RPI_FIRMWARE_SET_RTC_REG 0x00038087
++enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE};
++
++static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_TIME};
++      int err;
++
++      err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++                                  &data, sizeof(data));
++      rtc_time64_to_tm(data[1], tm);
++      return err;
++}
++
++static int rpi_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_TIME, rtc_tm_to_time64(tm)};
++
++      return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++                                   &data, sizeof(data));
++}
++
++static int rpi_rtc_alarm_irq_is_enabled(struct device *dev, unsigned char *enabled)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_ALARM_ENABLE};
++      s32 err = 0;
++
++      err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++                                  &data, sizeof(data));
++      *enabled = data[1] & 0x1;
++      return err;
++}
++
++static int rpi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_ALARM_ENABLE, enabled};
++
++      return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++                                   &data, sizeof(data));
++}
++
++static int rpi_rtc_alarm_clear_pending(struct device *dev)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_ALARM_PENDING, 1};
++
++      return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++                                   &data, sizeof(data));
++}
++
++static int rpi_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_ALARM};
++      s32 err = 0;
++
++      err = rpi_rtc_alarm_irq_is_enabled(dev, &alarm->enabled);
++      if (!err)
++              err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++                                          &data, sizeof(data));
++      rtc_time64_to_tm(data[1], &alarm->time);
++
++      return err;
++}
++
++static int rpi_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_ALARM, rtc_tm_to_time64(&alarm->time)};
++      int err;
++
++      err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++                                  &data, sizeof(data));
++
++      if (err == 0)
++              err = rpi_rtc_alarm_irq_enable(dev, alarm->enabled);
++
++      return err;
++}
++
++static const struct rtc_class_ops rpi_rtc_ops = {
++      .read_time = rpi_rtc_read_time,
++      .set_time = rpi_rtc_set_time,
++      .read_alarm = rpi_rtc_read_alarm,
++      .set_alarm = rpi_rtc_set_alarm,
++      .alarm_irq_enable = rpi_rtc_alarm_irq_enable,
++};
++
++static int rpi_rtc_probe(struct platform_device *pdev)
++{
++      struct rpi_rtc_data *vrtc;
++      struct device *dev = &pdev->dev;
++      struct device_node *np = dev->of_node;
++      struct device_node *fw_node;
++      struct rpi_firmware *fw;
++      int ret;
++
++      fw_node = of_parse_phandle(np, "firmware", 0);
++      if (!fw_node) {
++              dev_err(dev, "Missing firmware node\n");
++              return -ENOENT;
++      }
++
++      fw = rpi_firmware_get(fw_node);
++      if (!fw)
++              return -EPROBE_DEFER;
++
++      vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL);
++      if (!vrtc)
++              return -ENOMEM;
++
++      vrtc->fw = fw;
++
++      device_init_wakeup(&pdev->dev, 1);
++
++      platform_set_drvdata(pdev, vrtc);
++
++      vrtc->rtc = devm_rtc_allocate_device(&pdev->dev);
++      if (IS_ERR(vrtc->rtc))
++              return PTR_ERR(vrtc->rtc);
++
++      set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, vrtc->rtc->features);
++      clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features);
++
++      vrtc->rtc->ops = &rpi_rtc_ops;
++      ret = devm_rtc_register_device(vrtc->rtc);
++
++      rpi_rtc_alarm_clear_pending(dev);
++      return ret;
++}
++
++static const struct of_device_id rpi_rtc_dt_match[] = {
++      { .compatible = "raspberrypi,rpi-rtc"},
++      {},
++};
++MODULE_DEVICE_TABLE(of, rpi_rtc_dt_match);
++
++static struct platform_driver rpi_rtc_driver = {
++      .probe = rpi_rtc_probe,
++      .driver = {
++              .name = "rpi-rtc",
++              .of_match_table = rpi_rtc_dt_match,
++      },
++};
++
++module_platform_driver(rpi_rtc_driver);
++
++MODULE_DESCRIPTION("Raspberry Pi RTC driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch b/target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch
new file mode 100644 (file)
index 0000000..f4c630c
--- /dev/null
@@ -0,0 +1,35 @@
+From ff2c2f67689e10ad66c1e33ae6a7552d82ac983c Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 7 Jul 2023 20:16:06 +0100
+Subject: [PATCH] dt-bindings: rtc: new binding for Raspberry Pi RTC driver
+
+Add binding for the new RTC driver for Raspberry Pi.
+This platform has an RTC managed by firmware, and this RTC
+driver provides the simple mailbox interface to access it.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ .../devicetree/bindings/rtc/rtc-rpi.txt         | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+@@ -0,0 +1,17 @@
++* Raspberry Pi RTC
++
++This is a Linux interface to an RTC managed by firmware, hence it's
++virtual from a Linux perspective.
++
++The interface uses the firmware mailbox api to access the RTC registers.
++
++Required properties:
++compatible: should be "raspberrypi,rpi-rtc"
++firmware:   Reference to the RPi firmware device node.
++
++Example:
++
++      rpi_rtc: rpi_rtc {
++              compatible = "raspberrypi,rpi-rtc";
++              firmware = <&firmware>;
++      };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch b/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch
new file mode 100644 (file)
index 0000000..205806c
--- /dev/null
@@ -0,0 +1,164 @@
+From 96a8a4776cb142f5d2bb7f6379df9af40e727c0b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 11 Jul 2023 10:17:29 +0100
+Subject: [PATCH] hwmon: (pwm-fan) Add fan speed register support
+
+Some platforms include a fan-speed register that reports RPM directly
+as an alternative to counting interrupts from the fan tachometer input.
+Add support for reading a register at a given offset (rpm-offset) within
+a block declared in another node (rpm-regmap). This indirection allows
+the usual address mapping to be performed, and for address sharing with
+another driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/hwmon/pwm-fan.c | 59 ++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 52 insertions(+), 7 deletions(-)
+
+--- a/drivers/hwmon/pwm-fan.c
++++ b/drivers/hwmon/pwm-fan.c
+@@ -12,6 +12,7 @@
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/of.h>
++#include <linux/of_address.h>
+ #include <linux/platform_device.h>
+ #include <linux/pwm.h>
+ #include <linux/regulator/consumer.h>
+@@ -51,6 +52,9 @@ struct pwm_fan_ctx {
+       ktime_t sample_start;
+       struct timer_list rpm_timer;
++      void __iomem *rpm_regbase;
++      unsigned int rpm_offset;
++
+       unsigned int pwm_value;
+       unsigned int pwm_fan_state;
+       unsigned int pwm_fan_max_state;
+@@ -61,6 +65,10 @@ struct pwm_fan_ctx {
+       struct hwmon_channel_info fan_channel;
+ };
++static const u32 rpm_reg_channel_config[] = {
++      HWMON_F_INPUT, 0
++};
++
+ /* This handler assumes self resetting edge triggered interrupt. */
+ static irqreturn_t pulse_handler(int irq, void *dev_id)
+ {
+@@ -335,7 +343,10 @@ static int pwm_fan_read(struct device *d
+               }
+               return -EOPNOTSUPP;
+       case hwmon_fan:
+-              *val = ctx->tachs[channel].rpm;
++              if (ctx->rpm_regbase)
++                      *val = (long)readl(ctx->rpm_regbase + ctx->rpm_offset);
++              else
++                      *val = ctx->tachs[channel].rpm;
+               return 0;
+       default:
+@@ -470,6 +481,7 @@ static void pwm_fan_cleanup(void *__ctx)
+       /* Switch off everything */
+       ctx->enable_mode = pwm_disable_reg_disable;
+       pwm_fan_power_off(ctx);
++      iounmap(ctx->rpm_regbase);
+ }
+ static int pwm_fan_probe(struct platform_device *pdev)
+@@ -534,10 +546,23 @@ static int pwm_fan_probe(struct platform
+               return ret;
+       ctx->tach_count = platform_irq_count(pdev);
++      if (ctx->tach_count == 0) {
++              struct device_node *rpm_node;
++
++              rpm_node = of_parse_phandle(dev->of_node, "rpm-regmap", 0);
++              if (rpm_node)
++                      ctx->rpm_regbase = of_iomap(rpm_node, 0);
++      }
++
+       if (ctx->tach_count < 0)
+               return dev_err_probe(dev, ctx->tach_count,
+                                    "Could not get number of fan tachometer inputs\n");
+-      dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count);
++      if (IS_ERR(ctx->rpm_regbase))
++              return dev_err_probe(dev, PTR_ERR(ctx->rpm_regbase),
++                                   "Could not get rpm reg\n");
++
++      dev_dbg(dev, "%d fan tachometer inputs, %d rpm regmap\n", ctx->tach_count,
++              !!ctx->rpm_regbase);
+       if (ctx->tach_count) {
+               channel_count++;        /* We also have a FAN channel. */
+@@ -554,12 +579,24 @@ static int pwm_fan_probe(struct platform
+               if (!fan_channel_config)
+                       return -ENOMEM;
+               ctx->fan_channel.config = fan_channel_config;
++      } else if (ctx->rpm_regbase) {
++              channel_count++;        /* We also have a FAN channel. */
++              ctx->fan_channel.type = hwmon_fan;
++              ctx->fan_channel.config = rpm_reg_channel_config;
++
++              if (of_property_read_u32(pdev->dev.of_node, "rpm-offset", &ctx->rpm_offset)) {
++                      dev_err(&pdev->dev, "unable to read 'rpm-offset'");
++                      ret = -EINVAL;
++                      goto error;
++              }
+       }
+       channels = devm_kcalloc(dev, channel_count + 1,
+                               sizeof(struct hwmon_channel_info *), GFP_KERNEL);
+-      if (!channels)
+-              return -ENOMEM;
++      if (!channels) {
++              ret = -ENOMEM;
++              goto error;
++      }
+       channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE);
+@@ -602,6 +639,8 @@ static int pwm_fan_probe(struct platform
+               mod_timer(&ctx->rpm_timer, jiffies + HZ);
+               channels[1] = &ctx->fan_channel;
++      } else if (ctx->rpm_regbase) {
++              channels[1] = &ctx->fan_channel;
+       }
+       ctx->info.ops = &pwm_fan_hwmon_ops;
+@@ -611,12 +650,13 @@ static int pwm_fan_probe(struct platform
+                                                    ctx, &ctx->info, NULL);
+       if (IS_ERR(hwmon)) {
+               dev_err(dev, "Failed to register hwmon device\n");
+-              return PTR_ERR(hwmon);
++              ret = PTR_ERR(hwmon);
++              goto error;
+       }
+       ret = pwm_fan_of_get_cooling_data(dev, ctx);
+       if (ret)
+-              return ret;
++              goto error;
+       ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+       if (IS_ENABLED(CONFIG_THERMAL)) {
+@@ -627,12 +667,17 @@ static int pwm_fan_probe(struct platform
+                       dev_err(dev,
+                               "Failed to register pwm-fan as cooling device: %d\n",
+                               ret);
+-                      return ret;
++                      goto error;
+               }
+               ctx->cdev = cdev;
+       }
+       return 0;
++
++error:
++      if (ctx->rpm_regbase)
++              iounmap(ctx->rpm_regbase);
++      return ret;
+ }
+ static void pwm_fan_shutdown(struct platform_device *pdev)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch b/target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch
new file mode 100644 (file)
index 0000000..2972c57
--- /dev/null
@@ -0,0 +1,63 @@
+From 07419175fdb507be2c9d3aaf4b7d18306a336348 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 11:38:03 +0100
+Subject: [PATCH] dt-bindings: input: Add bindings for raspberrypi-button
+
+Add bindings for the firmware-based button driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../input/raspberrypi,firmware-button.yaml    | 47 +++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/input/raspberrypi,firmware-button.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/input/raspberrypi,firmware-button.yaml
+@@ -0,0 +1,47 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/input/raspberrypi,firmware-button.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi firmware buttons
++
++maintainers:
++  - Phil Elwell <phil@raspberrypi.com>
++
++description: >
++  The Raspberry Pi 5 firmware exposes the state of the power button. The
++  raspberrypi-button driver generates a keycode when it is pressed.
++
++properties:
++  compatible:
++    enum:
++      - raspberrypi,firmware-button
++
++  id:
++    description: A numeric identifier of the button
++
++  label:
++    description: Descriptive name of the button.
++
++  linux,code:
++    description: Key code to emit.
++
++required:
++  - compatible
++  - linux,code
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/input/raspberrypi-button.h>
++
++    pwr_button: pwr_button {
++        compatible = "raspberrypi,firmware-button";
++        id = <RASPBERRYPI_BUTTON_POWER>;
++        label = "pwr_button";
++        linux,code = <116>; // KEY_POWER
++    };
++
++...
diff --git a/target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch b/target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch
new file mode 100644 (file)
index 0000000..e8fea23
--- /dev/null
@@ -0,0 +1,27 @@
+From 93c8947bc7813b49fe27a5251eef97c6df1e14c6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 15:01:29 +0100
+Subject: [PATCH] dt-bindings: input: Add bindings for raspberrypi-button
+
+Add bindings for the firmware-based button driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ include/dt-bindings/input/raspberrypi-button.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+ create mode 100644 include/dt-bindings/input/raspberrypi-button.h
+
+--- /dev/null
++++ b/include/dt-bindings/input/raspberrypi-button.h
+@@ -0,0 +1,11 @@
++/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
++/*
++ * This header provides constants the raspberrypi-button driver.
++ */
++
++#ifndef _DT_BINDINGS_RASPBERRYPI_BUTTON_H
++#define _DT_BINDINGS_RASPBERRYPI_BUTTON_H
++
++#define RASPBERRYPI_BUTTON_POWER 0
++
++#endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch
new file mode 100644 (file)
index 0000000..76a9261
--- /dev/null
@@ -0,0 +1,197 @@
+From 7c0d40384b0648030d5202114d90239b8db7d4e0 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 11:30:48 +0100
+Subject: [PATCH] Input: Add raspberrypi-button firmware driver
+
+Raspberry Pi 5s have a power/suspend button that is only accessible to
+the firmware. Add a driver to read it and generate key events.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/input/misc/Kconfig                 |  10 ++
+ drivers/input/misc/Makefile                |   1 +
+ drivers/input/misc/raspberrypi-button.c    | 138 +++++++++++++++++++++
+ include/soc/bcm2835/raspberrypi-firmware.h |   1 +
+ 4 files changed, 150 insertions(+)
+ create mode 100644 drivers/input/misc/raspberrypi-button.c
+
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -918,6 +918,16 @@ config INPUT_RT5120_PWRKEY
+         To compile this driver as a module, choose M here. the module will
+         be called rt5120-pwrkey.
++config INPUT_RASPBERRYPI_BUTTON
++      tristate "Raspberry Pi button support"
++      depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
++      help
++        This enables support for firmware-controlled buttons on Raspberry
++        Pi devices.
++
++        To compile this driver as a module, choose M here. the module will
++        be called raspberrypi-button.
++
+ config INPUT_STPMIC1_ONKEY
+       tristate "STPMIC1 PMIC Onkey support"
+       depends on MFD_STPMIC1
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -70,6 +70,7 @@ obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON)        +=
+ obj-$(CONFIG_INPUT_RB532_BUTTON)      += rb532_button.o
+ obj-$(CONFIG_INPUT_REGULATOR_HAPTIC)  += regulator-haptic.o
+ obj-$(CONFIG_INPUT_RETU_PWRBUTTON)    += retu-pwrbutton.o
++obj-$(CONFIG_INPUT_RASPBERRYPI_BUTTON)        += raspberrypi-button.o
+ obj-$(CONFIG_INPUT_RT5120_PWRKEY)     += rt5120-pwrkey.o
+ obj-$(CONFIG_INPUT_AXP20X_PEK)                += axp20x-pek.o
+ obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)       += rotary_encoder.o
+--- /dev/null
++++ b/drivers/input/misc/raspberrypi-button.c
+@@ -0,0 +1,138 @@
++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
++/*
++ * Driver for Raspberry Pi power button
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * This driver is based on drivers/hwmon/raspberrypi-hwmon.c and
++ * input/misc/pm8941-pwrkey.c/ - see original files for copyright information
++ */
++
++#include <linux/delay.h>
++#include <linux/devm-helpers.h>
++#include <dt-bindings/input/raspberrypi-button.h>
++#include <linux/errno.h>
++#include <linux/input.h>
++#include <linux/kernel.h>
++#include <linux/ktime.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/workqueue.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++
++struct rpi_button {
++      struct device *dev;
++      struct rpi_firmware *fw;
++      struct input_dev *input;
++      struct delayed_work poll_work;
++      unsigned long poll_rate;
++      const char *name;
++      u32 id;
++      u32 code;
++};
++
++static void button_poll(struct work_struct *work)
++{
++      struct rpi_button *button;
++      u32 value;
++      int err;
++
++      button = container_of(work, struct rpi_button,
++                            poll_work.work);
++
++      value = BIT(button->id);
++      err = rpi_firmware_property(button->fw, RPI_FIRMWARE_GET_BUTTONS_PRESSED,
++                                  &value, sizeof(value));
++      if (err) {
++              dev_err_once(button->dev, "GET_BUTTON_PRESSED not implemented?\n");
++              return;
++      }
++
++      if (value & BIT(button->id)) {
++              input_event(button->input, EV_KEY, button->code, 1);
++              input_sync(button->input);
++              input_event(button->input, EV_KEY, button->code, 0);
++              input_sync(button->input);
++      }
++
++      schedule_delayed_work(&button->poll_work, button->poll_rate);
++}
++
++static int rpi_button_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct rpi_button *button;
++      int err;
++
++      button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL);
++      if (!button)
++              return -ENOMEM;
++
++      button->dev = dev;
++
++      /* Get the firmware pointer from our parent */
++      button->fw = dev_get_drvdata(dev->parent);
++
++      if (device_property_read_u32(dev, "id", &button->id))
++              button->id = RASPBERRYPI_BUTTON_POWER;
++
++      if (device_property_read_string(dev, "label", &button->name))
++              button->name = "raspberrypi-button";
++
++      if (device_property_read_u32(dev, "linux,code", &button->code)) {
++              dev_err(&pdev->dev, "no linux,code property\n");
++              return -EINVAL;
++      }
++
++      button->input = devm_input_allocate_device(dev);
++      if (!button->input) {
++              dev_dbg(&pdev->dev, "unable to allocate input device\n");
++              return -ENOMEM;
++      }
++
++      input_set_capability(button->input, EV_KEY, button->code);
++
++      button->input->name = button->name;
++      button->input->phys = "raspberrypi-button/input0";
++      button->input->dev.parent = dev;
++      button->poll_rate = HZ;
++
++      err = input_register_device(button->input);
++      if (err) {
++              dev_err(&pdev->dev, "failed to register input device: %d\n",
++                      err);
++              return err;
++      }
++
++      err = devm_delayed_work_autocancel(dev, &button->poll_work,
++                                         button_poll);
++      if (err)
++              return err;
++
++      platform_set_drvdata(pdev, button);
++      schedule_delayed_work(&button->poll_work, button->poll_rate);
++
++      return 0;
++}
++
++static const struct of_device_id rpi_button_match[] = {
++      { .compatible = "raspberrypi,firmware-button", },
++      { }
++};
++MODULE_DEVICE_TABLE(of, rpi_button_match);
++
++static struct platform_driver rpi_button_driver = {
++      .probe = rpi_button_probe,
++      .driver = {
++              .name = "raspberrypi-button",
++              .of_match_table = of_match_ptr(rpi_button_match),
++      },
++};
++module_platform_driver(rpi_button_driver);
++
++MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
++MODULE_DESCRIPTION("Raspberry Pi button driver");
++MODULE_LICENSE("Dual BSD/GPL");
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -98,6 +98,7 @@ enum rpi_firmware_property_tag {
+       RPI_FIRMWARE_GET_REBOOT_FLAGS =                       0x00030064,
+       RPI_FIRMWARE_SET_REBOOT_FLAGS =                       0x00038064,
+       RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066,
++      RPI_FIRMWARE_GET_BUTTONS_PRESSED =                    0x00030088,
+       /* Dispmanx TAGS */
+       RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch b/target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch
new file mode 100644 (file)
index 0000000..ffc5cc1
--- /dev/null
@@ -0,0 +1,29 @@
+From a7a3679a148e40879f1ce77580d9edf64cb5b51c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 18 Sep 2023 16:33:06 +0100
+Subject: [PATCH] dt: bindings: update rpi-rtc binding
+
+Add property for bcm2712 firmware RTC driver charger control
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/rtc/rtc-rpi.txt | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
++++ b/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+@@ -9,9 +9,14 @@ Required properties:
+ compatible: should be "raspberrypi,rpi-rtc"
+ firmware:   Reference to the RPi firmware device node.
++Optional property:
++trickle-charge-microvolt: specify a trickle charge voltage for the backup
++                          battery in microvolts.
++
+ Example:
+       rpi_rtc: rpi_rtc {
+               compatible = "raspberrypi,rpi-rtc";
+               firmware = <&firmware>;
++              trickle-charge-microvolt = <3000000>;
+       };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch b/target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch
new file mode 100644 (file)
index 0000000..dae0a1c
--- /dev/null
@@ -0,0 +1,153 @@
+From 33b514cb16dbf13395a0becf7442d19676ae4224 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 15 Sep 2023 17:33:03 +0100
+Subject: [PATCH] drivers: rtc-rpi: add battery charge circuit control and
+ readback
+
+Parse devicetree for a charger voltage and apply it. If nonzero and a
+valid voltage, the firmware will enable charging, otherwise the charger
+circuit is disabled.
+
+Add sysfs attributes to read back the supported charge voltage range,
+the measured battery voltage, and the charger setpoint.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/rtc/rtc-rpi.c | 106 ++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 103 insertions(+), 3 deletions(-)
+
+--- a/drivers/rtc/rtc-rpi.c
++++ b/drivers/rtc/rtc-rpi.c
+@@ -19,11 +19,22 @@
+ struct rpi_rtc_data {
+       struct rtc_device *rtc;
+       struct rpi_firmware *fw;
++      u32 bbat_vchg_microvolts;
+ };
+ #define RPI_FIRMWARE_GET_RTC_REG 0x00030087
+ #define RPI_FIRMWARE_SET_RTC_REG 0x00038087
+-enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE};
++
++enum {
++      RTC_TIME,
++      RTC_ALARM,
++      RTC_ALARM_PENDING,
++      RTC_ALARM_ENABLE,
++      RTC_BBAT_CHG_VOLTS,
++      RTC_BBAT_CHG_VOLTS_MIN,
++      RTC_BBAT_CHG_VOLTS_MAX,
++      RTC_BBAT_VOLTS
++};
+ static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
+ {
+@@ -114,6 +125,83 @@ static const struct rtc_class_ops rpi_rt
+       .alarm_irq_enable = rpi_rtc_alarm_irq_enable,
+ };
++static int rpi_rtc_set_charge_voltage(struct device *dev)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++      u32 data[2] = {RTC_BBAT_CHG_VOLTS, vrtc->bbat_vchg_microvolts};
++      int err;
++
++      err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++                                  &data, sizeof(data));
++
++      if (err)
++              dev_err(dev, "failed to set trickle charge voltage to %uuV: %d\n",
++                      vrtc->bbat_vchg_microvolts, err);
++      else if (vrtc->bbat_vchg_microvolts)
++              dev_info(dev, "trickle charging enabled at %uuV\n",
++                       vrtc->bbat_vchg_microvolts);
++
++      return err;
++}
++
++static ssize_t rpi_rtc_print_uint_reg(struct device *dev, char *buf, u32 reg)
++{
++      struct rpi_rtc_data *vrtc = dev_get_drvdata(dev->parent);
++      u32 data[2] = {reg, 0};
++      int ret = 0;
++
++      ret = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++                                  &data, sizeof(data));
++      if (ret < 0)
++              return ret;
++
++      return sprintf(buf, "%u\n", data[1]);
++}
++
++static ssize_t charging_voltage_show(struct device *dev,
++                                   struct device_attribute *attr,
++                                   char *buf)
++{
++      return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS);
++}
++static DEVICE_ATTR_RO(charging_voltage);
++
++static ssize_t charging_voltage_min_show(struct device *dev,
++                                       struct device_attribute *attr,
++                                       char *buf)
++{
++      return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MIN);
++}
++static DEVICE_ATTR_RO(charging_voltage_min);
++
++static ssize_t charging_voltage_max_show(struct device *dev,
++                                       struct device_attribute *attr,
++                                       char *buf)
++{
++      return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MAX);
++}
++static DEVICE_ATTR_RO(charging_voltage_max);
++
++static ssize_t battery_voltage_show(struct device *dev,
++                                  struct device_attribute *attr,
++                                  char *buf)
++{
++      return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_VOLTS);
++}
++static DEVICE_ATTR_RO(battery_voltage);
++
++static struct attribute *rpi_rtc_attrs[] = {
++      &dev_attr_charging_voltage.attr,
++      &dev_attr_charging_voltage_min.attr,
++      &dev_attr_charging_voltage_max.attr,
++      &dev_attr_battery_voltage.attr,
++      NULL
++};
++
++static const struct attribute_group rpi_rtc_sysfs_files = {
++      .attrs = rpi_rtc_attrs,
++};
++
+ static int rpi_rtc_probe(struct platform_device *pdev)
+ {
+       struct rpi_rtc_data *vrtc;
+@@ -151,10 +239,22 @@ static int rpi_rtc_probe(struct platform
+       clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features);
+       vrtc->rtc->ops = &rpi_rtc_ops;
+-      ret = devm_rtc_register_device(vrtc->rtc);
++      ret = rtc_add_group(vrtc->rtc, &rpi_rtc_sysfs_files);
++      if (ret)
++              return ret;
+       rpi_rtc_alarm_clear_pending(dev);
+-      return ret;
++
++      /*
++       * Optionally enable trickle charging - if the property isn't
++       * present (or set to zero), trickle charging is disabled.
++       */
++      of_property_read_u32(np, "trickle-charge-microvolt",
++                           &vrtc->bbat_vchg_microvolts);
++
++      rpi_rtc_set_charge_voltage(dev);
++
++      return devm_rtc_register_device(vrtc->rtc);
+ }
+ static const struct of_device_id rpi_rtc_dt_match[] = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch b/target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch
new file mode 100644 (file)
index 0000000..8aae09c
--- /dev/null
@@ -0,0 +1,29 @@
+From 0f5fd4538774aa6c936bb8fc78611c3113bf19d7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 17 Apr 2023 15:21:41 +0100
+Subject: [PATCH] vc4_drv: Avoid panic when booted with no kms
+
+If kms/fkms overlay is not present we have no matching drivers
+and so match is NULL.
+
+It is not safe to call component_master_add_with_match with a null match argument.
+
+So don't do that
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -456,6 +456,9 @@ static int vc4_platform_drm_probe(struct
+       vc4_match_add_drivers(dev, &match,
+                             component_drivers, ARRAY_SIZE(component_drivers));
++      if (!match)
++              return -ENODEV;
++
+       return component_master_add_with_match(dev, &vc4_drm_ops, match);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch b/target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch
new file mode 100644 (file)
index 0000000..70a11df
--- /dev/null
@@ -0,0 +1,28 @@
+From 2c987545a88507acdd8a572a3bd23a4ca0124d14 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 13 Apr 2023 17:41:11 +0100
+Subject: [PATCH] drm/vc4: Treat zero sized destination as full screen
+
+Kodi video planes come through with all zeros for fullscreen
+Without this check, we WARN when writing width-1, height-1
+to destination dlist
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -484,6 +484,11 @@ static int vc4_plane_setup_clipping_and_
+       vc4_state->crtc_w = state->dst.x2 - state->dst.x1;
+       vc4_state->crtc_h = state->dst.y2 - state->dst.y1;
++      if (!vc4_state->crtc_w)
++              vc4_state->crtc_w = state->crtc->mode.hdisplay;
++      if (!vc4_state->crtc_h)
++              vc4_state->crtc_h = state->crtc->mode.vdisplay;
++
+       ret = vc4_plane_margins_adj(state);
+       if (ret)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch b/target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch
new file mode 100644 (file)
index 0000000..9182194
--- /dev/null
@@ -0,0 +1,50 @@
+From bb1ee75de382c7a5218750476aa2a5792309cc70 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 30 Mar 2023 17:18:36 +0100
+Subject: [PATCH] drm/vc4: Fix FKMS for when the YUV chroma planes are
+ different buffers
+
+The code was assuming that it was a single buffer with offsets,
+when kmstest uses separate buffers and 0 offsets for each plane.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -528,7 +528,7 @@ static int vc4_plane_to_mb(struct drm_pl
+                          struct drm_plane_state *state)
+ {
+       struct drm_framebuffer *fb = state->fb;
+-      struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
++      struct drm_gem_dma_object *bo;
+       const struct drm_format_info *drm_fmt = fb->format;
+       const struct vc_image_format *vc_fmt =
+                                       vc4_get_vc_image_fmt(drm_fmt->format);
+@@ -552,6 +552,7 @@ static int vc4_plane_to_mb(struct drm_pl
+                                       state->normalized_zpos : -127;
+       mb->plane.num_planes = num_planes;
+       mb->plane.is_vu = vc_fmt->is_vu;
++      bo = drm_fb_dma_get_gem_obj(fb, 0);
+       mb->plane.planes[0] = bo->dma_addr + fb->offsets[0];
+       rotation = drm_rotation_simplify(state->rotation,
+@@ -572,11 +573,14 @@ static int vc4_plane_to_mb(struct drm_pl
+               /* Makes assumptions on the stride for the chroma planes as we
+                * can't easily plumb in non-standard pitches.
+                */
++              bo = drm_fb_dma_get_gem_obj(fb, 1);
+               mb->plane.planes[1] = bo->dma_addr + fb->offsets[1];
+-              if (num_planes > 2)
++              if (num_planes > 2) {
++                      bo = drm_fb_dma_get_gem_obj(fb, 2);
+                       mb->plane.planes[2] = bo->dma_addr + fb->offsets[2];
+-              else
++              } else {
+                       mb->plane.planes[2] = 0;
++              }
+               /* Special case the YUV420 with U and V as line interleaved
+                * planes as we have special handling for that case.
diff --git a/target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch
new file mode 100644 (file)
index 0000000..f228dca
--- /dev/null
@@ -0,0 +1,39 @@
+From 0da58dfbd2cc2cfa14a629787b9ba6fa10b5f666 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 29 Mar 2023 15:26:52 +0100
+Subject: [PATCH] drm/vc4: hdmi: Enable the audio clock
+
+The audio clock is used by the HDMI controller driver and we were using
+it to get its audio rate and compute the dividers needed to reach a
+given audio sample rate.
+
+However, we were never enabling it, which was resulting in lockups on
+the BCM2712.
+
+Fixes: 632ee3aa8786 ("drm/vc4: hdmi: Add audio-related callbacks")
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -3625,6 +3625,7 @@ static int vc4_hdmi_runtime_suspend(stru
+ {
+       struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
++      clk_disable_unprepare(vc4_hdmi->audio_clock);
+       clk_disable_unprepare(vc4_hdmi->hsm_rpm_clock);
+       return 0;
+@@ -3666,6 +3667,10 @@ static int vc4_hdmi_runtime_resume(struc
+               goto err_disable_clk;
+       }
++      ret = clk_prepare_enable(vc4_hdmi->audio_clock);
++      if (ret)
++              goto err_disable_clk;
++
+       if (vc4_hdmi->variant->reset)
+               vc4_hdmi->variant->reset(vc4_hdmi);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch b/target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch
new file mode 100644 (file)
index 0000000..fa6a399
--- /dev/null
@@ -0,0 +1,31 @@
+From cdbebb3a92aca7327c88c6dc6ef5d4cd470d49fc Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 23 Feb 2023 19:44:32 +0100
+Subject: [PATCH] drm/vc4: hdmi: Warn if writing to an unknown HDMI register
+
+The VC4 HDMI driver has a bunch of accessors to read from a register.
+The read accessor was warning when accessing an unknown register, but
+the write one was just returning silently.
+
+Let's make sure we warn also when writing to an unknown register.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -498,8 +498,11 @@ static inline void vc4_hdmi_write(struct
+       field = &variant->registers[reg];
+       base = __vc4_hdmi_get_field_base(hdmi, field->reg);
+-      if (!base)
++      if (!base) {
++              dev_warn(&hdmi->pdev->dev,
++                       "Unknown register ID %u\n", reg);
+               return;
++      }
+       writel(value, base + field->offset);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch b/target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch
new file mode 100644 (file)
index 0000000..dee2fc0
--- /dev/null
@@ -0,0 +1,41 @@
+From 4ebd8283403daf5507e5aafb42fe3e4c7612eb14 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 09:51:51 +0100
+Subject: [PATCH] drm/vc4: hvs: More logging for dlist generation
+
+DLIST generation can get pretty tricky and there's not a lot of debug in
+the driver to help. Let's add a few more to track the generated DLIST
+size.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -826,11 +826,22 @@ int vc4_hvs_atomic_check(struct drm_crtc
+       if (hweight32(crtc_state->connector_mask) > 1)
+               return -EINVAL;
+-      drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state)
+-              dlist_count += vc4_plane_dlist_size(plane_state);
++      drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) {
++              u32 plane_dlist_count = vc4_plane_dlist_size(plane_state);
++
++              drm_dbg_driver(dev, "[CRTC:%d:%s] Found [PLANE:%d:%s] with DLIST size: %u\n",
++                             crtc->base.id, crtc->name,
++                             plane->base.id, plane->name,
++                             plane_dlist_count);
++
++              dlist_count += plane_dlist_count;
++      }
+       dlist_count++; /* Account for SCALER_CTL0_END. */
++      drm_dbg_driver(dev, "[CRTC:%d:%s] Allocating DLIST block with size: %u\n",
++                     crtc->base.id, crtc->name, dlist_count);
++
+       alloc = vc4_hvs_alloc_dlist_entry(vc4->hvs, vc4_state->assigned_channel, dlist_count);
+       if (IS_ERR(alloc))
+               return PTR_ERR(alloc);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch b/target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch
new file mode 100644 (file)
index 0000000..7c7bbb4
--- /dev/null
@@ -0,0 +1,66 @@
+From c0af63193bd307f281211e7fb32a02a52c2869b2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 09:53:17 +0100
+Subject: [PATCH] drm/vc4: hvs: Print error if we fail an allocation
+
+We need to allocate a few additional structures when checking our
+atomic_state, especially related to hardware SRAM that will hold the
+plane descriptors (DLIST) and the current line context (LBM) during
+composition.
+
+Since those allocation can fail, let's add some error message in that
+case to help debug what goes wrong.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c   | 6 +++++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 7 +++++--
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -441,6 +441,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+                         unsigned int channel,
+                         size_t dlist_count)
+ {
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *dev = &vc4->base;
+       struct vc4_hvs_dlist_allocation *alloc;
+       unsigned long flags;
+       int ret;
+@@ -458,8 +460,10 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+       ret = drm_mm_insert_node(&hvs->dlist_mm, &alloc->mm_node,
+                                dlist_count);
+       spin_unlock_irqrestore(&hvs->mm_lock, flags);
+-      if (ret)
++      if (ret) {
++              drm_err(dev, "Failed to allocate DLIST entry: %d\n", ret);
+               return ERR_PTR(ret);
++      }
+       alloc->channel = channel;
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -733,7 +733,8 @@ static void vc4_plane_calc_load(struct d
+ static int vc4_plane_allocate_lbm(struct drm_plane_state *state)
+ {
+-      struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
++      struct drm_device *drm = state->plane->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+       unsigned long irqflags;
+       u32 lbm_size;
+@@ -759,8 +760,10 @@ static int vc4_plane_allocate_lbm(struct
+                                                0, 0);
+               spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+-              if (ret)
++              if (ret) {
++                      drm_err(drm, "Failed to allocate LBM entry: %d\n", ret);
+                       return ret;
++              }
+       } else {
+               WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch b/target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch
new file mode 100644 (file)
index 0000000..d8d68e7
--- /dev/null
@@ -0,0 +1,36 @@
+From 164f7e94da446984f275be1c082b93243beadfba Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 16:17:57 +0100
+Subject: [PATCH] drm/vc4: plane: Add more debugging for LBM allocation
+
+LBM allocations need a different size depending on the line length,
+format, etc.
+
+This can get tricky, and fail. Let's add some more prints to ease the
+debugging when it does.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -735,6 +735,7 @@ static int vc4_plane_allocate_lbm(struct
+ {
+       struct drm_device *drm = state->plane->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
++      struct drm_plane *plane = state->plane;
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+       unsigned long irqflags;
+       u32 lbm_size;
+@@ -743,6 +744,9 @@ static int vc4_plane_allocate_lbm(struct
+       if (!lbm_size)
+               return 0;
++      drm_dbg_driver(drm, "[PLANE:%d:%s] LBM Allocation Size: %u\n",
++                     plane->base.id, plane->name, lbm_size);
++
+       if (WARN_ON(!vc4_state->lbm_offset))
+               return -EINVAL;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch b/target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch
new file mode 100644 (file)
index 0000000..220245c
--- /dev/null
@@ -0,0 +1,31 @@
+From 950394a39f659746e5933cbc203a8bedef8246b7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 14:26:44 +0100
+Subject: [PATCH] drm/vc4: plane: Use return variable in atomic_check
+
+The vc4_plane_atomic_check() directly returns the result of the final
+function it calls.
+
+Using the already defined ret variable to check its content on error,
+and a separate return 0 on success, makes it easier to extend.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1378,7 +1378,11 @@ static int vc4_plane_atomic_check(struct
+       if (ret)
+               return ret;
+-      return vc4_plane_allocate_lbm(new_plane_state);
++      ret = vc4_plane_allocate_lbm(new_plane_state);
++      if (ret)
++              return ret;
++
++      return 0;
+ }
+ static void vc4_plane_atomic_update(struct drm_plane *plane,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch b/target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch
new file mode 100644 (file)
index 0000000..3476f54
--- /dev/null
@@ -0,0 +1,47 @@
+From f3c6acc345113c57011f2b1c8421e6cf78f0bc30 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:39:13 +0100
+Subject: [PATCH] drm/vc4: crtc: Move assigned_channel to a variable
+
+We access multiple times the vc4_crtc_state->assigned_channel variable
+in the vc4_crtc_get_scanout_position() function, so let's store it in a
+local variable.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -104,6 +104,7 @@ static bool vc4_crtc_get_scanout_positio
+       struct vc4_hvs *hvs = vc4->hvs;
+       struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+       struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
++      unsigned int channel = vc4_crtc_state->assigned_channel;
+       unsigned int cob_size;
+       u32 val;
+       int fifo_lines;
+@@ -120,7 +121,7 @@ static bool vc4_crtc_get_scanout_positio
+        * Read vertical scanline which is currently composed for our
+        * pixelvalve by the HVS, and also the scaler status.
+        */
+-      val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel));
++      val = HVS_READ(SCALER_DISPSTATX(channel));
+       /* Get optional system timestamp after query. */
+       if (etime)
+@@ -136,11 +137,11 @@ static bool vc4_crtc_get_scanout_positio
+               *vpos /= 2;
+               /* Use hpos to correct for field offset in interlaced mode. */
+-              if (vc4_hvs_get_fifo_frame_count(hvs, vc4_crtc_state->assigned_channel) % 2)
++              if (vc4_hvs_get_fifo_frame_count(hvs, channel) % 2)
+                       *hpos += mode->crtc_htotal / 2;
+       }
+-      cob_size = vc4_crtc_get_cob_allocation(vc4, vc4_crtc_state->assigned_channel);
++      cob_size = vc4_crtc_get_cob_allocation(vc4, channel);
+       /* This is the offset we need for translating hvs -> pv scanout pos. */
+       fifo_lines = cob_size / mode->crtc_hdisplay;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch b/target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch
new file mode 100644 (file)
index 0000000..9314c79
--- /dev/null
@@ -0,0 +1,1029 @@
+From fa2571d625bb53b642cd9f29a7cfc3434e1cf576 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:07:36 +0100
+Subject: [PATCH] drm/vc4: Introduce generation number enum
+
+With the introduction of the BCM2712 support, we will get yet another
+generation of display engine to support.
+
+The binary check of whether it's VC5 or not thus doesn't work anymore,
+especially since some parts of the driver will have changed with BCM2711,
+and some others with BCM2712.
+
+Let's introduce an enum to store the generation the driver is running
+on, which should provide more flexibility.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c       | 12 +++---
+ drivers/gpu/drm/vc4/vc4_bo.c               | 28 ++++++------
+ drivers/gpu/drm/vc4/vc4_crtc.c             | 14 +++---
+ drivers/gpu/drm/vc4/vc4_drv.c              | 22 ++++++----
+ drivers/gpu/drm/vc4/vc4_drv.h              |  7 ++-
+ drivers/gpu/drm/vc4/vc4_gem.c              | 24 +++++------
+ drivers/gpu/drm/vc4/vc4_hdmi.c             |  2 +-
+ drivers/gpu/drm/vc4/vc4_hvs.c              | 50 ++++++++++++----------
+ drivers/gpu/drm/vc4/vc4_irq.c              | 10 ++---
+ drivers/gpu/drm/vc4/vc4_kms.c              | 14 +++---
+ drivers/gpu/drm/vc4/vc4_perfmon.c          | 20 ++++-----
+ drivers/gpu/drm/vc4/vc4_plane.c            | 12 +++---
+ drivers/gpu/drm/vc4/vc4_render_cl.c        |  2 +-
+ drivers/gpu/drm/vc4/vc4_v3d.c              | 10 ++---
+ drivers/gpu/drm/vc4/vc4_validate.c         |  8 ++--
+ drivers/gpu/drm/vc4/vc4_validate_shaders.c |  2 +-
+ 16 files changed, 126 insertions(+), 111 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -153,11 +153,11 @@ static int __build_mock(struct kunit *te
+       return 0;
+ }
+-static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
++static struct vc4_dev *__mock_device(struct kunit *test, enum vc4_gen gen)
+ {
+       struct drm_device *drm;
+-      const struct drm_driver *drv = is_vc5 ? &vc5_drm_driver : &vc4_drm_driver;
+-      const struct vc4_mock_desc *desc = is_vc5 ? &vc5_mock : &vc4_mock;
++      const struct drm_driver *drv = (gen == VC4_GEN_5) ? &vc5_drm_driver : &vc4_drm_driver;
++      const struct vc4_mock_desc *desc = (gen == VC4_GEN_5) ? &vc5_mock : &vc4_mock;
+       struct vc4_dev *vc4;
+       struct device *dev;
+       int ret;
+@@ -171,7 +171,7 @@ static struct vc4_dev *__mock_device(str
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+       vc4->dev = dev;
+-      vc4->is_vc5 = is_vc5;
++      vc4->gen = gen;
+       vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
+@@ -191,10 +191,10 @@ static struct vc4_dev *__mock_device(str
+ struct vc4_dev *vc4_mock_device(struct kunit *test)
+ {
+-      return __mock_device(test, false);
++      return __mock_device(test, VC4_GEN_4);
+ }
+ struct vc4_dev *vc5_mock_device(struct kunit *test)
+ {
+-      return __mock_device(test, true);
++      return __mock_device(test, VC4_GEN_5);
+ }
+--- a/drivers/gpu/drm/vc4/vc4_bo.c
++++ b/drivers/gpu/drm/vc4/vc4_bo.c
+@@ -251,7 +251,7 @@ void vc4_bo_add_to_purgeable_pool(struct
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       mutex_lock(&vc4->purgeable.lock);
+@@ -265,7 +265,7 @@ static void vc4_bo_remove_from_purgeable
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       /* list_del_init() is used here because the caller might release
+@@ -396,7 +396,7 @@ struct drm_gem_object *vc4_create_object
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return ERR_PTR(-ENODEV);
+       bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+@@ -427,7 +427,7 @@ struct vc4_bo *vc4_bo_create(struct drm_
+       struct drm_gem_dma_object *dma_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return ERR_PTR(-ENODEV);
+       if (size == 0)
+@@ -496,7 +496,7 @@ int vc4_bo_dumb_create(struct drm_file *
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       ret = vc4_dumb_fixup_args(args);
+@@ -622,7 +622,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo)
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       /* Fast path: if the BO is already retained by someone, no need to
+@@ -661,7 +661,7 @@ void vc4_bo_dec_usecnt(struct vc4_bo *bo
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       /* Fast path: if the BO is still retained by someone, no need to test
+@@ -783,7 +783,7 @@ int vc4_create_bo_ioctl(struct drm_devic
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       ret = vc4_grab_bin_bo(vc4, vc4file);
+@@ -813,7 +813,7 @@ int vc4_mmap_bo_ioctl(struct drm_device
+       struct drm_vc4_mmap_bo *args = data;
+       struct drm_gem_object *gem_obj;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       gem_obj = drm_gem_object_lookup(file_priv, args->handle);
+@@ -839,7 +839,7 @@ vc4_create_shader_bo_ioctl(struct drm_de
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (args->size == 0)
+@@ -918,7 +918,7 @@ int vc4_set_tiling_ioctl(struct drm_devi
+       struct vc4_bo *bo;
+       bool t_format;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (args->flags != 0)
+@@ -964,7 +964,7 @@ int vc4_get_tiling_ioctl(struct drm_devi
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (args->flags != 0 || args->modifier != 0)
+@@ -1011,7 +1011,7 @@ int vc4_bo_cache_init(struct drm_device
+       int ret;
+       int i;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       /* Create the initial set of BO labels that the kernel will
+@@ -1075,7 +1075,7 @@ int vc4_label_bo_ioctl(struct drm_device
+       struct drm_gem_object *gem_obj;
+       int ret = 0, label;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!args->len)
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -263,7 +263,7 @@ static u32 vc4_get_fifo_full_level(struc
+                * Removing 1 from the FIFO full level however
+                * seems to completely remove that issue.
+                */
+-              if (!vc4->is_vc5)
++              if (vc4->gen == VC4_GEN_4)
+                       return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX - 1;
+               return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX;
+@@ -445,7 +445,7 @@ static void vc4_crtc_config_pv(struct dr
+       if (is_dsi)
+               CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
+-      if (vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_5)
+               CRTC_WRITE(PV_MUX_CFG,
+                          VC4_SET_FIELD(PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP,
+                                        PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
+@@ -936,7 +936,7 @@ static int vc4_async_set_fence_cb(struct
+       struct dma_fence *fence;
+       int ret;
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+               return vc4_queue_seqno_cb(dev, &flip_state->cb.seqno, bo->seqno,
+@@ -1023,7 +1023,7 @@ static int vc4_async_page_flip(struct dr
+       struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       /*
+@@ -1066,7 +1066,7 @@ int vc4_page_flip(struct drm_crtc *crtc,
+               struct drm_device *dev = crtc->dev;
+               struct vc4_dev *vc4 = to_vc4_dev(dev);
+-              if (vc4->is_vc5)
++              if (vc4->gen == VC4_GEN_5)
+                       return vc5_async_page_flip(crtc, fb, event, flags);
+               else
+                       return vc4_async_page_flip(crtc, fb, event, flags);
+@@ -1358,13 +1358,13 @@ int __vc4_crtc_init(struct drm_device *d
+       drm_crtc_helper_add(crtc, crtc_helper_funcs);
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+               drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+       }
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               /* We support CTM, but only for one CRTC at a time. It's therefore
+                * implemented as private driver state in vc4_kms, not here.
+                */
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -98,7 +98,7 @@ static int vc4_get_param_ioctl(struct dr
+       if (args->pad != 0)
+               return -EINVAL;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d)
+@@ -147,7 +147,7 @@ static int vc4_open(struct drm_device *d
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_file *vc4file;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
+@@ -165,7 +165,7 @@ static void vc4_close(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_file *vc4file = file->driver_priv;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (vc4file->bin_bo_used)
+@@ -305,13 +305,17 @@ static int vc4_drm_bind(struct device *d
+       struct vc4_dev *vc4;
+       struct device_node *node;
+       struct drm_crtc *crtc;
+-      bool is_vc5;
++      enum vc4_gen gen;
+       int ret = 0;
+       dev->coherent_dma_mask = DMA_BIT_MASK(32);
+-      is_vc5 = of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5");
+-      if (is_vc5)
++      if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
++              gen = VC4_GEN_5;
++      else
++              gen = VC4_GEN_4;
++
++      if (gen == VC4_GEN_5)
+               driver = &vc5_drm_driver;
+       else
+               driver = &vc4_drm_driver;
+@@ -329,14 +333,14 @@ static int vc4_drm_bind(struct device *d
+       vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base);
+       if (IS_ERR(vc4))
+               return PTR_ERR(vc4);
+-      vc4->is_vc5 = is_vc5;
++      vc4->gen = gen;
+       vc4->dev = dev;
+       drm = &vc4->base;
+       platform_set_drvdata(pdev, drm);
+       INIT_LIST_HEAD(&vc4->debugfs_list);
+-      if (!is_vc5) {
++      if (gen == VC4_GEN_4) {
+               ret = drmm_mutex_init(drm, &vc4->bin_bo_lock);
+               if (ret)
+                       return ret;
+@@ -350,7 +354,7 @@ static int vc4_drm_bind(struct device *d
+       if (ret)
+               return ret;
+-      if (!is_vc5) {
++      if (gen == VC4_GEN_4) {
+               ret = vc4_gem_init(drm);
+               if (ret)
+                       return ret;
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -80,11 +80,16 @@ struct vc4_perfmon {
+       u64 counters[];
+ };
++enum vc4_gen {
++      VC4_GEN_4,
++      VC4_GEN_5,
++};
++
+ struct vc4_dev {
+       struct drm_device base;
+       struct device *dev;
+-      bool is_vc5;
++      enum vc4_gen gen;
+       unsigned int irq;
+--- a/drivers/gpu/drm/vc4/vc4_gem.c
++++ b/drivers/gpu/drm/vc4/vc4_gem.c
+@@ -76,7 +76,7 @@ vc4_get_hang_state_ioctl(struct drm_devi
+       u32 i;
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -389,7 +389,7 @@ vc4_wait_for_seqno(struct drm_device *de
+       unsigned long timeout_expire;
+       DEFINE_WAIT(wait);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (vc4->finished_seqno >= seqno)
+@@ -474,7 +474,7 @@ vc4_submit_next_bin_job(struct drm_devic
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_exec_info *exec;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+ again:
+@@ -522,7 +522,7 @@ vc4_submit_next_render_job(struct drm_de
+       if (!exec)
+               return;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       /* A previous RCL may have written to one of our textures, and
+@@ -543,7 +543,7 @@ vc4_move_job_to_render(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       bool was_empty = list_empty(&vc4->render_job_list);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       list_move_tail(&exec->head, &vc4->render_job_list);
+@@ -1012,7 +1012,7 @@ vc4_job_handle_completed(struct vc4_dev
+       unsigned long irqflags;
+       struct vc4_seqno_cb *cb, *cb_temp;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+@@ -1051,7 +1051,7 @@ int vc4_queue_seqno_cb(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       cb->func = func;
+@@ -1107,7 +1107,7 @@ vc4_wait_seqno_ioctl(struct drm_device *
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_vc4_wait_seqno *args = data;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
+@@ -1124,7 +1124,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (args->pad != 0)
+@@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *d
+                                 args->shader_rec_size,
+                                 args->bo_handle_count);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -1310,7 +1310,7 @@ int vc4_gem_init(struct drm_device *dev)
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       vc4->dma_fence_context = dma_fence_context_alloc(1);
+@@ -1369,7 +1369,7 @@ int vc4_gem_madvise_ioctl(struct drm_dev
+       struct vc4_bo *bo;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       switch (args->madv) {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -2614,7 +2614,7 @@ static int vc4_hdmi_audio_prepare(struct
+                                            VC4_HDMI_AUDIO_PACKET_CEA_MASK);
+       /* Set the MAI threshold */
+-      if (vc4->is_vc5)
++      if (vc4->gen >= VC4_GEN_5)
+               HDMI_WRITE(HDMI_MAI_THR,
+                       VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
+                       VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -416,7 +416,7 @@ static void vc4_hvs_irq_enable_eof(const
+                                  unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
+-      u32 irq_mask = vc4->is_vc5 ?
++      u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+               SCALER5_DISPCTRL_DSPEIEOF(channel) :
+               SCALER_DISPCTRL_DSPEIEOF(channel);
+@@ -428,7 +428,7 @@ static void vc4_hvs_irq_clear_eof(const
+                                 unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
+-      u32 irq_mask = vc4->is_vc5 ?
++      u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+               SCALER5_DISPCTRL_DSPEIEOF(channel) :
+               SCALER_DISPCTRL_DSPEIEOF(channel);
+@@ -620,7 +620,7 @@ int vc4_hvs_get_fifo_from_output(struct
+       u32 reg;
+       int ret;
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               return output;
+       /*
+@@ -701,7 +701,7 @@ static int vc4_hvs_init_channel(struct v
+       dispctrl = SCALER_DISPCTRLX_ENABLE;
+       dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+                                         SCALER_DISPCTRLX_WIDTH) |
+                           VC4_SET_FIELD(mode->vdisplay,
+@@ -732,7 +732,7 @@ static int vc4_hvs_init_channel(struct v
+       /* Reload the LUT, since the SRAMs would have been disabled if
+        * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
+        */
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               vc4_hvs_lut_load(hvs, vc4_crtc);
+       else
+               vc5_hvs_lut_load(hvs, vc4_crtc);
+@@ -782,7 +782,7 @@ static int vc4_hvs_gamma_check(struct dr
+       struct drm_device *dev = crtc->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               return 0;
+       if (!crtc_state->color_mgmt_changed)
+@@ -1036,7 +1036,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+               u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
+               if (crtc->state->gamma_lut) {
+-                      if (!vc4->is_vc5) {
++                      if (vc4->gen == VC4_GEN_4) {
+                               vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
+                               dispbkgndx |= SCALER_DISPBKGND_GAMMA;
+                       } else {
+@@ -1053,7 +1053,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+                        * should already be disabling/enabling the pipeline
+                        * when gamma changes.
+                        */
+-                      if (!vc4->is_vc5)
++                      if (vc4->gen == VC4_GEN_4)
+                               dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+               }
+               HVS_WRITE(SCALER_DISPBKGNDX(channel), dispbkgndx);
+@@ -1069,7 +1069,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
+ {
+-      struct drm_device *drm = &hvs->vc4->base;
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
+       u32 dispctrl;
+       int idx;
+@@ -1077,8 +1078,9 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+               return;
+       dispctrl = HVS_READ(SCALER_DISPCTRL);
+-      dispctrl &= ~(hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+-                                       SCALER_DISPCTRL_DSPEISLUR(channel));
++      dispctrl &= ~((vc4->gen == VC4_GEN_5) ?
++                    SCALER5_DISPCTRL_DSPEISLUR(channel) :
++                    SCALER_DISPCTRL_DSPEISLUR(channel));
+       HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+@@ -1087,7 +1089,8 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+ void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
+ {
+-      struct drm_device *drm = &hvs->vc4->base;
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
+       u32 dispctrl;
+       int idx;
+@@ -1095,8 +1098,9 @@ void vc4_hvs_unmask_underrun(struct vc4_
+               return;
+       dispctrl = HVS_READ(SCALER_DISPCTRL);
+-      dispctrl |= (hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+-                                      SCALER_DISPCTRL_DSPEISLUR(channel));
++      dispctrl |= ((vc4->gen == VC4_GEN_5) ?
++                   SCALER5_DISPCTRL_DSPEISLUR(channel) :
++                   SCALER_DISPCTRL_DSPEISLUR(channel));
+       HVS_WRITE(SCALER_DISPSTAT,
+                 SCALER_DISPSTAT_EUFLOW(channel));
+@@ -1139,8 +1143,10 @@ static irqreturn_t vc4_hvs_irq_handler(i
+       control = HVS_READ(SCALER_DISPCTRL);
+       for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) {
+-              dspeislur = vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+-                                        SCALER_DISPCTRL_DSPEISLUR(channel);
++              dspeislur = (vc4->gen == VC4_GEN_5) ?
++                      SCALER5_DISPCTRL_DSPEISLUR(channel) :
++                      SCALER_DISPCTRL_DSPEISLUR(channel);
++
+               /* Interrupt masking is not always honored, so check it here. */
+               if (status & SCALER_DISPSTAT_EUFLOW(channel) &&
+                   control & dspeislur) {
+@@ -1177,7 +1183,7 @@ int vc4_hvs_debugfs_init(struct drm_mino
+       if (!vc4->hvs)
+               return -ENODEV;
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
+                                   minor->debugfs_root,
+                                   &vc4->load_tracker_enabled);
+@@ -1235,7 +1241,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+        * between planes when they don't overlap on the screen, but
+        * for now we just allocate globally.
+        */
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               /* 48k words of 2x12-bit pixels */
+               drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
+       else
+@@ -1269,7 +1275,7 @@ static int vc4_hvs_bind(struct device *d
+       hvs->regset.regs = hvs_regs;
+       hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+-      if (vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_5) {
+               struct rpi_firmware *firmware;
+               struct device_node *node;
+               unsigned int max_rate;
+@@ -1307,7 +1313,7 @@ static int vc4_hvs_bind(struct device *d
+               }
+       }
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               hvs->dlist = hvs->regs + SCALER_DLIST_START;
+       else
+               hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+@@ -1348,7 +1354,7 @@ static int vc4_hvs_bind(struct device *d
+                   SCALER_DISPCTRL_DISPEIRQ(1) |
+                   SCALER_DISPCTRL_DISPEIRQ(2);
+-      if (!vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_4)
+               dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
+                             SCALER_DISPCTRL_SLVWREIRQ |
+                             SCALER_DISPCTRL_SLVRDEIRQ |
+@@ -1403,7 +1409,7 @@ static int vc4_hvs_bind(struct device *d
+       /* Recompute Composite Output Buffer (COB) allocations for the displays
+        */
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
+                * The bottom 2048 pixels are full 32bpp RGBA (intended for the
+                * TXP composing RGBA to memory), whilst the remainder are only
+--- a/drivers/gpu/drm/vc4/vc4_irq.c
++++ b/drivers/gpu/drm/vc4/vc4_irq.c
+@@ -265,7 +265,7 @@ vc4_irq_enable(struct drm_device *dev)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (!vc4->v3d)
+@@ -282,7 +282,7 @@ vc4_irq_disable(struct drm_device *dev)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (!vc4->v3d)
+@@ -305,7 +305,7 @@ int vc4_irq_install(struct drm_device *d
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (irq == IRQ_NOTCONNECTED)
+@@ -326,7 +326,7 @@ void vc4_irq_uninstall(struct drm_device
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       vc4_irq_disable(dev);
+@@ -339,7 +339,7 @@ void vc4_irq_reset(struct drm_device *de
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       /* Acknowledge any stale IRQs. */
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -378,7 +378,7 @@ static void vc4_atomic_commit_tail(struc
+               old_hvs_state->fifo_state[channel].pending_commit = NULL;
+       }
+-      if (vc4->is_vc5 && !vc4->firmware_kms) {
++      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+                                              new_hvs_state->core_clock_rate);
+               unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -398,7 +398,7 @@ static void vc4_atomic_commit_tail(struc
+       vc4_ctm_commit(vc4, state);
+       if (!vc4->firmware_kms) {
+-              if (vc4->is_vc5)
++              if (vc4->gen == VC4_GEN_5)
+                       vc5_hvs_pv_muxing_commit(vc4, state);
+               else
+                       vc4_hvs_pv_muxing_commit(vc4, state);
+@@ -417,7 +417,7 @@ static void vc4_atomic_commit_tail(struc
+       drm_atomic_helper_cleanup_planes(dev, state);
+-      if (vc4->is_vc5 && !vc4->firmware_kms) {
++      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long core_rate = min_t(unsigned long,
+                                               hvs->max_core_rate,
+                                               new_hvs_state->core_clock_rate);
+@@ -482,7 +482,7 @@ static struct drm_framebuffer *vc4_fb_cr
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_mode_fb_cmd2 mode_cmd_local;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return ERR_PTR(-ENODEV);
+       /* If the user didn't specify a modifier, use the
+@@ -1065,7 +1065,7 @@ int vc4_kms_load(struct drm_device *dev)
+        * the BCM2711, but the load tracker computations are used for
+        * the core clock rate calculation.
+        */
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+               /* Start with the load tracker enabled. Can be
+                * disabled through the debugfs load_tracker file.
+                */
+@@ -1081,7 +1081,7 @@ int vc4_kms_load(struct drm_device *dev)
+               return ret;
+       }
+-      if (vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_5) {
+               dev->mode_config.max_width = 7680;
+               dev->mode_config.max_height = 7680;
+       } else {
+@@ -1089,7 +1089,7 @@ int vc4_kms_load(struct drm_device *dev)
+               dev->mode_config.max_height = 2048;
+       }
+-      dev->mode_config.funcs = vc4->is_vc5 ? &vc5_mode_funcs : &vc4_mode_funcs;
++      dev->mode_config.funcs = (vc4->gen > VC4_GEN_4) ? &vc5_mode_funcs : &vc4_mode_funcs;
+       dev->mode_config.helper_private = &vc4_mode_config_helpers;
+       dev->mode_config.preferred_depth = 24;
+       dev->mode_config.async_page_flip = true;
+--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
++++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
+@@ -23,7 +23,7 @@ void vc4_perfmon_get(struct vc4_perfmon
+               return;
+       vc4 = perfmon->dev;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       refcount_inc(&perfmon->refcnt);
+@@ -37,7 +37,7 @@ void vc4_perfmon_put(struct vc4_perfmon
+               return;
+       vc4 = perfmon->dev;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (refcount_dec_and_test(&perfmon->refcnt))
+@@ -49,7 +49,7 @@ void vc4_perfmon_start(struct vc4_dev *v
+       unsigned int i;
+       u32 mask;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
+@@ -69,7 +69,7 @@ void vc4_perfmon_stop(struct vc4_dev *vc
+ {
+       unsigned int i;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       if (WARN_ON_ONCE(!vc4->active_perfmon ||
+@@ -90,7 +90,7 @@ struct vc4_perfmon *vc4_perfmon_find(str
+       struct vc4_dev *vc4 = vc4file->dev;
+       struct vc4_perfmon *perfmon;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return NULL;
+       mutex_lock(&vc4file->perfmon.lock);
+@@ -105,7 +105,7 @@ void vc4_perfmon_open_file(struct vc4_fi
+ {
+       struct vc4_dev *vc4 = vc4file->dev;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       mutex_init(&vc4file->perfmon.lock);
+@@ -126,7 +126,7 @@ void vc4_perfmon_close_file(struct vc4_f
+ {
+       struct vc4_dev *vc4 = vc4file->dev;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       mutex_lock(&vc4file->perfmon.lock);
+@@ -146,7 +146,7 @@ int vc4_perfmon_create_ioctl(struct drm_
+       unsigned int i;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -200,7 +200,7 @@ int vc4_perfmon_destroy_ioctl(struct drm
+       struct drm_vc4_perfmon_destroy *req = data;
+       struct vc4_perfmon *perfmon;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -228,7 +228,7 @@ int vc4_perfmon_get_values_ioctl(struct
+       struct vc4_perfmon *perfmon;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (!vc4->v3d) {
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -633,10 +633,10 @@ static u32 vc4_lbm_size(struct drm_plane
+       }
+       /* Align it to 64 or 128 (hvs5) bytes */
+-      lbm = roundup(lbm, vc4->is_vc5 ? 128 : 64);
++      lbm = roundup(lbm, vc4->gen == VC4_GEN_5 ? 128 : 64);
+       /* Each "word" of the LBM memory contains 2 or 4 (hvs5) pixels */
+-      lbm /= vc4->is_vc5 ? 4 : 2;
++      lbm /= vc4->gen == VC4_GEN_5 ? 4 : 2;
+       return lbm;
+ }
+@@ -760,7 +760,7 @@ static int vc4_plane_allocate_lbm(struct
+               ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+                                                &vc4_state->lbm,
+                                                lbm_size,
+-                                               vc4->is_vc5 ? 64 : 32,
++                                               vc4->gen == VC4_GEN_5 ? 64 : 32,
+                                                0, 0);
+               spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+@@ -1141,7 +1141,7 @@ static int vc4_plane_mode_set(struct drm
+       mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
+                         fb->format->has_alpha;
+-      if (!vc4->is_vc5) {
++      if (vc4->gen == VC4_GEN_4) {
+       /* Control word */
+               vc4_dlist_write(vc4_state,
+                               SCALER_CTL0_VALID |
+@@ -1717,7 +1717,7 @@ struct drm_plane *vc4_plane_init(struct
+       };
+       for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+-              if (!hvs_formats[i].hvs5_only || vc4->is_vc5) {
++              if (!hvs_formats[i].hvs5_only || vc4->gen == VC4_GEN_5) {
+                       formats[num_formats] = hvs_formats[i].drm;
+                       num_formats++;
+               }
+@@ -1732,7 +1732,7 @@ struct drm_plane *vc4_plane_init(struct
+               return ERR_CAST(vc4_plane);
+       plane = &vc4_plane->base;
+-      if (vc4->is_vc5)
++      if (vc4->gen == VC4_GEN_5)
+               drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
+       else
+               drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
+@@ -599,7 +599,7 @@ int vc4_get_rcl(struct drm_device *dev,
+       bool has_bin = args->bin_cl_size != 0;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       if (args->min_x_tile > args->max_x_tile ||
+--- a/drivers/gpu/drm/vc4/vc4_v3d.c
++++ b/drivers/gpu/drm/vc4/vc4_v3d.c
+@@ -127,7 +127,7 @@ static int vc4_v3d_debugfs_ident(struct
+ int
+ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       mutex_lock(&vc4->power_lock);
+@@ -148,7 +148,7 @@ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ void
+ vc4_v3d_pm_put(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       mutex_lock(&vc4->power_lock);
+@@ -178,7 +178,7 @@ int vc4_v3d_get_bin_slot(struct vc4_dev
+       uint64_t seqno = 0;
+       struct vc4_exec_info *exec;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+ try_again:
+@@ -325,7 +325,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *v
+ {
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       mutex_lock(&vc4->bin_bo_lock);
+@@ -360,7 +360,7 @@ static void bin_bo_release(struct kref *
+ void vc4_v3d_bin_bo_put(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return;
+       mutex_lock(&vc4->bin_bo_lock);
+--- a/drivers/gpu/drm/vc4/vc4_validate.c
++++ b/drivers/gpu/drm/vc4/vc4_validate.c
+@@ -109,7 +109,7 @@ vc4_use_bo(struct vc4_exec_info *exec, u
+       struct drm_gem_dma_object *obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return NULL;
+       if (hindex >= exec->bo_count) {
+@@ -169,7 +169,7 @@ vc4_check_tex_size(struct vc4_exec_info
+       uint32_t utile_w = utile_width(cpp);
+       uint32_t utile_h = utile_height(cpp);
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return false;
+       /* The shaded vertex format stores signed 12.4 fixed point
+@@ -495,7 +495,7 @@ vc4_validate_bin_cl(struct drm_device *d
+       uint32_t dst_offset = 0;
+       uint32_t src_offset = 0;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       while (src_offset < len) {
+@@ -942,7 +942,7 @@ vc4_validate_shader_recs(struct drm_devi
+       uint32_t i;
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return -ENODEV;
+       for (i = 0; i < exec->shader_state_count; i++) {
+--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+@@ -786,7 +786,7 @@ vc4_validate_shader(struct drm_gem_dma_o
+       struct vc4_validated_shader_info *validated_shader = NULL;
+       struct vc4_shader_validation_state validation_state;
+-      if (WARN_ON_ONCE(vc4->is_vc5))
++      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+               return NULL;
+       memset(&validation_state, 0, sizeof(validation_state));
diff --git a/target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch b/target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch
new file mode 100644 (file)
index 0000000..699db05
--- /dev/null
@@ -0,0 +1,577 @@
+From c382ea6b0457027b6ad883ee4348e03df515a785 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:29:27 +0100
+Subject: [PATCH] drm/vc4: Make v3d paths unavailable on any generation newer
+ than vc4
+
+The V3D IP has been separate since BCM2711, so let's make sure we issue
+a WARN if we're running not only on BCM2711, but also anything newer.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_bo.c               | 28 +++++++++++-----------
+ drivers/gpu/drm/vc4/vc4_crtc.c             |  4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.c              |  8 +++----
+ drivers/gpu/drm/vc4/vc4_gem.c              | 24 +++++++++----------
+ drivers/gpu/drm/vc4/vc4_irq.c              | 10 ++++----
+ drivers/gpu/drm/vc4/vc4_kms.c              |  2 +-
+ drivers/gpu/drm/vc4/vc4_perfmon.c          | 20 ++++++++--------
+ drivers/gpu/drm/vc4/vc4_render_cl.c        |  2 +-
+ drivers/gpu/drm/vc4/vc4_v3d.c              | 10 ++++----
+ drivers/gpu/drm/vc4/vc4_validate.c         |  8 +++----
+ drivers/gpu/drm/vc4/vc4_validate_shaders.c |  2 +-
+ 11 files changed, 59 insertions(+), 59 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_bo.c
++++ b/drivers/gpu/drm/vc4/vc4_bo.c
+@@ -251,7 +251,7 @@ void vc4_bo_add_to_purgeable_pool(struct
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       mutex_lock(&vc4->purgeable.lock);
+@@ -265,7 +265,7 @@ static void vc4_bo_remove_from_purgeable
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       /* list_del_init() is used here because the caller might release
+@@ -396,7 +396,7 @@ struct drm_gem_object *vc4_create_object
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return ERR_PTR(-ENODEV);
+       bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+@@ -427,7 +427,7 @@ struct vc4_bo *vc4_bo_create(struct drm_
+       struct drm_gem_dma_object *dma_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return ERR_PTR(-ENODEV);
+       if (size == 0)
+@@ -496,7 +496,7 @@ int vc4_bo_dumb_create(struct drm_file *
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       ret = vc4_dumb_fixup_args(args);
+@@ -622,7 +622,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo)
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       /* Fast path: if the BO is already retained by someone, no need to
+@@ -661,7 +661,7 @@ void vc4_bo_dec_usecnt(struct vc4_bo *bo
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       /* Fast path: if the BO is still retained by someone, no need to test
+@@ -783,7 +783,7 @@ int vc4_create_bo_ioctl(struct drm_devic
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       ret = vc4_grab_bin_bo(vc4, vc4file);
+@@ -813,7 +813,7 @@ int vc4_mmap_bo_ioctl(struct drm_device
+       struct drm_vc4_mmap_bo *args = data;
+       struct drm_gem_object *gem_obj;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       gem_obj = drm_gem_object_lookup(file_priv, args->handle);
+@@ -839,7 +839,7 @@ vc4_create_shader_bo_ioctl(struct drm_de
+       struct vc4_bo *bo = NULL;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (args->size == 0)
+@@ -918,7 +918,7 @@ int vc4_set_tiling_ioctl(struct drm_devi
+       struct vc4_bo *bo;
+       bool t_format;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (args->flags != 0)
+@@ -964,7 +964,7 @@ int vc4_get_tiling_ioctl(struct drm_devi
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (args->flags != 0 || args->modifier != 0)
+@@ -1011,7 +1011,7 @@ int vc4_bo_cache_init(struct drm_device
+       int ret;
+       int i;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       /* Create the initial set of BO labels that the kernel will
+@@ -1075,7 +1075,7 @@ int vc4_label_bo_ioctl(struct drm_device
+       struct drm_gem_object *gem_obj;
+       int ret = 0, label;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!args->len)
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1023,7 +1023,7 @@ static int vc4_async_page_flip(struct dr
+       struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       /*
+@@ -1066,7 +1066,7 @@ int vc4_page_flip(struct drm_crtc *crtc,
+               struct drm_device *dev = crtc->dev;
+               struct vc4_dev *vc4 = to_vc4_dev(dev);
+-              if (vc4->gen == VC4_GEN_5)
++              if (vc4->gen > VC4_GEN_4)
+                       return vc5_async_page_flip(crtc, fb, event, flags);
+               else
+                       return vc4_async_page_flip(crtc, fb, event, flags);
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -98,7 +98,7 @@ static int vc4_get_param_ioctl(struct dr
+       if (args->pad != 0)
+               return -EINVAL;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d)
+@@ -147,7 +147,7 @@ static int vc4_open(struct drm_device *d
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_file *vc4file;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
+@@ -165,7 +165,7 @@ static void vc4_close(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_file *vc4file = file->driver_priv;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (vc4file->bin_bo_used)
+@@ -315,7 +315,7 @@ static int vc4_drm_bind(struct device *d
+       else
+               gen = VC4_GEN_4;
+-      if (gen == VC4_GEN_5)
++      if (gen > VC4_GEN_4)
+               driver = &vc5_drm_driver;
+       else
+               driver = &vc4_drm_driver;
+--- a/drivers/gpu/drm/vc4/vc4_gem.c
++++ b/drivers/gpu/drm/vc4/vc4_gem.c
+@@ -76,7 +76,7 @@ vc4_get_hang_state_ioctl(struct drm_devi
+       u32 i;
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -389,7 +389,7 @@ vc4_wait_for_seqno(struct drm_device *de
+       unsigned long timeout_expire;
+       DEFINE_WAIT(wait);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (vc4->finished_seqno >= seqno)
+@@ -474,7 +474,7 @@ vc4_submit_next_bin_job(struct drm_devic
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_exec_info *exec;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+ again:
+@@ -522,7 +522,7 @@ vc4_submit_next_render_job(struct drm_de
+       if (!exec)
+               return;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       /* A previous RCL may have written to one of our textures, and
+@@ -543,7 +543,7 @@ vc4_move_job_to_render(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       bool was_empty = list_empty(&vc4->render_job_list);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       list_move_tail(&exec->head, &vc4->render_job_list);
+@@ -1012,7 +1012,7 @@ vc4_job_handle_completed(struct vc4_dev
+       unsigned long irqflags;
+       struct vc4_seqno_cb *cb, *cb_temp;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       spin_lock_irqsave(&vc4->job_lock, irqflags);
+@@ -1051,7 +1051,7 @@ int vc4_queue_seqno_cb(struct drm_device
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       cb->func = func;
+@@ -1107,7 +1107,7 @@ vc4_wait_seqno_ioctl(struct drm_device *
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_vc4_wait_seqno *args = data;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
+@@ -1124,7 +1124,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev
+       struct drm_gem_object *gem_obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (args->pad != 0)
+@@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *d
+                                 args->shader_rec_size,
+                                 args->bo_handle_count);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -1310,7 +1310,7 @@ int vc4_gem_init(struct drm_device *dev)
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       vc4->dma_fence_context = dma_fence_context_alloc(1);
+@@ -1369,7 +1369,7 @@ int vc4_gem_madvise_ioctl(struct drm_dev
+       struct vc4_bo *bo;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       switch (args->madv) {
+--- a/drivers/gpu/drm/vc4/vc4_irq.c
++++ b/drivers/gpu/drm/vc4/vc4_irq.c
+@@ -265,7 +265,7 @@ vc4_irq_enable(struct drm_device *dev)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (!vc4->v3d)
+@@ -282,7 +282,7 @@ vc4_irq_disable(struct drm_device *dev)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (!vc4->v3d)
+@@ -305,7 +305,7 @@ int vc4_irq_install(struct drm_device *d
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (irq == IRQ_NOTCONNECTED)
+@@ -326,7 +326,7 @@ void vc4_irq_uninstall(struct drm_device
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       vc4_irq_disable(dev);
+@@ -339,7 +339,7 @@ void vc4_irq_reset(struct drm_device *de
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       unsigned long irqflags;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       /* Acknowledge any stale IRQs. */
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -482,7 +482,7 @@ static struct drm_framebuffer *vc4_fb_cr
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct drm_mode_fb_cmd2 mode_cmd_local;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return ERR_PTR(-ENODEV);
+       /* If the user didn't specify a modifier, use the
+--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
++++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
+@@ -23,7 +23,7 @@ void vc4_perfmon_get(struct vc4_perfmon
+               return;
+       vc4 = perfmon->dev;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       refcount_inc(&perfmon->refcnt);
+@@ -37,7 +37,7 @@ void vc4_perfmon_put(struct vc4_perfmon
+               return;
+       vc4 = perfmon->dev;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (refcount_dec_and_test(&perfmon->refcnt))
+@@ -49,7 +49,7 @@ void vc4_perfmon_start(struct vc4_dev *v
+       unsigned int i;
+       u32 mask;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
+@@ -69,7 +69,7 @@ void vc4_perfmon_stop(struct vc4_dev *vc
+ {
+       unsigned int i;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       if (WARN_ON_ONCE(!vc4->active_perfmon ||
+@@ -90,7 +90,7 @@ struct vc4_perfmon *vc4_perfmon_find(str
+       struct vc4_dev *vc4 = vc4file->dev;
+       struct vc4_perfmon *perfmon;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return NULL;
+       mutex_lock(&vc4file->perfmon.lock);
+@@ -105,7 +105,7 @@ void vc4_perfmon_open_file(struct vc4_fi
+ {
+       struct vc4_dev *vc4 = vc4file->dev;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       mutex_init(&vc4file->perfmon.lock);
+@@ -126,7 +126,7 @@ void vc4_perfmon_close_file(struct vc4_f
+ {
+       struct vc4_dev *vc4 = vc4file->dev;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       mutex_lock(&vc4file->perfmon.lock);
+@@ -146,7 +146,7 @@ int vc4_perfmon_create_ioctl(struct drm_
+       unsigned int i;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -200,7 +200,7 @@ int vc4_perfmon_destroy_ioctl(struct drm
+       struct drm_vc4_perfmon_destroy *req = data;
+       struct vc4_perfmon *perfmon;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d) {
+@@ -228,7 +228,7 @@ int vc4_perfmon_get_values_ioctl(struct
+       struct vc4_perfmon *perfmon;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (!vc4->v3d) {
+--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
+@@ -599,7 +599,7 @@ int vc4_get_rcl(struct drm_device *dev,
+       bool has_bin = args->bin_cl_size != 0;
+       int ret;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       if (args->min_x_tile > args->max_x_tile ||
+--- a/drivers/gpu/drm/vc4/vc4_v3d.c
++++ b/drivers/gpu/drm/vc4/vc4_v3d.c
+@@ -127,7 +127,7 @@ static int vc4_v3d_debugfs_ident(struct
+ int
+ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       mutex_lock(&vc4->power_lock);
+@@ -148,7 +148,7 @@ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ void
+ vc4_v3d_pm_put(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       mutex_lock(&vc4->power_lock);
+@@ -178,7 +178,7 @@ int vc4_v3d_get_bin_slot(struct vc4_dev
+       uint64_t seqno = 0;
+       struct vc4_exec_info *exec;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+ try_again:
+@@ -325,7 +325,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *v
+ {
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       mutex_lock(&vc4->bin_bo_lock);
+@@ -360,7 +360,7 @@ static void bin_bo_release(struct kref *
+ void vc4_v3d_bin_bo_put(struct vc4_dev *vc4)
+ {
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return;
+       mutex_lock(&vc4->bin_bo_lock);
+--- a/drivers/gpu/drm/vc4/vc4_validate.c
++++ b/drivers/gpu/drm/vc4/vc4_validate.c
+@@ -109,7 +109,7 @@ vc4_use_bo(struct vc4_exec_info *exec, u
+       struct drm_gem_dma_object *obj;
+       struct vc4_bo *bo;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return NULL;
+       if (hindex >= exec->bo_count) {
+@@ -169,7 +169,7 @@ vc4_check_tex_size(struct vc4_exec_info
+       uint32_t utile_w = utile_width(cpp);
+       uint32_t utile_h = utile_height(cpp);
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return false;
+       /* The shaded vertex format stores signed 12.4 fixed point
+@@ -495,7 +495,7 @@ vc4_validate_bin_cl(struct drm_device *d
+       uint32_t dst_offset = 0;
+       uint32_t src_offset = 0;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       while (src_offset < len) {
+@@ -942,7 +942,7 @@ vc4_validate_shader_recs(struct drm_devi
+       uint32_t i;
+       int ret = 0;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return -ENODEV;
+       for (i = 0; i < exec->shader_state_count; i++) {
+--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+@@ -786,7 +786,7 @@ vc4_validate_shader(struct drm_gem_dma_o
+       struct vc4_validated_shader_info *validated_shader = NULL;
+       struct vc4_shader_validation_state validation_state;
+-      if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++      if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+               return NULL;
+       memset(&validation_state, 0, sizeof(validation_state));
diff --git a/target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch b/target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch
new file mode 100644 (file)
index 0000000..048b5d3
--- /dev/null
@@ -0,0 +1,125 @@
+From 72e5eb3d9511af2f056911d70c4d033d4fc674b2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:07:29 +0100
+Subject: [PATCH] drm/vc4: hvs: Use switch statement to simplify
+ vc4_hvs_get_fifo_from_output
+
+Since we'll support BCM2712 soon, let's move the logic behind
+vc4_hvs_get_fifo_from_output() to a switch to extend it more easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 80 +++++++++++++++++++----------------
+ 1 file changed, 43 insertions(+), 37 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -620,57 +620,63 @@ int vc4_hvs_get_fifo_from_output(struct
+       u32 reg;
+       int ret;
+-      if (vc4->gen == VC4_GEN_4)
++      switch (vc4->gen) {
++      case VC4_GEN_4:
+               return output;
+-      /*
+-       * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
+-       * here, but this function is only used during the DRM device
+-       * initialization, so we should be fine.
+-       */
+-
+-      switch (output) {
+-      case 0:
+-              return 0;
+-
+-      case 1:
+-              return 1;
+-
+-      case 2:
+-              reg = HVS_READ(SCALER_DISPECTRL);
+-              ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg);
+-              if (ret == 0)
+-                      return 2;
+-
+-              return 0;
+-
+-      case 3:
+-              reg = HVS_READ(SCALER_DISPCTRL);
+-              ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg);
+-              if (ret == 3)
+-                      return -EPIPE;
+-
+-              return ret;
+-
+-      case 4:
+-              reg = HVS_READ(SCALER_DISPEOLN);
+-              ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg);
+-              if (ret == 3)
+-                      return -EPIPE;
++      case VC4_GEN_5:
++              /*
++               * NOTE: We should probably use
++               * drm_dev_enter()/drm_dev_exit() here, but this
++               * function is only used during the DRM device
++               * initialization, so we should be fine.
++               */
++
++              switch (output) {
++              case 0:
++                      return 0;
++
++              case 1:
++                      return 1;
++
++              case 2:
++                      reg = HVS_READ(SCALER_DISPECTRL);
++                      ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg);
++                      if (ret == 0)
++                              return 2;
++
++                      return 0;
++
++              case 3:
++                      reg = HVS_READ(SCALER_DISPCTRL);
++                      ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg);
++                      if (ret == 3)
++                              return -EPIPE;
++
++                      return ret;
++
++              case 4:
++                      reg = HVS_READ(SCALER_DISPEOLN);
++                      ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg);
++                      if (ret == 3)
++                              return -EPIPE;
++
++                      return ret;
++
++              case 5:
++                      reg = HVS_READ(SCALER_DISPDITHER);
++                      ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg);
++                      if (ret == 3)
++                              return -EPIPE;
+-              return ret;
++                      return ret;
+-      case 5:
+-              reg = HVS_READ(SCALER_DISPDITHER);
+-              ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg);
+-              if (ret == 3)
++              default:
+                       return -EPIPE;
+-
+-              return ret;
+-
+-      default:
+-              return -EPIPE;
++              }
+       }
++
++      return -EPIPE;
+ }
+ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch b/target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch
new file mode 100644 (file)
index 0000000..984b329
--- /dev/null
@@ -0,0 +1,74 @@
+From 72bfb10c9393688d00e4e0b00d416e23c2753318 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:07:29 +0100
+Subject: [PATCH] drm/vc4: hvs: Use switch statement to simplify
+ enabling/disabling irq
+
+Since we'll support BCM2712 soon, let's move the logic to enable and
+disable the end-of-frame interrupts to a switch to extend it more
+easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 42 ++++++++++++++++++++++++++---------
+ 1 file changed, 32 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -416,24 +416,46 @@ static void vc4_hvs_irq_enable_eof(const
+                                  unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
+-      u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+-              SCALER5_DISPCTRL_DSPEIEOF(channel) :
+-              SCALER_DISPCTRL_DSPEIEOF(channel);
+-      HVS_WRITE(SCALER_DISPCTRL,
+-                HVS_READ(SCALER_DISPCTRL) | irq_mask);
++      switch (vc4->gen) {
++      case VC4_GEN_4:
++              HVS_WRITE(SCALER_DISPCTRL,
++                        HVS_READ(SCALER_DISPCTRL) |
++                        SCALER_DISPCTRL_DSPEIEOF(channel));
++              break;
++
++      case VC4_GEN_5:
++              HVS_WRITE(SCALER_DISPCTRL,
++                        HVS_READ(SCALER_DISPCTRL) |
++                        SCALER5_DISPCTRL_DSPEIEOF(channel));
++              break;
++
++      default:
++              break;
++      }
+ }
+ static void vc4_hvs_irq_clear_eof(const struct vc4_hvs *hvs,
+                                 unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
+-      u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+-              SCALER5_DISPCTRL_DSPEIEOF(channel) :
+-              SCALER_DISPCTRL_DSPEIEOF(channel);
+-      HVS_WRITE(SCALER_DISPCTRL,
+-                HVS_READ(SCALER_DISPCTRL) & ~irq_mask);
++      switch (vc4->gen) {
++      case VC4_GEN_4:
++              HVS_WRITE(SCALER_DISPCTRL,
++                        HVS_READ(SCALER_DISPCTRL) &
++                        ~SCALER_DISPCTRL_DSPEIEOF(channel));
++              break;
++
++      case VC4_GEN_5:
++              HVS_WRITE(SCALER_DISPCTRL,
++                        HVS_READ(SCALER_DISPCTRL) &
++                        ~SCALER5_DISPCTRL_DSPEIEOF(channel));
++              break;
++
++      default:
++              break;
++      }
+ }
+ static struct vc4_hvs_dlist_allocation *
diff --git a/target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch b/target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch
new file mode 100644 (file)
index 0000000..704468b
--- /dev/null
@@ -0,0 +1,100 @@
+From bcf02f6ac0d429a425e8409f140bd875a1feed2e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 13:46:53 +0200
+Subject: [PATCH] drm/vc4: hvs: Test if the EOF interrupts are enabled
+
+We currently enable the EOF interrupts through the CRTC destroy_state
+implementation.
+
+However, nothing guarantees that we can't call destroy_state multiple
+times in a row, and therefore before the EOF interrupt even happens.
+
+This means we would enable the interrupt multiple times but disable it
+only once. It wasn't an issue so far since the interrupts were only
+enabled by setting a bit in a register, but with BCM2712 we will use an
+external interrupt controller, with a refcounted interrupt.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h |  8 ++++++--
+ drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++++++--
+ 2 files changed, 18 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -333,6 +333,8 @@ struct vc4_v3d {
+       struct debugfs_regset32 regset;
+ };
++#define HVS_NUM_CHANNELS 3
++
+ struct vc4_hvs {
+       struct vc4_dev *vc4;
+       struct platform_device *pdev;
+@@ -341,6 +343,10 @@ struct vc4_hvs {
+       struct clk *core_clk;
++      struct {
++              unsigned int enabled: 1;
++      } eof_irq[HVS_NUM_CHANNELS];
++
+       unsigned long max_core_rate;
+       /* Memory manager for CRTCs to allocate space in the display
+@@ -373,8 +379,6 @@ struct vc4_hvs {
+       bool vc5_hdmi_enable_4096by2160;
+ };
+-#define HVS_NUM_CHANNELS 3
+-
+ struct vc4_hvs_state {
+       struct drm_private_state base;
+       unsigned long core_clock_rate;
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -412,11 +412,14 @@ static void vc5_hvs_update_gamma_lut(str
+       vc5_hvs_lut_load(hvs, vc4_crtc);
+ }
+-static void vc4_hvs_irq_enable_eof(const struct vc4_hvs *hvs,
++static void vc4_hvs_irq_enable_eof(struct vc4_hvs *hvs,
+                                  unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
++      if (hvs->eof_irq[channel].enabled)
++              return;
++
+       switch (vc4->gen) {
+       case VC4_GEN_4:
+               HVS_WRITE(SCALER_DISPCTRL,
+@@ -433,13 +436,18 @@ static void vc4_hvs_irq_enable_eof(const
+       default:
+               break;
+       }
++
++      hvs->eof_irq[channel].enabled = true;
+ }
+-static void vc4_hvs_irq_clear_eof(const struct vc4_hvs *hvs,
++static void vc4_hvs_irq_clear_eof(struct vc4_hvs *hvs,
+                                 unsigned int channel)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
++      if (!hvs->eof_irq[channel].enabled)
++              return;
++
+       switch (vc4->gen) {
+       case VC4_GEN_4:
+               HVS_WRITE(SCALER_DISPCTRL,
+@@ -456,6 +464,8 @@ static void vc4_hvs_irq_clear_eof(const
+       default:
+               break;
+       }
++
++      hvs->eof_irq[channel].enabled = false;
+ }
+ static struct vc4_hvs_dlist_allocation *
diff --git a/target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch b/target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch
new file mode 100644 (file)
index 0000000..5b748b5
--- /dev/null
@@ -0,0 +1,188 @@
+From f3c84bb53107cef0009347d071c1a188ce24b8a3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 14:36:28 +0100
+Subject: [PATCH] drm/vc4: hvs: Create hw_init function
+
+Since the BCM2712 will feature a significantly different HVS, let's move
+the hardware initialisation part of our bind function into a separate
+function.
+
+That way, it will be easier to extend in the future.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 155 ++++++++++++++++++----------------
+ 1 file changed, 83 insertions(+), 72 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1291,79 +1291,10 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+       return hvs;
+ }
+-static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
++static int vc4_hvs_hw_init(struct vc4_hvs *hvs)
+ {
+-      struct platform_device *pdev = to_platform_device(dev);
+-      struct drm_device *drm = dev_get_drvdata(master);
+-      struct vc4_dev *vc4 = to_vc4_dev(drm);
+-      struct vc4_hvs *hvs = NULL;
+-      int ret;
+-      u32 dispctrl;
+-      u32 reg, top;
+-
+-      hvs = __vc4_hvs_alloc(vc4, NULL);
+-      if (IS_ERR(hvs))
+-              return PTR_ERR(hvs);
+-
+-      hvs->regs = vc4_ioremap_regs(pdev, 0);
+-      if (IS_ERR(hvs->regs))
+-              return PTR_ERR(hvs->regs);
+-
+-      hvs->regset.base = hvs->regs;
+-      hvs->regset.regs = hvs_regs;
+-      hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+-
+-      if (vc4->gen == VC4_GEN_5) {
+-              struct rpi_firmware *firmware;
+-              struct device_node *node;
+-              unsigned int max_rate;
+-
+-              node = rpi_firmware_find_node();
+-              if (!node)
+-                      return -EINVAL;
+-
+-              firmware = rpi_firmware_get(node);
+-              of_node_put(node);
+-              if (!firmware)
+-                      return -EPROBE_DEFER;
+-
+-              hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
+-              if (IS_ERR(hvs->core_clk)) {
+-                      dev_err(&pdev->dev, "Couldn't get core clock\n");
+-                      return PTR_ERR(hvs->core_clk);
+-              }
+-
+-              max_rate = rpi_firmware_clk_get_max_rate(firmware,
+-                                                       RPI_FIRMWARE_CORE_CLK_ID);
+-              rpi_firmware_put(firmware);
+-              if (max_rate >= 550000000)
+-                      hvs->vc5_hdmi_enable_hdmi_20 = true;
+-
+-              if (max_rate >= 600000000)
+-                      hvs->vc5_hdmi_enable_4096by2160 = true;
+-
+-              hvs->max_core_rate = max_rate;
+-
+-              ret = clk_prepare_enable(hvs->core_clk);
+-              if (ret) {
+-                      dev_err(&pdev->dev, "Couldn't enable the core clock\n");
+-                      return ret;
+-              }
+-      }
+-
+-      if (vc4->gen == VC4_GEN_4)
+-              hvs->dlist = hvs->regs + SCALER_DLIST_START;
+-      else
+-              hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+-
+-      /* Upload filter kernels.  We only have the one for now, so we
+-       * keep it around for the lifetime of the driver.
+-       */
+-      ret = vc4_hvs_upload_linear_kernel(hvs,
+-                                         &hvs->mitchell_netravali_filter,
+-                                         mitchell_netravali_1_3_1_3_kernel);
+-      if (ret)
+-              return ret;
++      struct vc4_dev *vc4 = hvs->vc4;
++      u32 dispctrl, reg;
+       reg = HVS_READ(SCALER_DISPECTRL);
+       reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
+@@ -1445,6 +1376,86 @@ static int vc4_hvs_bind(struct device *d
+       HVS_WRITE(SCALER_DISPCTRL, dispctrl);
++      return 0;
++}
++
++static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
++{
++      struct platform_device *pdev = to_platform_device(dev);
++      struct drm_device *drm = dev_get_drvdata(master);
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
++      struct vc4_hvs *hvs = NULL;
++      int ret;
++      u32 reg, top;
++
++      hvs = __vc4_hvs_alloc(vc4, NULL);
++      if (IS_ERR(hvs))
++              return PTR_ERR(hvs);
++
++      hvs->regs = vc4_ioremap_regs(pdev, 0);
++      if (IS_ERR(hvs->regs))
++              return PTR_ERR(hvs->regs);
++
++      hvs->regset.base = hvs->regs;
++      hvs->regset.regs = hvs_regs;
++      hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
++
++      if (vc4->gen == VC4_GEN_5) {
++              struct rpi_firmware *firmware;
++              struct device_node *node;
++              unsigned int max_rate;
++
++              node = rpi_firmware_find_node();
++              if (!node)
++                      return -EINVAL;
++
++              firmware = rpi_firmware_get(node);
++              of_node_put(node);
++              if (!firmware)
++                      return -EPROBE_DEFER;
++
++              hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++              if (IS_ERR(hvs->core_clk)) {
++                      dev_err(&pdev->dev, "Couldn't get core clock\n");
++                      return PTR_ERR(hvs->core_clk);
++              }
++
++              max_rate = rpi_firmware_clk_get_max_rate(firmware,
++                                                       RPI_FIRMWARE_CORE_CLK_ID);
++              rpi_firmware_put(firmware);
++              if (max_rate >= 550000000)
++                      hvs->vc5_hdmi_enable_hdmi_20 = true;
++
++              if (max_rate >= 600000000)
++                      hvs->vc5_hdmi_enable_4096by2160 = true;
++
++              hvs->max_core_rate = max_rate;
++
++              ret = clk_prepare_enable(hvs->core_clk);
++              if (ret) {
++                      dev_err(&pdev->dev, "Couldn't enable the core clock\n");
++                      return ret;
++              }
++      }
++
++      if (vc4->gen == VC4_GEN_4)
++              hvs->dlist = hvs->regs + SCALER_DLIST_START;
++      else
++              hvs->dlist = hvs->regs + SCALER5_DLIST_START;
++
++      /* Upload filter kernels.  We only have the one for now, so we
++       * keep it around for the lifetime of the driver.
++       */
++      ret = vc4_hvs_upload_linear_kernel(hvs,
++                                         &hvs->mitchell_netravali_filter,
++                                         mitchell_netravali_1_3_1_3_kernel);
++      if (ret)
++              return ret;
++
++      ret = vc4_hvs_hw_init(hvs);
++      if (ret)
++              return ret;
++
+       /* Recompute Composite Output Buffer (COB) allocations for the displays
+        */
+       if (vc4->gen == VC4_GEN_4) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch b/target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch
new file mode 100644 (file)
index 0000000..5b2e974
--- /dev/null
@@ -0,0 +1,167 @@
+From 99a13ce3a12303dfb54815637972627a7d207086 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:14:55 +0100
+Subject: [PATCH] drm/vc4: hvs: Create cob_init function
+
+Just like the HVS itself, the COB parameters will be fairly different in
+the BCM2712.
+
+Let's move the COB parameters computation and its initialisation to a
+separate function that will be easier to extend in the future.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 128 ++++++++++++++++++++--------------
+ 1 file changed, 74 insertions(+), 54 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1379,6 +1379,77 @@ static int vc4_hvs_hw_init(struct vc4_hv
+       return 0;
+ }
++static int vc4_hvs_cob_init(struct vc4_hvs *hvs)
++{
++      struct vc4_dev *vc4 = hvs->vc4;
++      u32 reg, top;
++
++      /*
++       * Recompute Composite Output Buffer (COB) allocations for the
++       * displays
++       */
++      switch (vc4->gen) {
++      case VC4_GEN_4:
++              /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
++               * The bottom 2048 pixels are full 32bpp RGBA (intended for the
++               * TXP composing RGBA to memory), whilst the remainder are only
++               * 24bpp RGB.
++               *
++               * Assign 3 lines to channels 1 & 2, and just over 4 lines to
++               * channel 0.
++               */
++              #define VC4_COB_SIZE            20736
++              #define VC4_COB_LINE_WIDTH      2048
++              #define VC4_COB_NUM_LINES       3
++              reg = 0;
++              top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
++              reg |= (top - 1) << 16;
++              HVS_WRITE(SCALER_DISPBASE2, reg);
++              reg = top;
++              top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
++              reg |= (top - 1) << 16;
++              HVS_WRITE(SCALER_DISPBASE1, reg);
++              reg = top;
++              top = VC4_COB_SIZE;
++              reg |= (top - 1) << 16;
++              HVS_WRITE(SCALER_DISPBASE0, reg);
++              break;
++
++      case VC4_GEN_5:
++              /* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
++               * The bottom 4096 pixels are full RGBA (intended for the TXP
++               * composing RGBA to memory), whilst the remainder are only
++               * RGB. Addressing is always pixel wide.
++               *
++               * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
++               * lines. to channel 0.
++               */
++              #define VC5_COB_SIZE            44416
++              #define VC5_COB_LINE_WIDTH      4096
++              #define VC5_COB_NUM_LINES       3
++              reg = 0;
++              top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
++              reg |= top << 16;
++              HVS_WRITE(SCALER_DISPBASE2, reg);
++              top += 16;
++              reg = top;
++              top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
++              reg |= top << 16;
++              HVS_WRITE(SCALER_DISPBASE1, reg);
++              top += 16;
++              reg = top;
++              top = VC5_COB_SIZE;
++              reg |= top << 16;
++              HVS_WRITE(SCALER_DISPBASE0, reg);
++              break;
++
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
+ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
+ {
+       struct platform_device *pdev = to_platform_device(dev);
+@@ -1386,7 +1457,6 @@ static int vc4_hvs_bind(struct device *d
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_hvs *hvs = NULL;
+       int ret;
+-      u32 reg, top;
+       hvs = __vc4_hvs_alloc(vc4, NULL);
+       if (IS_ERR(hvs))
+@@ -1456,59 +1526,9 @@ static int vc4_hvs_bind(struct device *d
+       if (ret)
+               return ret;
+-      /* Recompute Composite Output Buffer (COB) allocations for the displays
+-       */
+-      if (vc4->gen == VC4_GEN_4) {
+-              /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
+-               * The bottom 2048 pixels are full 32bpp RGBA (intended for the
+-               * TXP composing RGBA to memory), whilst the remainder are only
+-               * 24bpp RGB.
+-               *
+-               * Assign 3 lines to channels 1 & 2, and just over 4 lines to
+-               * channel 0.
+-               */
+-              #define VC4_COB_SIZE            20736
+-              #define VC4_COB_LINE_WIDTH      2048
+-              #define VC4_COB_NUM_LINES       3
+-              reg = 0;
+-              top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+-              reg |= (top - 1) << 16;
+-              HVS_WRITE(SCALER_DISPBASE2, reg);
+-              reg = top;
+-              top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+-              reg |= (top - 1) << 16;
+-              HVS_WRITE(SCALER_DISPBASE1, reg);
+-              reg = top;
+-              top = VC4_COB_SIZE;
+-              reg |= (top - 1) << 16;
+-              HVS_WRITE(SCALER_DISPBASE0, reg);
+-      } else {
+-              /* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
+-               * The bottom 4096 pixels are full RGBA (intended for the TXP
+-               * composing RGBA to memory), whilst the remainder are only
+-               * RGB. Addressing is always pixel wide.
+-               *
+-               * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
+-               * lines. to channel 0.
+-               */
+-              #define VC5_COB_SIZE            44416
+-              #define VC5_COB_LINE_WIDTH      4096
+-              #define VC5_COB_NUM_LINES       3
+-              reg = 0;
+-              top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+-              reg |= top << 16;
+-              HVS_WRITE(SCALER_DISPBASE2, reg);
+-              top += 16;
+-              reg = top;
+-              top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+-              reg |= top << 16;
+-              HVS_WRITE(SCALER_DISPBASE1, reg);
+-              top += 16;
+-              reg = top;
+-              top = VC5_COB_SIZE;
+-              reg |= top << 16;
+-              HVS_WRITE(SCALER_DISPBASE0, reg);
+-      }
++      ret = vc4_hvs_cob_init(hvs);
++      if (ret)
++              return ret;
+       ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+                              vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch b/target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch
new file mode 100644 (file)
index 0000000..4139d79
--- /dev/null
@@ -0,0 +1,38 @@
+From 7a1c157ac856384c47df38e1de2995f55a111b85 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:41:59 +0100
+Subject: [PATCH] drm/vc4: hvs: Rename hvs_regs list
+
+The HVS register set has been heavily modified in the BCM2712, and we'll
+thus need a separate debugfs_reg32 array for it.
+
+The name hvs_regs is thus a bit too generic, so let's rename it to
+something more specific.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -33,7 +33,7 @@
+ #include "vc4_drv.h"
+ #include "vc4_regs.h"
+-static const struct debugfs_reg32 hvs_regs[] = {
++static const struct debugfs_reg32 vc4_hvs_regs[] = {
+       VC4_REG32(SCALER_DISPCTRL),
+       VC4_REG32(SCALER_DISPSTAT),
+       VC4_REG32(SCALER_DISPID),
+@@ -1467,8 +1467,8 @@ static int vc4_hvs_bind(struct device *d
+               return PTR_ERR(hvs->regs);
+       hvs->regset.base = hvs->regs;
+-      hvs->regset.regs = hvs_regs;
+-      hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
++      hvs->regset.regs = vc4_hvs_regs;
++      hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
+       if (vc4->gen == VC4_GEN_5) {
+               struct rpi_firmware *firmware;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch b/target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch
new file mode 100644 (file)
index 0000000..ed55f53
--- /dev/null
@@ -0,0 +1,103 @@
+From 531f66804eb95323f807d240273087fbe162aeee Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 09:56:31 +0100
+Subject: [PATCH] drm/vc4: plane: Change ptr0_offset to an array
+
+The BCM2712 will have a fairly different dlist, that will feature one
+Pointer 0 word for each plane.
+
+Let's prepare by changing the ptr0_offset variable that holds the offset
+in a dlist of the pointer 0 word to an array.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h   |  3 ++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 18 +++++++++---------
+ 2 files changed, 11 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -14,6 +14,7 @@
+ #include <drm/drm_debugfs.h>
+ #include <drm/drm_device.h>
+ #include <drm/drm_encoder.h>
++#include <drm/drm_fourcc.h>
+ #include <drm/drm_gem_dma_helper.h>
+ #include <drm/drm_managed.h>
+ #include <drm/drm_mm.h>
+@@ -430,7 +431,7 @@ struct vc4_plane_state {
+        */
+       u32 pos0_offset;
+       u32 pos2_offset;
+-      u32 ptr0_offset;
++      u32 ptr0_offset[DRM_FORMAT_MAX_PLANES];
+       u32 lbm_offset;
+       /* Offset where the plane's dlist was last stored in the
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1242,7 +1242,7 @@ static int vc4_plane_mode_set(struct drm
+        *
+        * The pointers may be any byte address.
+        */
+-      vc4_state->ptr0_offset = vc4_state->dlist_count;
++      vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+       for (i = 0; i < num_planes; i++)
+               vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
+@@ -1447,13 +1447,13 @@ void vc4_plane_async_set_fb(struct drm_p
+        * scanout will start from this address as soon as the FIFO
+        * needs to refill with pixels.
+        */
+-      writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
++      writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
+       /* Also update the CPU-side dlist copy, so that any later
+        * atomic updates that don't do a new modeset on our plane
+        * also use our updated address.
+        */
+-      vc4_state->dlist[vc4_state->ptr0_offset] = addr;
++      vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
+       drm_dev_exit(idx);
+ }
+@@ -1517,8 +1517,8 @@ static void vc4_plane_atomic_async_updat
+               new_vc4_state->dlist[vc4_state->pos0_offset];
+       vc4_state->dlist[vc4_state->pos2_offset] =
+               new_vc4_state->dlist[vc4_state->pos2_offset];
+-      vc4_state->dlist[vc4_state->ptr0_offset] =
+-              new_vc4_state->dlist[vc4_state->ptr0_offset];
++      vc4_state->dlist[vc4_state->ptr0_offset[0]] =
++              new_vc4_state->dlist[vc4_state->ptr0_offset[0]];
+       /* Note that we can't just call vc4_plane_write_dlist()
+        * because that would smash the context data that the HVS is
+@@ -1528,8 +1528,8 @@ static void vc4_plane_atomic_async_updat
+              &vc4_state->hw_dlist[vc4_state->pos0_offset]);
+       writel(vc4_state->dlist[vc4_state->pos2_offset],
+              &vc4_state->hw_dlist[vc4_state->pos2_offset]);
+-      writel(vc4_state->dlist[vc4_state->ptr0_offset],
+-             &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
++      writel(vc4_state->dlist[vc4_state->ptr0_offset[0]],
++             &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
+       drm_dev_exit(idx);
+ }
+@@ -1556,7 +1556,7 @@ static int vc4_plane_atomic_async_check(
+       if (old_vc4_state->dlist_count != new_vc4_state->dlist_count ||
+           old_vc4_state->pos0_offset != new_vc4_state->pos0_offset ||
+           old_vc4_state->pos2_offset != new_vc4_state->pos2_offset ||
+-          old_vc4_state->ptr0_offset != new_vc4_state->ptr0_offset ||
++          old_vc4_state->ptr0_offset[0] != new_vc4_state->ptr0_offset[0] ||
+           vc4_lbm_size(plane->state) != vc4_lbm_size(new_plane_state))
+               return -EINVAL;
+@@ -1566,7 +1566,7 @@ static int vc4_plane_atomic_async_check(
+       for (i = 0; i < new_vc4_state->dlist_count; i++) {
+               if (i == new_vc4_state->pos0_offset ||
+                   i == new_vc4_state->pos2_offset ||
+-                  i == new_vc4_state->ptr0_offset ||
++                  i == new_vc4_state->ptr0_offset[0] ||
+                   (new_vc4_state->lbm_offset &&
+                    i == new_vc4_state->lbm_offset))
+                       continue;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch b/target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch
new file mode 100644 (file)
index 0000000..3a30b9c
--- /dev/null
@@ -0,0 +1,45 @@
+From 2834b58a3b58198e551d8461a0786b75d3d76823 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Apr 2023 10:12:19 +0200
+Subject: [PATCH] drm/vc4: hvs: Rework LBM alignment
+
+With the introduction of the support for BCM2712, the check of whether
+we're running on vc5 or not to compute the LBM alignment requirement
+doesn't work anymore.
+
+Moreover, the LBM size will need to be computed in words for the
+BCM2712, while we've had sizes in bytes so far.
+
+Aligning on either 64 or 32 words is thus fairly harmful on BCM2712, so
+let's just explicitly align the size when needed, and then call
+drm_mm_insert_node_generic() with an alignment of 1.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -744,6 +744,11 @@ static int vc4_plane_allocate_lbm(struct
+       if (!lbm_size)
+               return 0;
++      if (vc4->gen == VC4_GEN_5)
++              lbm_size = ALIGN(lbm_size, 64);
++      else if (vc4->gen == VC4_GEN_4)
++              lbm_size = ALIGN(lbm_size, 32);
++
+       drm_dbg_driver(drm, "[PLANE:%d:%s] LBM Allocation Size: %u\n",
+                      plane->base.id, plane->name, lbm_size);
+@@ -759,8 +764,7 @@ static int vc4_plane_allocate_lbm(struct
+               spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
+               ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+                                                &vc4_state->lbm,
+-                                               lbm_size,
+-                                               vc4->gen == VC4_GEN_5 ? 64 : 32,
++                                               lbm_size, 1,
+                                                0, 0);
+               spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch b/target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch
new file mode 100644 (file)
index 0000000..2927a3e
--- /dev/null
@@ -0,0 +1,93 @@
+From 23cba2abd5ffbb7337e3f381c4724eaaf01cf5a1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 15:45:50 +0100
+Subject: [PATCH] drm/vc4: hvs: Change prototype of __vc4_hvs_alloc to pass
+ registers
+
+The BCM2712 HVS has registers to report the size of the various SRAM the
+driver uses, and their size actually differ depending on the stepping.
+
+The initialisation of the memory pools happen in the __vc4_hvs_alloc()
+function that also allocates the main HVS structure, that will then hold
+the pointer to the memory mapping of the registers.
+
+This creates some kind of circular dependency that we can break by
+passing the mapping pointer as an argument for __vc4_hvs_alloc() to use
+to query to get the SRAM sizes and initialise the memory pools
+accordingly.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c |  2 +-
+ drivers/gpu/drm/vc4/vc4_drv.h        |  4 +++-
+ drivers/gpu/drm/vc4/vc4_hvs.c        | 16 ++++++++++------
+ 3 files changed, 14 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -173,7 +173,7 @@ static struct vc4_dev *__mock_device(str
+       vc4->dev = dev;
+       vc4->gen = gen;
+-      vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
++      vc4->hvs = __vc4_hvs_alloc(vc4, NULL, NULL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
+       drm = &vc4->base;
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -1092,7 +1092,9 @@ void vc4_irq_reset(struct drm_device *de
+ /* vc4_hvs.c */
+ extern struct platform_driver vc4_hvs_driver;
+-struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev);
++struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4,
++                              void __iomem *regs,
++                              struct platform_device *pdev);
+ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int output);
+ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output);
+ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo);
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1248,7 +1248,9 @@ int vc4_hvs_debugfs_init(struct drm_mino
+       return 0;
+ }
+-struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev)
++struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4,
++                              void __iomem *regs,
++                              struct platform_device *pdev)
+ {
+       struct drm_device *drm = &vc4->base;
+       struct vc4_hvs *hvs;
+@@ -1258,6 +1260,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+               return ERR_PTR(-ENOMEM);
+       hvs->vc4 = vc4;
++      hvs->regs = regs;
+       hvs->pdev = pdev;
+       spin_lock_init(&hvs->mm_lock);
+@@ -1456,16 +1459,17 @@ static int vc4_hvs_bind(struct device *d
+       struct drm_device *drm = dev_get_drvdata(master);
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_hvs *hvs = NULL;
++      void __iomem *regs;
+       int ret;
+-      hvs = __vc4_hvs_alloc(vc4, NULL);
++      regs = vc4_ioremap_regs(pdev, 0);
++      if (IS_ERR(regs))
++              return PTR_ERR(regs);
++
++      hvs = __vc4_hvs_alloc(vc4, regs, pdev);
+       if (IS_ERR(hvs))
+               return PTR_ERR(hvs);
+-      hvs->regs = vc4_ioremap_regs(pdev, 0);
+-      if (IS_ERR(hvs->regs))
+-              return PTR_ERR(hvs->regs);
+-
+       hvs->regset.base = hvs->regs;
+       hvs->regset.regs = vc4_hvs_regs;
+       hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch b/target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch
new file mode 100644 (file)
index 0000000..4a51862
--- /dev/null
@@ -0,0 +1,30 @@
+From 03e0e348df7b685439f2db61ec2d8c8da25d1217 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 23 Aug 2023 17:48:23 +0100
+Subject: [PATCH] drm/vc4: UV planes vertical scaling must always be enabled
+
+It has been observed that a YUV422 unity scaled plane isn't displayed.
+Enabling vertical scaling on the UV planes solves this. There is
+already a similar clause to always enable horizontal scaling on the
+UV planes.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -522,6 +522,12 @@ static int vc4_plane_setup_clipping_and_
+                */
+               if (vc4_state->x_scaling[1] == VC4_SCALING_NONE)
+                       vc4_state->x_scaling[1] = VC4_SCALING_PPF;
++
++              /* Similarly UV needs vertical scaling to be enabled.
++               * Without this a 1:1 scaled YUV422 plane isn't rendered.
++               */
++              if (vc4_state->y_scaling[1] == VC4_SCALING_NONE)
++                      vc4_state->y_scaling[1] = VC4_SCALING_PPF;
+       } else {
+               vc4_state->is_yuv = false;
+               vc4_state->x_scaling[1] = VC4_SCALING_NONE;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch b/target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch
new file mode 100644 (file)
index 0000000..8e36b79
--- /dev/null
@@ -0,0 +1,39 @@
+From e5e7679d634b10e88df340c85cd8368c9f9989eb Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 5 Sep 2023 19:38:24 +0100
+Subject: [PATCH] drm/vc4: hdmi: Avoid hang with debug registers when suspended
+
+Trying to read /sys/kernel/debug/dri/1/hdmi1_regs
+when the hdmi is disconnected results in a fatal system hang.
+
+This is due to the pm suspend code disabling the dvp clock.
+That is just a gate of the 108MHz clock in DVP_HT_RPI_MISC_CONFIG,
+which results in accesses hanging AXI bus.
+
+Protect against this.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -193,6 +193,8 @@ static int vc4_hdmi_debugfs_regs(struct
+       if (!drm_dev_enter(drm, &idx))
+               return -ENODEV;
++      WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev));
++
+       drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
+       drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+       drm_print_regset32(&p, &vc4_hdmi->cec_regset);
+@@ -202,6 +204,8 @@ static int vc4_hdmi_debugfs_regs(struct
+       drm_print_regset32(&p, &vc4_hdmi->ram_regset);
+       drm_print_regset32(&p, &vc4_hdmi->rm_regset);
++      pm_runtime_put(&vc4_hdmi->pdev->dev);
++
+       drm_dev_exit(idx);
+       return 0;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch b/target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch
new file mode 100644 (file)
index 0000000..eeb2a93
--- /dev/null
@@ -0,0 +1,154 @@
+From 11cf37e741b439b26fe932750bde841a16a96828 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 25 Sep 2023 16:57:07 +0100
+Subject: [PATCH] drm/vc4: Move the buffer offset out of the vc4_plane_state
+
+The offset fields in vc4_plane_state are described as being
+the offset for each buffer in the bo, however it is used to
+store the complete DMA address that is then written into the
+register.
+
+The DMA address including the fb ofset  can be retrieved
+using drm_fb_dma_get_gem_addr, and the offset adjustment due to
+clipping is local to vc4_plane_mode_set.
+Drop the offset field from the state, and compute the complete
+DMA address in vc4_plane_mode_set.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h   |  5 ----
+ drivers/gpu/drm/vc4/vc4_plane.c | 51 +++++++++++++--------------------
+ 2 files changed, 20 insertions(+), 36 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -451,11 +451,6 @@ struct vc4_plane_state {
+       bool is_unity;
+       bool is_yuv;
+-      /* Offset to start scanning out from the start of the plane's
+-       * BO.
+-       */
+-      u32 offsets[3];
+-
+       /* Our allocation in LBM for temporary storage during scaling. */
+       struct drm_mm_node lbm;
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -450,12 +450,11 @@ static int vc4_plane_setup_clipping_and_
+ {
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+       struct drm_framebuffer *fb = state->fb;
+-      struct drm_gem_dma_object *bo;
+       int num_planes = fb->format->num_planes;
+       struct drm_crtc_state *crtc_state;
+       u32 h_subsample = fb->format->hsub;
+       u32 v_subsample = fb->format->vsub;
+-      int i, ret;
++      int ret;
+       crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+                                                       state->crtc);
+@@ -469,11 +468,6 @@ static int vc4_plane_setup_clipping_and_
+       if (ret)
+               return ret;
+-      for (i = 0; i < num_planes; i++) {
+-              bo = drm_fb_dma_get_gem_obj(fb, i);
+-              vc4_state->offsets[i] = bo->dma_addr + fb->offsets[i];
+-      }
+-
+       vc4_state->src_x = state->src.x1;
+       vc4_state->src_y = state->src.y1;
+       vc4_state->src_w[0] = state->src.x2 - vc4_state->src_x;
+@@ -896,6 +890,7 @@ static int vc4_plane_mode_set(struct drm
+       u32 width, height;
+       u32 hvs_format = format->hvs;
+       unsigned int rotation;
++      u32 offsets[3] = { 0 };
+       int ret, i;
+       if (vc4_state->dlist_initialized)
+@@ -943,13 +938,8 @@ static int vc4_plane_mode_set(struct drm
+                * out.
+                */
+               for (i = 0; i < num_planes; i++) {
+-                      vc4_state->offsets[i] += src_y /
+-                                               (i ? v_subsample : 1) *
+-                                               fb->pitches[i];
+-
+-                      vc4_state->offsets[i] += src_x /
+-                                               (i ? h_subsample : 1) *
+-                                               fb->format->cpp[i];
++                      offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
++                      offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
+               }
+               break;
+@@ -1004,19 +994,18 @@ static int vc4_plane_mode_set(struct drm
+                          VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) |
+                          VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) |
+                          VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));
+-              vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift);
+-              vc4_state->offsets[0] += subtile_y << 8;
+-              vc4_state->offsets[0] += utile_y << 4;
++              offsets[0] += tiles_t * (tiles_w << tile_size_shift);
++              offsets[0] += subtile_y << 8;
++              offsets[0] += utile_y << 4;
+               /* Rows of tiles alternate left-to-right and right-to-left. */
+               if (tiles_t & 1) {
+                       pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR;
+-                      vc4_state->offsets[0] += (tiles_w - tiles_l) <<
+-                                               tile_size_shift;
+-                      vc4_state->offsets[0] -= (1 + !tile_y) << 10;
++                      offsets[0] += (tiles_w - tiles_l) << tile_size_shift;
++                      offsets[0] -= (1 + !tile_y) << 10;
+               } else {
+-                      vc4_state->offsets[0] += tiles_l << tile_size_shift;
+-                      vc4_state->offsets[0] += tile_y << 10;
++                      offsets[0] += tiles_l << tile_size_shift;
++                      offsets[0] += tile_y << 10;
+               }
+               break;
+@@ -1105,11 +1094,9 @@ static int vc4_plane_mode_set(struct drm
+                       tile = src_x / pix_per_tile;
+-                      vc4_state->offsets[i] += param * tile_w * tile;
+-                      vc4_state->offsets[i] += src_y /
+-                                               (i ? v_subsample : 1) *
+-                                               tile_w;
+-                      vc4_state->offsets[i] += x_off & ~(i ? 1 : 0);
++                      offsets[i] += param * tile_w * tile;
++                      offsets[i] += src_y / (i ? v_subsample : 1) * tile_w;
++                      offsets[i] += x_off & ~(i ? 1 : 0);
+               }
+               pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
+@@ -1253,8 +1240,12 @@ static int vc4_plane_mode_set(struct drm
+        * The pointers may be any byte address.
+        */
+       vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+-      for (i = 0; i < num_planes; i++)
+-              vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
++
++      for (i = 0; i < num_planes; i++) {
++              dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++
++              vc4_dlist_write(vc4_state, paddr + offsets[i]);
++      }
+       /* Pointer Context Word 0/1/2: Written by the HVS */
+       for (i = 0; i < num_planes; i++)
+@@ -1518,8 +1509,6 @@ static void vc4_plane_atomic_async_updat
+              sizeof(vc4_state->y_scaling));
+       vc4_state->is_unity = new_vc4_state->is_unity;
+       vc4_state->is_yuv = new_vc4_state->is_yuv;
+-      memcpy(vc4_state->offsets, new_vc4_state->offsets,
+-             sizeof(vc4_state->offsets));
+       vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill;
+       /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch b/target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch
new file mode 100644 (file)
index 0000000..01ad932
--- /dev/null
@@ -0,0 +1,33 @@
+From 3660abb4a8523e988f1345985e89149804e50ebe Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 24 Aug 2023 15:36:21 +0100
+Subject: [PATCH] drm/vc4: Fix dlist debug not resetting the next entry pointer
+
+The debug function to display the dlists didn't reset next_entry_start
+when starting each display, so resulting in not stopping the
+list at the correct place.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -110,7 +110,7 @@ static int vc4_hvs_debugfs_dlist(struct
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hvs *hvs = vc4->hvs;
+       struct drm_printer p = drm_seq_file_printer(m);
+-      unsigned int next_entry_start = 0;
++      unsigned int next_entry_start;
+       unsigned int i, j;
+       u32 dlist_word, dispstat;
+@@ -124,6 +124,7 @@ static int vc4_hvs_debugfs_dlist(struct
+               }
+               drm_printf(&p, "HVS chan %u:\n", i);
++              next_entry_start = 0;
+               for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
+                       dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch b/target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch
new file mode 100644 (file)
index 0000000..d41d53e
--- /dev/null
@@ -0,0 +1,57 @@
+From ebf11a4cfd9f1236fb9eeb7e32e87b18f5f56f16 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 Sep 2023 13:45:08 +0100
+Subject: [PATCH] drm: vc4: Remove incorrect limit from hvs_dlist debugfs
+ function
+
+The debugfs function to dump dlists aborted at 256 bytes,
+when actually the dlist memory is generally significantly
+larger but varies based on SoC.
+
+We already have the correct limit in __vc4_hvs_alloc, so
+store it for use in the debugfs dlist function.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 5 ++++-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -341,6 +341,7 @@ struct vc4_hvs {
+       struct platform_device *pdev;
+       void __iomem *regs;
+       u32 __iomem *dlist;
++      unsigned int dlist_mem_size;
+       struct clk *core_clk;
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -110,6 +110,7 @@ static int vc4_hvs_debugfs_dlist(struct
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hvs *hvs = vc4->hvs;
+       struct drm_printer p = drm_seq_file_printer(m);
++      unsigned int dlist_mem_size = hvs->dlist_mem_size;
+       unsigned int next_entry_start;
+       unsigned int i, j;
+       u32 dlist_word, dispstat;
+@@ -126,7 +127,7 @@ static int vc4_hvs_debugfs_dlist(struct
+               drm_printf(&p, "HVS chan %u:\n", i);
+               next_entry_start = 0;
+-              for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
++              for (j = HVS_READ(SCALER_DISPLISTX(i)); j < dlist_mem_size; j++) {
+                       dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
+                       drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
+                                  dlist_word);
+@@ -1278,6 +1279,8 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+                   HVS_BOOTLOADER_DLIST_END,
+                   (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
++      hvs->dlist_mem_size = dlist_size;
++
+       /* Set up the HVS LBM memory manager.  We could have some more
+        * complicated data structure that allowed reuse of LBM areas
+        * between planes when they don't overlap on the screen, but
diff --git a/target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch b/target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch
new file mode 100644 (file)
index 0000000..640b0ea
--- /dev/null
@@ -0,0 +1,50 @@
+From 712bccec241e84e28ccb725fae87d3255d039f42 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 22 Jun 2023 14:06:40 +0100
+Subject: [PATCH] drm/vc4: hvs: Remove ABORT_ON_EMPTY flag
+
+ABORT_ON_EMPTY chooses whether the HVS abandons the current frame
+when it experiences an underflow, or attempts to continue.
+
+In theory the frame should be black from the point of underflow,
+compared to a shift of sebsequent pixels to the left.
+
+Unfortunately it seems to put the HVS is a bad state where it is not
+possible to recover simply. This typically requires a reboot
+following the 'flip done timed out message'.
+
+Discussion with Broadcom has suggested we don't use this flag.
+All their testing is done with it disabled.
+
+Additionally setting BLANK_INSERT_EN causes the HDMI to output
+blank pixels on an underflow which avoids it losing sync.
+
+After this change a 'flip done timed out' due to sdram bandwidth
+starvation or too low a clock is recoverable once the situation improves.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 1 +
+ drivers/gpu/drm/vc4/vc4_regs.h | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1866,6 +1866,7 @@ static void vc4_hdmi_encoder_post_crtc_e
+                  VC4_HD_VID_CTL_CLRRGB |
+                  VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+                  VC4_HD_VID_CTL_FRAME_COUNTER_RESET |
++                 VC4_HD_VID_CTL_BLANK_INSERT_EN |
+                  (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+                  (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -799,6 +799,7 @@ enum {
+ # define VC4_HD_VID_CTL_CLRSYNC                       BIT(24)
+ # define VC4_HD_VID_CTL_CLRRGB                        BIT(23)
+ # define VC4_HD_VID_CTL_BLANKPIX              BIT(18)
++# define VC4_HD_VID_CTL_BLANK_INSERT_EN               BIT(16)
+ # define VC4_HD_CSC_CTL_ORDER_MASK            VC4_MASK(7, 5)
+ # define VC4_HD_CSC_CTL_ORDER_SHIFT           5
diff --git a/target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch b/target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch
new file mode 100644 (file)
index 0000000..3060c02
--- /dev/null
@@ -0,0 +1,59 @@
+From 542ba979b4fa1e07ff2ad2dabbdc12e92b80ed46 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Thu, 13 Jul 2023 17:47:22 +0100
+Subject: [PATCH] drm/vc4: Enable SCALER_CONTROL early in HVS init
+
+Always enable SCALER_CONTROL before attempting other HVS
+operations. It's safe to write to some parts of the HVS but
+in general it's dangerous to do this because it can cause bus
+lockups.
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1303,6 +1303,10 @@ static int vc4_hvs_hw_init(struct vc4_hv
+       struct vc4_dev *vc4 = hvs->vc4;
+       u32 dispctrl, reg;
++      dispctrl = HVS_READ(SCALER_DISPCTRL);
++      dispctrl |= SCALER_DISPCTRL_ENABLE;
++      HVS_WRITE(SCALER_DISPCTRL, dispctrl);
++
+       reg = HVS_READ(SCALER_DISPECTRL);
+       reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
+       HVS_WRITE(SCALER_DISPECTRL,
+@@ -1324,8 +1328,6 @@ static int vc4_hvs_hw_init(struct vc4_hv
+                 reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX));
+       dispctrl = HVS_READ(SCALER_DISPCTRL);
+-
+-      dispctrl |= SCALER_DISPCTRL_ENABLE;
+       dispctrl |= SCALER_DISPCTRL_DISPEIRQ(0) |
+                   SCALER_DISPCTRL_DISPEIRQ(1) |
+                   SCALER_DISPCTRL_DISPEIRQ(2);
+@@ -1521,6 +1523,10 @@ static int vc4_hvs_bind(struct device *d
+       else
+               hvs->dlist = hvs->regs + SCALER5_DLIST_START;
++      ret = vc4_hvs_hw_init(hvs);
++      if (ret)
++              return ret;
++
+       /* Upload filter kernels.  We only have the one for now, so we
+        * keep it around for the lifetime of the driver.
+        */
+@@ -1530,10 +1536,6 @@ static int vc4_hvs_bind(struct device *d
+       if (ret)
+               return ret;
+-      ret = vc4_hvs_hw_init(hvs);
+-      if (ret)
+-              return ret;
+-
+       ret = vc4_hvs_cob_init(hvs);
+       if (ret)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch
new file mode 100644 (file)
index 0000000..dab1fff
--- /dev/null
@@ -0,0 +1,26 @@
+From aed3dadaa6fb4c38275b264ecc0ff5ebe0408b82 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:02 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 HDMI bindings
+
+The BCM2712 HDMI controller uses a slightly different HDMI controller
+than the BCM2711, and a completely different PHY.
+
+Let's introduce a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2711-hdmi.yaml          | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
+@@ -14,6 +14,8 @@ properties:
+     enum:
+       - brcm,bcm2711-hdmi0
+       - brcm,bcm2711-hdmi1
++      - brcm,bcm2712-hdmi0
++      - brcm,bcm2712-hdmi1
+   reg:
+     items:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch
new file mode 100644 (file)
index 0000000..e281007
--- /dev/null
@@ -0,0 +1,34 @@
+From 2f5a75f9687553d6e91fcc09b233f5f6176b3681 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:14 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 HVS bindings
+
+The BCM2712 has a completely different HVS than the previous
+generations, so let's add a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-hvs.yaml        | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
+@@ -13,6 +13,7 @@ properties:
+   compatible:
+     enum:
+       - brcm,bcm2711-hvs
++      - brcm,bcm2712-hvs
+       - brcm,bcm2835-hvs
+   reg:
+@@ -36,7 +37,9 @@ if:
+   properties:
+     compatible:
+       contains:
+-        const: brcm,bcm2711-hvs
++        enum:
++          - brcm,bcm2711-hvs
++          - brcm,bcm2712-hvs
+ then:
+   required:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch
new file mode 100644 (file)
index 0000000..b37c127
--- /dev/null
@@ -0,0 +1,28 @@
+From 9f26d5827745df2c59e5559fd59a5045a4ad7ce0 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:27 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 PixelValve bindings
+
+The BCM2712 has 3 different pixelvalves that are similar to the ones
+found in the previous generations but with slightly different
+capabilities.
+
+Express that using a new set of compatibles.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml  | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
+@@ -20,6 +20,9 @@ properties:
+       - brcm,bcm2711-pixelvalve2
+       - brcm,bcm2711-pixelvalve3
+       - brcm,bcm2711-pixelvalve4
++      - brcm,bcm2712-pixelvalve0
++      - brcm,bcm2712-pixelvalve1
++      - brcm,bcm2712-pixelvalve2
+   reg:
+     maxItems: 1
diff --git a/target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch
new file mode 100644 (file)
index 0000000..ea53752
--- /dev/null
@@ -0,0 +1,28 @@
+From 07f90ad6a81d9ed923ee0da05541718baf49fb3c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:36 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 MOP bindings
+
+The BCM2712 has a MOP controller which is basically a new revision of
+the TXP.
+
+Express that by adding a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-txp.yaml         | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+@@ -11,7 +11,9 @@ maintainers:
+ properties:
+   compatible:
+-    const: brcm,bcm2835-txp
++    enum:
++      - brcm,bcm2712-mop
++      - brcm,bcm2835-txp
+   reg:
+     maxItems: 1
diff --git a/target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch
new file mode 100644 (file)
index 0000000..97aaf5b
--- /dev/null
@@ -0,0 +1,25 @@
+From e4a3722d08c723f1212bfbdcb52710de8340720a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:36 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 MOPLET bindings
+
+The BCM2712 has a MOPLET controller which is basically a TXP without the
+transpose feature.
+
+Express that by adding a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+@@ -13,6 +13,7 @@ properties:
+   compatible:
+     enum:
+       - brcm,bcm2712-mop
++      - brcm,bcm2712-moplet
+       - brcm,bcm2835-txp
+   reg:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch
new file mode 100644 (file)
index 0000000..420c66c
--- /dev/null
@@ -0,0 +1,23 @@
+From e66d4b49a027257c347fa57ce6972f08747c0917 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:51 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 KMS driver bindings
+
+The BCM2712 SoC comes with a new variation of the videocore display
+pipeline. Let's create a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
+@@ -18,6 +18,7 @@ properties:
+   compatible:
+     enum:
+       - brcm,bcm2711-vc5
++      - brcm,bcm2712-vc6
+       - brcm,bcm2835-vc4
+       - brcm,cygnus-vc4
diff --git a/target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch
new file mode 100644 (file)
index 0000000..57a797c
--- /dev/null
@@ -0,0 +1,47 @@
+From 847ec495822ad512dd9f1a58a85dabea01534855 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:29:52 +0100
+Subject: [PATCH] drm/vc4: drv: Support BCM2712
+
+The BCM2712 has an improved display pipeline, most notably with a
+different HVS and only HDMI and writeback outputs.
+
+Let's introduce it as a new VideoCore generation and compatible.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 5 ++++-
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -310,7 +310,9 @@ static int vc4_drm_bind(struct device *d
+       dev->coherent_dma_mask = DMA_BIT_MASK(32);
+-      if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
++      if (of_device_is_compatible(dev->of_node, "brcm,bcm2712-vc6"))
++              gen = VC4_GEN_6;
++      else if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
+               gen = VC4_GEN_5;
+       else
+               gen = VC4_GEN_4;
+@@ -475,6 +477,7 @@ static int vc4_platform_drm_remove(struc
+ static const struct of_device_id vc4_of_match[] = {
+       { .compatible = "brcm,bcm2711-vc5", },
++      { .compatible = "brcm,bcm2712-vc6", },
+       { .compatible = "brcm,bcm2835-vc4", },
+       { .compatible = "brcm,cygnus-vc4", },
+       {},
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -84,6 +84,7 @@ struct vc4_perfmon {
+ enum vc4_gen {
+       VC4_GEN_4,
+       VC4_GEN_5,
++      VC4_GEN_6,
+ };
+ struct vc4_dev {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch b/target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch
new file mode 100644 (file)
index 0000000..9659432
--- /dev/null
@@ -0,0 +1,2139 @@
+From e84da235223d0209165183c430692dde5c69854c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:25:16 +0100
+Subject: [PATCH] drm/vc4: hvs: Support BCM2712 HVS
+
+The HVS found in the BCM2712, while having a similar role, is very
+different from the one found in the previous SoCs. Indeed, the register
+layout is fairly different, and the DLIST format is new as well.
+
+Let's introduce the needed functions to support the new HVS.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c  |  47 ++-
+ drivers/gpu/drm/vc4/vc4_drv.c   |   8 +-
+ drivers/gpu/drm/vc4/vc4_drv.h   |  18 +
+ drivers/gpu/drm/vc4/vc4_hvs.c   | 626 ++++++++++++++++++++++++++++---
+ drivers/gpu/drm/vc4/vc4_kms.c   | 102 ++++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 641 +++++++++++++++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc4_regs.h  | 181 +++++++++
+ 7 files changed, 1540 insertions(+), 83 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -82,13 +82,22 @@ static unsigned int
+ vc4_crtc_get_cob_allocation(struct vc4_dev *vc4, unsigned int channel)
+ {
+       struct vc4_hvs *hvs = vc4->hvs;
+-      u32 dispbase = HVS_READ(SCALER_DISPBASEX(channel));
++      u32 dispbase, top, base;
++
+       /* Top/base are supposed to be 4-pixel aligned, but the
+        * Raspberry Pi firmware fills the low bits (which are
+        * presumably ignored).
+        */
+-      u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
+-      u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
++
++      if (vc4->gen >= VC4_GEN_6) {
++              dispbase = HVS_READ(SCALER6_DISPX_COB(channel));
++              top = VC4_GET_FIELD(dispbase, SCALER6_DISPX_COB_TOP) & ~3;
++              base = VC4_GET_FIELD(dispbase, SCALER6_DISPX_COB_BASE) & ~3;
++      } else {
++              dispbase = HVS_READ(SCALER_DISPBASEX(channel));
++              top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
++              base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
++      }
+       return top - base + 4;
+ }
+@@ -121,7 +130,10 @@ static bool vc4_crtc_get_scanout_positio
+        * Read vertical scanline which is currently composed for our
+        * pixelvalve by the HVS, and also the scaler status.
+        */
+-      val = HVS_READ(SCALER_DISPSTATX(channel));
++      if (vc4->gen >= VC4_GEN_6)
++              val = HVS_READ(SCALER6_DISPX_STATUS(channel));
++      else
++              val = HVS_READ(SCALER_DISPSTATX(channel));
+       /* Get optional system timestamp after query. */
+       if (etime)
+@@ -130,7 +142,12 @@ static bool vc4_crtc_get_scanout_positio
+       /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
+       /* Vertical position of hvs composed scanline. */
+-      *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
++
++      if (vc4->gen >= VC4_GEN_6)
++              *vpos = VC4_GET_FIELD(val, SCALER6_DISPX_STATUS_YLINE);
++      else
++              *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
++
+       *hpos = 0;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+@@ -475,8 +492,10 @@ static void require_hvs_enabled(struct d
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hvs *hvs = vc4->hvs;
+-      WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
+-                   SCALER_DISPCTRL_ENABLE);
++      if (vc4->gen >= VC4_GEN_6)
++              WARN_ON_ONCE(!(HVS_READ(SCALER6_CONTROL) & SCALER6_CONTROL_HVS_EN));
++      else
++              WARN_ON_ONCE(!(HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE));
+ }
+ static int vc4_crtc_disable(struct drm_crtc *crtc,
+@@ -804,14 +823,21 @@ static void vc4_crtc_handle_page_flip(st
+       struct drm_device *dev = crtc->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hvs *hvs = vc4->hvs;
++      unsigned int current_dlist;
+       u32 chan = vc4_crtc->current_hvs_channel;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->event_lock, flags);
+       spin_lock(&vc4_crtc->irq_lock);
++
++      if (vc4->gen >= VC4_GEN_6)
++              current_dlist = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_DL(chan)),
++                                            SCALER6_DISPX_DL_LACT);
++      else
++              current_dlist = HVS_READ(SCALER_DISPLACTX(chan));
++
+       if (vc4_crtc->event &&
+-          (vc4_crtc->current_dlist == HVS_READ(SCALER_DISPLACTX(chan)) ||
+-           vc4_crtc->feeds_txp)) {
++          (vc4_crtc->current_dlist == current_dlist || vc4_crtc->feeds_txp)) {
+               drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
+               vc4_crtc->event = NULL;
+               drm_crtc_vblank_put(crtc);
+@@ -822,7 +848,8 @@ static void vc4_crtc_handle_page_flip(st
+                * the CRTC and encoder already reconfigured, leading to
+                * underruns. This can be seen when reconfiguring the CRTC.
+                */
+-              vc4_hvs_unmask_underrun(hvs, chan);
++              if (vc4->gen < VC4_GEN_6)
++                      vc4_hvs_unmask_underrun(hvs, chan);
+       }
+       spin_unlock(&vc4_crtc->irq_lock);
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -277,6 +277,7 @@ static const struct of_device_id vc4_dma
+       { .compatible = "brcm,bcm2711-hvs" },
+       { .compatible = "brcm,bcm2835-hvs" },
+       { .compatible = "brcm,bcm2711-hvs" },
++      { .compatible = "brcm,bcm2712-hvs" },
+       { .compatible = "raspberrypi,rpi-firmware-kms" },
+       { .compatible = "brcm,bcm2835-v3d" },
+       { .compatible = "brcm,cygnus-v3d" },
+@@ -308,8 +309,6 @@ static int vc4_drm_bind(struct device *d
+       enum vc4_gen gen;
+       int ret = 0;
+-      dev->coherent_dma_mask = DMA_BIT_MASK(32);
+-
+       if (of_device_is_compatible(dev->of_node, "brcm,bcm2712-vc6"))
+               gen = VC4_GEN_6;
+       else if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
+@@ -322,6 +321,11 @@ static int vc4_drm_bind(struct device *d
+       else
+               driver = &vc4_drm_driver;
++      if (gen >= VC4_GEN_6)
++              dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
++      else
++              dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
++
+       node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
+                                              NULL);
+       if (node) {
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -345,8 +345,10 @@ struct vc4_hvs {
+       unsigned int dlist_mem_size;
+       struct clk *core_clk;
++      struct clk *disp_clk;
+       struct {
++              unsigned int desc;
+               unsigned int enabled: 1;
+       } eof_irq[HVS_NUM_CHANNELS];
+@@ -358,6 +360,11 @@ struct vc4_hvs {
+       struct drm_mm dlist_mm;
+       /* Memory manager for the LBM memory used by HVS scaling. */
+       struct drm_mm lbm_mm;
++
++      /* Memory manager for the UPM memory used for prefetching. */
++      struct drm_mm upm_mm;
++      struct ida upm_handles;
++
+       spinlock_t mm_lock;
+       struct list_head stale_dlist_entries;
+@@ -382,6 +389,8 @@ struct vc4_hvs {
+       bool vc5_hdmi_enable_4096by2160;
+ };
++#define HVS_UBM_WORD_SIZE 256
++
+ struct vc4_hvs_state {
+       struct drm_private_state base;
+       unsigned long core_clock_rate;
+@@ -456,6 +465,15 @@ struct vc4_plane_state {
+       /* Our allocation in LBM for temporary storage during scaling. */
+       struct drm_mm_node lbm;
++      /* Our allocation in UPM for prefetching. */
++      struct drm_mm_node upm[DRM_FORMAT_MAX_PLANES];
++
++      /* The Unified Pre-Fetcher Handle */
++      unsigned int upm_handle[DRM_FORMAT_MAX_PLANES];
++
++      /* Number of lines to pre-fetch */
++      unsigned int upm_buffer_lines;
++
+       /* Set when the plane has per-pixel alpha content or does not cover
+        * the entire screen. This is a hint to the CRTC that it might need
+        * to enable background color fill.
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -67,6 +67,80 @@ static const struct debugfs_reg32 vc4_hv
+       VC4_REG32(SCALER_OLEDCOEF2),
+ };
++static const struct debugfs_reg32 vc6_hvs_regs[] = {
++      VC4_REG32(SCALER6_VERSION),
++      VC4_REG32(SCALER6_CXM_SIZE),
++      VC4_REG32(SCALER6_LBM_SIZE),
++      VC4_REG32(SCALER6_UBM_SIZE),
++      VC4_REG32(SCALER6_COBA_SIZE),
++      VC4_REG32(SCALER6_COB_SIZE),
++      VC4_REG32(SCALER6_CONTROL),
++      VC4_REG32(SCALER6_FETCHER_STATUS),
++      VC4_REG32(SCALER6_FETCH_STATUS),
++      VC4_REG32(SCALER6_HANDLE_ERROR),
++      VC4_REG32(SCALER6_DISP0_CTRL0),
++      VC4_REG32(SCALER6_DISP0_CTRL1),
++      VC4_REG32(SCALER6_DISP0_BGND),
++      VC4_REG32(SCALER6_DISP0_LPTRS),
++      VC4_REG32(SCALER6_DISP0_COB),
++      VC4_REG32(SCALER6_DISP0_STATUS),
++      VC4_REG32(SCALER6_DISP0_DL),
++      VC4_REG32(SCALER6_DISP0_RUN),
++      VC4_REG32(SCALER6_DISP1_CTRL0),
++      VC4_REG32(SCALER6_DISP1_CTRL1),
++      VC4_REG32(SCALER6_DISP1_BGND),
++      VC4_REG32(SCALER6_DISP1_LPTRS),
++      VC4_REG32(SCALER6_DISP1_COB),
++      VC4_REG32(SCALER6_DISP1_STATUS),
++      VC4_REG32(SCALER6_DISP1_DL),
++      VC4_REG32(SCALER6_DISP1_RUN),
++      VC4_REG32(SCALER6_DISP2_CTRL0),
++      VC4_REG32(SCALER6_DISP2_CTRL1),
++      VC4_REG32(SCALER6_DISP2_BGND),
++      VC4_REG32(SCALER6_DISP2_LPTRS),
++      VC4_REG32(SCALER6_DISP2_COB),
++      VC4_REG32(SCALER6_DISP2_STATUS),
++      VC4_REG32(SCALER6_DISP2_DL),
++      VC4_REG32(SCALER6_DISP2_RUN),
++      VC4_REG32(SCALER6_EOLN),
++      VC4_REG32(SCALER6_DL_STATUS),
++      VC4_REG32(SCALER6_BFG_MISC),
++      VC4_REG32(SCALER6_QOS0),
++      VC4_REG32(SCALER6_PROF0),
++      VC4_REG32(SCALER6_QOS1),
++      VC4_REG32(SCALER6_PROF1),
++      VC4_REG32(SCALER6_QOS2),
++      VC4_REG32(SCALER6_PROF2),
++      VC4_REG32(SCALER6_PRI_MAP0),
++      VC4_REG32(SCALER6_PRI_MAP1),
++      VC4_REG32(SCALER6_HISTCTRL),
++      VC4_REG32(SCALER6_HISTBIN0),
++      VC4_REG32(SCALER6_HISTBIN1),
++      VC4_REG32(SCALER6_HISTBIN2),
++      VC4_REG32(SCALER6_HISTBIN3),
++      VC4_REG32(SCALER6_HISTBIN4),
++      VC4_REG32(SCALER6_HISTBIN5),
++      VC4_REG32(SCALER6_HISTBIN6),
++      VC4_REG32(SCALER6_HISTBIN7),
++      VC4_REG32(SCALER6_HDR_CFG_REMAP),
++      VC4_REG32(SCALER6_COL_SPACE),
++      VC4_REG32(SCALER6_HVS_ID),
++      VC4_REG32(SCALER6_CFC1),
++      VC4_REG32(SCALER6_DISP_UPM_ISO0),
++      VC4_REG32(SCALER6_DISP_UPM_ISO1),
++      VC4_REG32(SCALER6_DISP_UPM_ISO2),
++      VC4_REG32(SCALER6_DISP_LBM_ISO0),
++      VC4_REG32(SCALER6_DISP_LBM_ISO1),
++      VC4_REG32(SCALER6_DISP_LBM_ISO2),
++      VC4_REG32(SCALER6_DISP_COB_ISO0),
++      VC4_REG32(SCALER6_DISP_COB_ISO1),
++      VC4_REG32(SCALER6_DISP_COB_ISO2),
++      VC4_REG32(SCALER6_BAD_COB),
++      VC4_REG32(SCALER6_BAD_LBM),
++      VC4_REG32(SCALER6_BAD_UPM),
++      VC4_REG32(SCALER6_BAD_AXI),
++};
++
+ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
+ {
+       struct drm_device *drm = &hvs->vc4->base;
+@@ -145,6 +219,55 @@ static int vc4_hvs_debugfs_dlist(struct
+       return 0;
+ }
++static int vc6_hvs_debugfs_dlist(struct seq_file *m, void *data)
++{
++      struct drm_info_node *node = m->private;
++      struct drm_device *dev = node->minor->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++      struct vc4_hvs *hvs = vc4->hvs;
++      struct drm_printer p = drm_seq_file_printer(m);
++      unsigned int dlist_mem_size = hvs->dlist_mem_size;
++      unsigned int next_entry_start;
++      unsigned int i;
++
++      for (i = 0; i < SCALER_CHANNELS_COUNT; i++) {
++              unsigned int active_dlist, dispstat;
++              unsigned int j;
++
++              dispstat = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(i)),
++                                       SCALER6_DISPX_STATUS_MODE);
++              if (dispstat == SCALER6_DISPX_STATUS_MODE_DISABLED ||
++                  dispstat == SCALER6_DISPX_STATUS_MODE_EOF) {
++                      drm_printf(&p, "HVS chan %u disabled\n", i);
++                      continue;
++              }
++
++              drm_printf(&p, "HVS chan %u:\n", i);
++
++              active_dlist = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_DL(i)),
++                                           SCALER6_DISPX_DL_LACT);
++              next_entry_start = 0;
++
++              for (j = active_dlist; j < dlist_mem_size; j++) {
++                      u32 dlist_word;
++
++                      dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
++                      drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
++                                 dlist_word);
++                      if (!next_entry_start ||
++                          next_entry_start == j) {
++                              if (dlist_word & SCALER_CTL0_END)
++                                      break;
++                              next_entry_start = j +
++                                      VC4_GET_FIELD(dlist_word,
++                                                    SCALER_CTL0_SIZE);
++                      }
++              }
++      }
++
++      return 0;
++}
++
+ static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data)
+ {
+       struct drm_info_node *node = m->private;
+@@ -435,6 +558,10 @@ static void vc4_hvs_irq_enable_eof(struc
+                         SCALER5_DISPCTRL_DSPEIEOF(channel));
+               break;
++      case VC4_GEN_6:
++              enable_irq(hvs->eof_irq[channel].desc);
++              break;
++
+       default:
+               break;
+       }
+@@ -463,6 +590,10 @@ static void vc4_hvs_irq_clear_eof(struct
+                         ~SCALER5_DISPCTRL_DSPEIEOF(channel));
+               break;
++      case VC4_GEN_6:
++              disable_irq_nosync(hvs->eof_irq[channel].desc);
++              break;
++
+       default:
+               break;
+       }
+@@ -622,26 +753,32 @@ static void vc4_hvs_dlist_free_work(stru
+ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
+ {
+-      struct drm_device *drm = &hvs->vc4->base;
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
+       u8 field = 0;
+       int idx;
+       if (!drm_dev_enter(drm, &idx))
+               return 0;
+-      switch (fifo) {
+-      case 0:
+-              field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+-                                    SCALER_DISPSTAT1_FRCNT0);
+-              break;
+-      case 1:
+-              field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+-                                    SCALER_DISPSTAT1_FRCNT1);
+-              break;
+-      case 2:
+-              field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
+-                                    SCALER_DISPSTAT2_FRCNT2);
+-              break;
++      if (vc4->gen >= VC4_GEN_6) {
++              field = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(fifo)),
++                                    SCALER6_DISPX_STATUS_FRCNT);
++      } else {
++              switch (fifo) {
++              case 0:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++                                            SCALER_DISPSTAT1_FRCNT0);
++                      break;
++              case 1:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++                                            SCALER_DISPSTAT1_FRCNT1);
++                      break;
++              case 2:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
++                                            SCALER_DISPSTAT2_FRCNT2);
++                      break;
++              }
+       }
+       drm_dev_exit(idx);
+@@ -708,6 +845,23 @@ int vc4_hvs_get_fifo_from_output(struct
+               default:
+                       return -EPIPE;
+               }
++
++      case VC4_GEN_6:
++              switch (output) {
++              case 0:
++                      return 0;
++
++              case 2:
++                      return 2;
++
++              case 1:
++              case 3:
++              case 4:
++                      return 1;
++
++              default:
++                      return -EPIPE;
++              }
+       }
+       return -EPIPE;
+@@ -782,7 +936,41 @@ static int vc4_hvs_init_channel(struct v
+       return 0;
+ }
+-void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
++                              struct drm_display_mode *mode, bool oneshot)
++{
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
++      struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
++      unsigned int chan = vc4_crtc_state->assigned_channel;
++      bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
++      u32 disp_ctrl1;
++      int idx;
++
++      if (!drm_dev_enter(drm, &idx))
++              return -ENODEV;
++
++      HVS_WRITE(SCALER6_DISPX_CTRL0(chan), SCALER6_DISPX_CTRL0_RESET);
++
++      disp_ctrl1 = HVS_READ(SCALER6_DISPX_CTRL1(chan));
++      disp_ctrl1 &= ~SCALER6_DISPX_CTRL1_INTLACE;
++      HVS_WRITE(SCALER6_DISPX_CTRL1(chan),
++                disp_ctrl1 | (interlace ? SCALER6_DISPX_CTRL1_INTLACE : 0));
++
++      HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++                SCALER6_DISPX_CTRL0_ENB |
++                VC4_SET_FIELD(mode->hdisplay - 1,
++                              SCALER6_DISPX_CTRL0_FWIDTH) |
++                (oneshot ? SCALER6_DISPX_CTRL0_ONESHOT : 0) |
++                VC4_SET_FIELD(mode->vdisplay - 1,
++                              SCALER6_DISPX_CTRL0_LINES));
++
++      drm_dev_exit(idx);
++
++      return 0;
++}
++
++static void __vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
+ {
+       struct drm_device *drm = &hvs->vc4->base;
+       int idx;
+@@ -813,6 +1001,42 @@ out:
+       drm_dev_exit(idx);
+ }
++static void __vc6_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++{
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
++      int idx;
++
++      if (!drm_dev_enter(drm, &idx))
++              return;
++
++      if (HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB)
++              goto out;
++
++      HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++                HVS_READ(SCALER6_DISPX_CTRL0(chan)) | SCALER6_DISPX_CTRL0_RESET);
++
++      HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++                HVS_READ(SCALER6_DISPX_CTRL0(chan)) & ~SCALER6_DISPX_CTRL0_ENB);
++
++      WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(chan)),
++                                 SCALER6_DISPX_STATUS_MODE) !=
++                   SCALER6_DISPX_STATUS_MODE_DISABLED);
++
++out:
++      drm_dev_exit(idx);
++}
++
++void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++{
++      struct vc4_dev *vc4 = hvs->vc4;
++
++      if (vc4->gen >= VC4_GEN_6)
++              __vc6_hvs_stop_channel(hvs, chan);
++      else
++              __vc4_hvs_stop_channel(hvs, chan);
++}
++
+ static int vc4_hvs_gamma_check(struct drm_crtc *crtc,
+                              struct drm_atomic_state *state)
+ {
+@@ -907,8 +1131,14 @@ static void vc4_hvs_install_dlist(struct
+               return;
+       WARN_ON(!vc4_state->mm);
+-      HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+-                vc4_state->mm->mm_node.start);
++
++      if (vc4->gen >= VC4_GEN_6)
++              HVS_WRITE(SCALER6_DISPX_LPTRS(vc4_state->assigned_channel),
++                        VC4_SET_FIELD(vc4_state->mm->mm_node.start,
++                                      SCALER6_DISPX_LPTRS_HEADE));
++      else
++              HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
++                        vc4_state->mm->mm_node.start);
+       drm_dev_exit(idx);
+ }
+@@ -965,7 +1195,11 @@ void vc4_hvs_atomic_enable(struct drm_cr
+       vc4_hvs_install_dlist(crtc);
+       vc4_hvs_update_dlist(crtc);
+-      vc4_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
++
++      if (vc4->gen >= VC4_GEN_6)
++              vc6_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
++      else
++              vc4_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
+ }
+ void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
+@@ -1052,13 +1286,28 @@ void vc4_hvs_atomic_flush(struct drm_crt
+       WARN_ON(!vc4_state->mm);
+       WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm->mm_node.size);
+-      if (enable_bg_fill)
++      if (enable_bg_fill) {
+               /* This sets a black background color fill, as is the case
+                * with other DRM drivers.
+                */
+-              HVS_WRITE(SCALER_DISPBKGNDX(channel),
+-                        HVS_READ(SCALER_DISPBKGNDX(channel)) |
+-                        SCALER_DISPBKGND_FILL);
++              if (vc4->gen >= VC4_GEN_6)
++                      HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
++                                HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
++                                SCALER6_DISPX_CTRL1_BGENB);
++              else
++                      HVS_WRITE(SCALER_DISPBKGNDX(channel),
++                                HVS_READ(SCALER_DISPBKGNDX(channel)) |
++                                SCALER_DISPBKGND_FILL);
++      } else {
++              if (vc4->gen >= VC4_GEN_6)
++                      HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
++                                HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
++                                ~SCALER6_DISPX_CTRL1_BGENB);
++              else
++                      HVS_WRITE(SCALER_DISPBKGNDX(channel),
++                                HVS_READ(SCALER_DISPBKGNDX(channel)) &
++                                ~SCALER_DISPBKGND_FILL);
++      }
+       /* Only update DISPLIST if the CRTC was already running and is not
+        * being disabled.
+@@ -1210,6 +1459,27 @@ static irqreturn_t vc4_hvs_irq_handler(i
+       return irqret;
+ }
++static irqreturn_t vc6_hvs_eof_irq_handler(int irq, void *data)
++{
++      struct drm_device *dev = data;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++      struct vc4_hvs *hvs = vc4->hvs;
++      unsigned int i;
++
++      for (i = 0; i < HVS_NUM_CHANNELS; i++) {
++              if (!hvs->eof_irq[i].enabled)
++                      continue;
++
++              if (hvs->eof_irq[i].desc != irq)
++                      continue;
++
++              vc4_hvs_schedule_dlist_sweep(hvs, i);
++              return IRQ_HANDLED;
++      }
++
++      return IRQ_NONE;
++}
++
+ int vc4_hvs_debugfs_init(struct drm_minor *minor)
+ {
+       struct drm_device *drm = minor->dev;
+@@ -1232,8 +1502,10 @@ int vc4_hvs_debugfs_init(struct drm_mino
+                                    NULL);
+       }
+-      ret = vc4_debugfs_add_file(minor, "hvs_dlists",
+-                                 vc4_hvs_debugfs_dlist, NULL);
++      if (vc4->gen >= VC4_GEN_6)
++              ret = vc4_debugfs_add_file(minor, "hvs_dlists", vc6_hvs_debugfs_dlist, NULL);
++      else
++              ret = vc4_debugfs_add_file(minor, "hvs_dlists", vc4_hvs_debugfs_dlist, NULL);
+       if (ret)
+               return ret;
+@@ -1256,6 +1528,9 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ {
+       struct drm_device *drm = &vc4->base;
+       struct vc4_hvs *hvs;
++      unsigned int dlist_start;
++      size_t dlist_size;
++      size_t lbm_size;
+       hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
+       if (!hvs)
+@@ -1270,14 +1545,39 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+       INIT_LIST_HEAD(&hvs->stale_dlist_entries);
+       INIT_WORK(&hvs->free_dlist_work, vc4_hvs_dlist_free_work);
+-      /* Set up the HVS display list memory manager.  We never
+-       * overwrite the setup from the bootloader (just 128b out of
+-       * our 16K), since we don't want to scramble the screen when
+-       * transitioning from the firmware's boot setup to runtime.
+-       */
+-      drm_mm_init(&hvs->dlist_mm,
+-                  HVS_BOOTLOADER_DLIST_END,
+-                  (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
++      switch (vc4->gen) {
++      case VC4_GEN_4:
++      case VC4_GEN_5:
++              /* Set up the HVS display list memory manager. We never
++               * overwrite the setup from the bootloader (just 128b
++               * out of our 16K), since we don't want to scramble the
++               * screen when transitioning from the firmware's boot
++               * setup to runtime.
++               */
++              dlist_start = HVS_BOOTLOADER_DLIST_END;
++              dlist_size = (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END;
++              break;
++
++      case VC4_GEN_6:
++              dlist_start = HVS_BOOTLOADER_DLIST_END;
++
++              /*
++               * If we are running a test, it means that we can't
++               * access a register. Use a plausible size then.
++               */
++              if (!kunit_get_current_test())
++                      dlist_size = HVS_READ(SCALER6_CXM_SIZE);
++              else
++                      dlist_size = 4096;
++
++              break;
++
++      default:
++              drm_err(drm, "Unknown VC4 generation: %d", vc4->gen);
++              return ERR_PTR(-ENODEV);
++      }
++
++      drm_mm_init(&hvs->dlist_mm, dlist_start, dlist_size);
+       hvs->dlist_mem_size = dlist_size;
+@@ -1286,12 +1586,46 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+        * between planes when they don't overlap on the screen, but
+        * for now we just allocate globally.
+        */
+-      if (vc4->gen == VC4_GEN_4)
++
++      switch (vc4->gen) {
++      case VC4_GEN_4:
+               /* 48k words of 2x12-bit pixels */
+-              drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
+-      else
++              lbm_size = 48 * SZ_1K;
++              break;
++
++      case VC4_GEN_5:
+               /* 60k words of 4x12-bit pixels */
+-              drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024);
++              lbm_size = 60 * SZ_1K;
++              break;
++
++      case VC4_GEN_6:
++              /*
++               * If we are running a test, it means that we can't
++               * access a register. Use a plausible size then.
++               */
++              lbm_size = 1024;
++              break;
++
++      default:
++              drm_err(drm, "Unknown VC4 generation: %d", vc4->gen);
++              return ERR_PTR(-ENODEV);
++      }
++
++      drm_mm_init(&hvs->lbm_mm, 0, lbm_size);
++
++      if (vc4->gen >= VC4_GEN_6) {
++              ida_init(&hvs->upm_handles);
++
++              /*
++               * NOTE: On BCM2712, the size can also be read through
++               * the SCALER_UBM_SIZE register. We would need to do a
++               * register access though, which we can't do with kunit
++               * that also uses this function to create its mock
++               * device.
++               */
++              drm_mm_init(&hvs->upm_mm, 0, 1024 * HVS_UBM_WORD_SIZE);
++      }
++
+       vc4->hvs = hvs;
+@@ -1388,10 +1722,124 @@ static int vc4_hvs_hw_init(struct vc4_hv
+       return 0;
+ }
++#define CFC1_N_NL_CSC_CTRL(x)         (0xa000 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C00(x)    (0xa008 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C01(x)    (0xa00c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C02(x)    (0xa010 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C03(x)    (0xa014 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C04(x)    (0xa018 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C10(x)    (0xa01c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C11(x)    (0xa020 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C12(x)    (0xa024 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C13(x)    (0xa028 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C14(x)    (0xa02c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C20(x)    (0xa030 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C21(x)    (0xa034 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C22(x)    (0xa038 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C23(x)    (0xa03c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C24(x)    (0xa040 + ((x) * 0x3000))
++
++/* 4 S2.22 multiplication factors, and 1 S9.15 addititive element for each of 3
++ * output components
++ */
++struct vc6_csc_coeff_entry {
++      u32 csc[3][5];
++};
++
++static const struct vc6_csc_coeff_entry csc_coeffs[2][3] = {
++      [DRM_COLOR_YCBCR_LIMITED_RANGE] = {
++              [DRM_COLOR_YCBCR_BT601] = {
++                      .csc = {
++                              { 0x004A8542, 0x0, 0x0066254A, 0x0, 0xFF908A0D },
++                              { 0x004A8542, 0xFFE6ED5D, 0xFFCBF856, 0x0, 0x0043C9A3 },
++                              { 0x004A8542, 0x00811A54, 0x0, 0x0, 0xFF759502 }
++                      }
++              },
++              [DRM_COLOR_YCBCR_BT709] = {
++                      .csc = {
++                              { 0x004A8542, 0x0, 0x0072BC44, 0x0, 0xFF83F312 },
++                              { 0x004A8542, 0xFFF25A22, 0xFFDDE4D0, 0x0, 0x00267064 },
++                              { 0x004A8542, 0x00873197, 0x0, 0x0, 0xFF6F7DC0 }
++                      }
++              },
++              [DRM_COLOR_YCBCR_BT2020] = {
++                      .csc = {
++                              { 0x004A8542, 0x0, 0x006B4A17, 0x0, 0xFF8B653F },
++                              { 0x004A8542, 0xFFF402D9, 0xFFDDE4D0, 0x0, 0x0024C7AE },
++                              { 0x004A8542, 0x008912CC, 0x0, 0x0, 0xFF6D9C8B }
++                      }
++              }
++      },
++      [DRM_COLOR_YCBCR_FULL_RANGE] = {
++              [DRM_COLOR_YCBCR_BT601] = {
++                      .csc = {
++                              { 0x00400000, 0x0, 0x0059BA5E, 0x0, 0xFFA645A1 },
++                              { 0x00400000, 0xFFE9F9AC, 0xFFD24B97, 0x0, 0x0043BABB },
++                              { 0x00400000, 0x00716872, 0x0, 0x0, 0xFF8E978D }
++                      }
++              },
++              [DRM_COLOR_YCBCR_BT709] = {
++                      .csc = {
++                              { 0x00400000, 0x0, 0x0064C985, 0x0, 0xFF9B367A },
++                              { 0x00400000, 0xFFF402E1, 0xFFE20A40, 0x0, 0x0029F2DE },
++                              { 0x00400000, 0x0076C226, 0x0, 0x0, 0xFF893DD9 }
++                      }
++              },
++              [DRM_COLOR_YCBCR_BT2020] = {
++                      .csc = {
++                              { 0x00400000, 0x0, 0x005E3F14, 0x0, 0xFFA1C0EB },
++                              { 0x00400000, 0xFFF577F6, 0xFFDB580F, 0x0, 0x002F2FFA },
++                              { 0x00400000, 0x007868DB, 0x0, 0x0, 0xFF879724 }
++                      }
++              }
++      }
++};
++
++static int vc6_hvs_hw_init(struct vc4_hvs *hvs)
++{
++      const struct vc6_csc_coeff_entry *coeffs;
++      unsigned int i;
++
++      HVS_WRITE(SCALER6_CONTROL,
++                SCALER6_CONTROL_HVS_EN |
++                VC4_SET_FIELD(8, SCALER6_CONTROL_PF_LINES) |
++                VC4_SET_FIELD(15, SCALER6_CONTROL_MAX_REQS));
++
++      /* Set HVS arbiter priority to max */
++      HVS_WRITE(SCALER6_PRI_MAP0, 0xffffffff);
++      HVS_WRITE(SCALER6_PRI_MAP1, 0xffffffff);
++
++      for (i = 0; i < 6; i++) {
++              coeffs = &csc_coeffs[i / 3][i % 3];
++
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C00(i), coeffs->csc[0][0]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C01(i), coeffs->csc[0][1]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C02(i), coeffs->csc[0][2]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C03(i), coeffs->csc[0][3]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C04(i), coeffs->csc[0][4]);
++
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C10(i), coeffs->csc[1][0]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C11(i), coeffs->csc[1][1]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C12(i), coeffs->csc[1][2]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C13(i), coeffs->csc[1][3]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C14(i), coeffs->csc[1][4]);
++
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C20(i), coeffs->csc[2][0]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C21(i), coeffs->csc[2][1]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C22(i), coeffs->csc[2][2]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C23(i), coeffs->csc[2][3]);
++              HVS_WRITE(CFC1_N_MA_CSC_COEFF_C24(i), coeffs->csc[2][4]);
++
++              HVS_WRITE(CFC1_N_NL_CSC_CTRL(i), BIT(15));
++      }
++
++      return 0;
++}
++
+ static int vc4_hvs_cob_init(struct vc4_hvs *hvs)
+ {
+       struct vc4_dev *vc4 = hvs->vc4;
+-      u32 reg, top;
++      u32 reg, top, base;
+       /*
+        * Recompute Composite Output Buffer (COB) allocations for the
+@@ -1452,6 +1900,31 @@ static int vc4_hvs_cob_init(struct vc4_h
+               HVS_WRITE(SCALER_DISPBASE0, reg);
+               break;
++      case VC4_GEN_6:
++              #define VC6_COB_LINE_WIDTH      3840
++              #define VC6_COB_NUM_LINES       4
++              reg = 0;
++              top = 3840;
++
++              HVS_WRITE(SCALER6_DISP2_COB,
++                        VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++                        VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++
++              base = top + 16;
++              top += VC6_COB_LINE_WIDTH * VC6_COB_NUM_LINES;
++
++              HVS_WRITE(SCALER6_DISP1_COB,
++                        VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++                        VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++
++              base = top + 16;
++              top += VC6_COB_LINE_WIDTH * VC6_COB_NUM_LINES;
++
++              HVS_WRITE(SCALER6_DISP0_COB,
++                        VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++                        VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++              break;
++
+       default:
+               return -EINVAL;
+       }
+@@ -1477,10 +1950,16 @@ static int vc4_hvs_bind(struct device *d
+               return PTR_ERR(hvs);
+       hvs->regset.base = hvs->regs;
+-      hvs->regset.regs = vc4_hvs_regs;
+-      hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
+-      if (vc4->gen == VC4_GEN_5) {
++      if (vc4->gen >= VC4_GEN_6) {
++              hvs->regset.regs = vc6_hvs_regs;
++              hvs->regset.nregs = ARRAY_SIZE(vc6_hvs_regs);
++      } else {
++              hvs->regset.regs = vc4_hvs_regs;
++              hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
++      }
++
++      if (vc4->gen >= VC4_GEN_5) {
+               struct rpi_firmware *firmware;
+               struct device_node *node;
+               unsigned int max_rate;
+@@ -1494,12 +1973,20 @@ static int vc4_hvs_bind(struct device *d
+               if (!firmware)
+                       return -EPROBE_DEFER;
+-              hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++              hvs->core_clk = devm_clk_get(&pdev->dev,
++                                           (vc4->gen >= VC4_GEN_6) ? "core" : NULL);
+               if (IS_ERR(hvs->core_clk)) {
+                       dev_err(&pdev->dev, "Couldn't get core clock\n");
+                       return PTR_ERR(hvs->core_clk);
+               }
++              hvs->disp_clk = devm_clk_get(&pdev->dev,
++                                           (vc4->gen >= VC4_GEN_6) ? "disp" : NULL);
++              if (IS_ERR(hvs->disp_clk)) {
++                      dev_err(&pdev->dev, "Couldn't get disp clock\n");
++                      return PTR_ERR(hvs->disp_clk);
++              }
++
+               max_rate = rpi_firmware_clk_get_max_rate(firmware,
+                                                        RPI_FIRMWARE_CORE_CLK_ID);
+               rpi_firmware_put(firmware);
+@@ -1516,14 +2003,51 @@ static int vc4_hvs_bind(struct device *d
+                       dev_err(&pdev->dev, "Couldn't enable the core clock\n");
+                       return ret;
+               }
++
++              ret = clk_prepare_enable(hvs->disp_clk);
++              if (ret) {
++                      dev_err(&pdev->dev, "Couldn't enable the disp clock\n");
++                      return ret;
++              }
+       }
+-      if (vc4->gen == VC4_GEN_4)
+-              hvs->dlist = hvs->regs + SCALER_DLIST_START;
+-      else
++      if (vc4->gen >= VC4_GEN_6) {
++              unsigned int i;
++
++              for (i = 0; i < HVS_NUM_CHANNELS; i++) {
++                      char irq_name[16];
++                      int irq;
++
++                      snprintf(irq_name, sizeof(irq_name), "ch%u-eof", i);
++
++                      irq = platform_get_irq_byname(pdev, irq_name);
++                      if (irq < 0) {
++                              dev_err(&pdev->dev,
++                                      "Couldn't get %s interrupt: %d\n",
++                                      irq_name, irq);
++                              return irq;
++                      }
++
++                      ret = devm_request_irq(&pdev->dev,
++                                             irq,
++                                             vc6_hvs_eof_irq_handler,
++                                             IRQF_NO_AUTOEN,
++                                             dev_name(&pdev->dev),
++                                             drm);
++
++                      hvs->eof_irq[i].desc = irq;
++              }
++      }
++
++      if (vc4->gen >= VC4_GEN_5)
+               hvs->dlist = hvs->regs + SCALER5_DLIST_START;
++      else
++              hvs->dlist = hvs->regs + SCALER_DLIST_START;
+-      ret = vc4_hvs_hw_init(hvs);
++      if (vc4->gen >= VC4_GEN_6)
++              ret = vc6_hvs_hw_init(hvs);
++      else
++              ret = vc4_hvs_hw_init(hvs);
+       if (ret)
+               return ret;
+@@ -1540,10 +2064,12 @@ static int vc4_hvs_bind(struct device *d
+       if (ret)
+               return ret;
+-      ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+-                             vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
+-      if (ret)
+-              return ret;
++      if (vc4->gen < VC4_GEN_6) {
++              ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++                                     vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
++              if (ret)
++                      return ret;
++      }
+       return 0;
+ }
+@@ -1568,6 +2094,7 @@ static void vc4_hvs_unbind(struct device
+               drm_mm_remove_node(node);
+       drm_mm_takedown(&vc4->hvs->lbm_mm);
++      clk_disable_unprepare(hvs->disp_clk);
+       clk_disable_unprepare(hvs->core_clk);
+       vc4->hvs = NULL;
+@@ -1591,6 +2118,7 @@ static int vc4_hvs_dev_remove(struct pla
+ static const struct of_device_id vc4_hvs_dt_match[] = {
+       { .compatible = "brcm,bcm2711-hvs" },
++      { .compatible = "brcm,bcm2712-hvs" },
+       { .compatible = "brcm,bcm2835-hvs" },
+       {}
+ };
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -329,17 +329,59 @@ static void vc5_hvs_pv_muxing_commit(str
+       }
+ }
++static void vc6_hvs_pv_muxing_commit(struct vc4_dev *vc4,
++                                   struct drm_atomic_state *state)
++{
++      struct vc4_hvs *hvs = vc4->hvs;
++      struct drm_crtc_state *crtc_state;
++      struct drm_crtc *crtc;
++      unsigned int i;
++
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
++      for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
++              struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
++              struct vc4_encoder *vc4_encoder;
++              struct drm_encoder *encoder;
++              unsigned char mux;
++              u32 reg;
++
++              if (!vc4_state->update_muxing)
++                      continue;
++
++              if (vc4_state->assigned_channel != 1)
++                      continue;
++
++              encoder = vc4_get_crtc_encoder(crtc, crtc_state);
++              vc4_encoder = to_vc4_encoder(encoder);
++              switch (vc4_encoder->type) {
++              case VC4_ENCODER_TYPE_HDMI1:
++                      mux = 0;
++                      break;
++
++              case VC4_ENCODER_TYPE_TXP:
++                      mux = 2;
++                      break;
++
++              default:
++                      break;
++              }
++
++              reg = HVS_READ(SCALER6_CONTROL);
++              HVS_WRITE(SCALER6_CONTROL,
++                        (reg & ~SCALER6_CONTROL_DSP1_TARGET_MASK) |
++                        VC4_SET_FIELD(mux, SCALER6_CONTROL_DSP1_TARGET));
++      }
++}
++
+ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
+ {
+       struct drm_device *dev = state->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       struct vc4_hvs *hvs = vc4->hvs;
+-      struct drm_crtc_state *new_crtc_state;
+       struct vc4_hvs_state *new_hvs_state;
+-      struct drm_crtc *crtc;
+       struct vc4_hvs_state *old_hvs_state;
+       unsigned int channel;
+-      int i;
+       old_hvs_state = vc4_hvs_get_old_global_state(state);
+       if (WARN_ON(IS_ERR(old_hvs_state)))
+@@ -349,14 +391,23 @@ static void vc4_atomic_commit_tail(struc
+       if (WARN_ON(IS_ERR(new_hvs_state)))
+               return;
+-      for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+-              struct vc4_crtc_state *vc4_crtc_state;
++      if (vc4->gen < VC4_GEN_6) {
++              struct drm_crtc_state *new_crtc_state;
++              struct drm_crtc *crtc;
++              int i;
++
++              for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
++                      struct vc4_crtc_state *vc4_crtc_state;
+-              if (!new_crtc_state->commit || vc4->firmware_kms)
+-                      continue;
++                      if (vc4->firmware_kms)
++                              continue;
+-              vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
+-              vc4_hvs_mask_underrun(hvs, vc4_crtc_state->assigned_channel);
++                      if (!new_crtc_state->commit)
++                              continue;
++
++                      vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
++                      vc4_hvs_mask_underrun(hvs, vc4_crtc_state->assigned_channel);
++              }
+       }
+       for (channel = 0; channel < HVS_NUM_CHANNELS; channel++) {
+@@ -378,7 +429,7 @@ static void vc4_atomic_commit_tail(struc
+               old_hvs_state->fifo_state[channel].pending_commit = NULL;
+       }
+-      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
++      if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+                                              new_hvs_state->core_clock_rate);
+               unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -391,17 +442,32 @@ static void vc4_atomic_commit_tail(struc
+                * modeset.
+                */
+               WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
++              WARN_ON(clk_set_min_rate(hvs->disp_clk, core_rate));
+       }
+       drm_atomic_helper_commit_modeset_disables(dev, state);
+-      vc4_ctm_commit(vc4, state);
++      if (vc4->gen <= VC4_GEN_5)
++              vc4_ctm_commit(vc4, state);
+       if (!vc4->firmware_kms) {
+-              if (vc4->gen == VC4_GEN_5)
+-                      vc5_hvs_pv_muxing_commit(vc4, state);
+-              else
++              switch (vc4->gen) {
++              case VC4_GEN_4:
+                       vc4_hvs_pv_muxing_commit(vc4, state);
++                      break;
++
++              case VC4_GEN_5:
++                      vc5_hvs_pv_muxing_commit(vc4, state);
++                      break;
++
++              case VC4_GEN_6:
++                      vc6_hvs_pv_muxing_commit(vc4, state);
++                      break;
++
++              default:
++                      drm_err(dev, "Unknown VC4 generation: %d", vc4->gen);
++                      break;
++              }
+       }
+       drm_atomic_helper_commit_planes(dev, state,
+@@ -417,7 +483,7 @@ static void vc4_atomic_commit_tail(struc
+       drm_atomic_helper_cleanup_planes(dev, state);
+-      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
++      if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long core_rate = min_t(unsigned long,
+                                               hvs->max_core_rate,
+                                               new_hvs_state->core_clock_rate);
+@@ -429,6 +495,7 @@ static void vc4_atomic_commit_tail(struc
+                * requirements.
+                */
+               WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
++              WARN_ON(clk_set_min_rate(hvs->disp_clk, core_rate));
+               drm_dbg(dev, "Core clock actual rate: %lu Hz\n",
+                       clk_get_rate(hvs->core_clk));
+@@ -1081,7 +1148,10 @@ int vc4_kms_load(struct drm_device *dev)
+               return ret;
+       }
+-      if (vc4->gen == VC4_GEN_5) {
++      if (vc4->gen >= VC4_GEN_6) {
++              dev->mode_config.max_width = 8192;
++              dev->mode_config.max_height = 8192;
++      } else if (vc4->gen >= VC4_GEN_5) {
+               dev->mode_config.max_width = 7680;
+               dev->mode_config.max_height = 7680;
+       } else {
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -279,6 +279,7 @@ static bool plane_enabled(struct drm_pla
+ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+ {
+       struct vc4_plane_state *vc4_state;
++      unsigned int i;
+       if (WARN_ON(!plane->state))
+               return NULL;
+@@ -288,6 +289,11 @@ static struct drm_plane_state *vc4_plane
+               return NULL;
+       memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
++      memset(&vc4_state->upm, 0, sizeof(vc4_state->upm));
++
++      for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++)
++              vc4_state->upm_handle[i] = 0;
++
+       vc4_state->dlist_initialized = 0;
+       __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
+@@ -310,14 +316,30 @@ static void vc4_plane_destroy_state(stru
+                                   struct drm_plane_state *state)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
++      struct vc4_hvs *hvs = vc4->hvs;
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      unsigned int i;
+       if (drm_mm_node_allocated(&vc4_state->lbm)) {
+               unsigned long irqflags;
+-              spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
++              spin_lock_irqsave(&hvs->mm_lock, irqflags);
+               drm_mm_remove_node(&vc4_state->lbm);
+-              spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
++              spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++      }
++
++      for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) {
++              unsigned long irqflags;
++
++              if (!drm_mm_node_allocated(&vc4_state->upm[i]))
++                      continue;
++
++              spin_lock_irqsave(&hvs->mm_lock, irqflags);
++              drm_mm_remove_node(&vc4_state->upm[i]);
++              spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++
++              if (vc4_state->upm_handle[i] > 0)
++                      ida_free(&hvs->upm_handles, vc4_state->upm_handle[i]);
+       }
+       kfree(vc4_state->dlist);
+@@ -543,6 +565,11 @@ static void vc4_write_tpz(struct vc4_pla
+       recip = ~0 / scale;
+       vc4_dlist_write(vc4_state,
++                      /*
++                       * The BCM2712 is lacking BIT(31) compared to
++                       * the previous generations, but we don't use
++                       * it.
++                       */
+                       VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
+                       VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
+       vc4_dlist_write(vc4_state,
+@@ -590,10 +617,15 @@ static void vc4_write_ppf(struct vc4_pla
+       vc4_dlist_write(vc4_state,
+                       SCALER_PPF_AGC |
+                       VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
++                      /*
++                       * The register layout documentation is slightly
++                       * different to setup the phase in the BCM2712,
++                       * but they seem equivalent.
++                       */
+                       VC4_SET_FIELD(phase, SCALER_PPF_IPHASE));
+ }
+-static u32 vc4_lbm_size(struct drm_plane_state *state)
++static u32 __vc4_lbm_size(struct drm_plane_state *state)
+ {
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+       struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
+@@ -641,6 +673,131 @@ static u32 vc4_lbm_size(struct drm_plane
+       return lbm;
+ }
++static unsigned int vc4_lbm_words_per_component(const struct drm_plane_state *state,
++                                              unsigned int channel)
++{
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++
++      switch (vc4_state->y_scaling[channel]) {
++      case VC4_SCALING_PPF:
++              return 4;
++
++      case VC4_SCALING_TPZ:
++              return 2;
++
++      default:
++              return 0;
++      }
++}
++
++static unsigned int vc4_lbm_components(const struct drm_plane_state *state,
++                                     unsigned int channel)
++{
++      const struct drm_format_info *info = state->fb->format;
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++
++      if (vc4_state->y_scaling[channel] == VC4_SCALING_NONE)
++              return 0;
++
++      if (info->is_yuv)
++              return channel ? 2 : 1;
++
++      if (info->has_alpha)
++              return 4;
++
++      return 3;
++}
++
++static unsigned int vc4_lbm_channel_size(const struct drm_plane_state *state,
++                                       unsigned int channel)
++{
++      const struct drm_format_info *info = state->fb->format;
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      unsigned int channels_scaled = 0;
++      unsigned int components, words, wpc;
++      unsigned int width, lines;
++      unsigned int i;
++
++      /* LBM is meant to use the smaller of source or dest width, but there
++       * is a issue with UV scaling that the size required for the second
++       * channel is based on the source width only.
++       */
++      if (info->hsub > 1 && channel == 1)
++              width = state->src_w >> 16;
++      else
++              width = min(state->src_w >> 16, state->crtc_w);
++      width = round_up(width / info->hsub, 4);
++
++      wpc = vc4_lbm_words_per_component(state, channel);
++      if (!wpc)
++              return 0;
++
++      components = vc4_lbm_components(state, channel);
++      if (!components)
++              return 0;
++
++      if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)
++              components -= 1;
++
++      words = width * wpc * components;
++
++      lines = DIV_ROUND_UP(words, 128 / info->hsub);
++
++      for (i = 0; i < 2; i++)
++              if (vc4_state->y_scaling[channel] != VC4_SCALING_NONE)
++                      channels_scaled++;
++
++      if (channels_scaled == 1)
++              lines = lines / 2;
++
++      return lines;
++}
++
++static unsigned int __vc6_lbm_size(const struct drm_plane_state *state)
++{
++      const struct drm_format_info *info = state->fb->format;
++
++      if (info->hsub > 1)
++              return max(vc4_lbm_channel_size(state, 0),
++                         vc4_lbm_channel_size(state, 1));
++      else
++              return vc4_lbm_channel_size(state, 0);
++}
++
++u32 vc4_lbm_size(struct drm_plane_state *state)
++{
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
++
++      /* LBM is not needed when there's no vertical scaling. */
++      if (vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
++          vc4_state->y_scaling[1] == VC4_SCALING_NONE)
++              return 0;
++
++      if (vc4->gen >= VC4_GEN_6)
++              return __vc6_lbm_size(state);
++      else
++              return __vc4_lbm_size(state);
++}
++
++static size_t vc6_upm_size(const struct drm_plane_state *state,
++                         unsigned int plane)
++{
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      unsigned int stride = state->fb->pitches[plane];
++
++      /*
++       * TODO: This only works for raster formats, and is sub-optimal
++       * for buffers with a stride aligned on 32 bytes.
++       */
++      unsigned int words_per_line = (stride + 62) / 32;
++      unsigned int fetch_region_size = words_per_line * 32;
++      unsigned int buffer_lines = 2 << vc4_state->upm_buffer_lines;
++      unsigned int buffer_size = fetch_region_size * buffer_lines;
++
++      return ALIGN(buffer_size, HVS_UBM_WORD_SIZE);
++}
++
+ static void vc4_write_scaling_parameters(struct drm_plane_state *state,
+                                        int channel)
+ {
+@@ -744,6 +901,10 @@ static int vc4_plane_allocate_lbm(struct
+       if (!lbm_size)
+               return 0;
++      /*
++       * NOTE: BCM2712 doesn't need to be aligned, since the size
++       * returned by vc4_lbm_size() is in words already.
++       */
+       if (vc4->gen == VC4_GEN_5)
+               lbm_size = ALIGN(lbm_size, 64);
+       else if (vc4->gen == VC4_GEN_4)
+@@ -781,6 +942,57 @@ static int vc4_plane_allocate_lbm(struct
+       return 0;
+ }
++static int vc6_plane_allocate_upm(struct drm_plane_state *state)
++{
++      const struct drm_format_info *info = state->fb->format;
++      struct drm_device *drm = state->plane->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
++      struct vc4_hvs *hvs = vc4->hvs;
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      unsigned int i;
++      int ret;
++
++      WARN_ON_ONCE(vc4->gen < VC4_GEN_6);
++
++      vc4_state->upm_buffer_lines = SCALER6_PTR0_UPM_BUFF_SIZE_2_LINES;
++
++      for (i = 0; i < info->num_planes; i++) {
++              unsigned long irqflags;
++              size_t upm_size;
++
++              upm_size = vc6_upm_size(state, i);
++              if (!upm_size)
++                      return -EINVAL;
++
++              spin_lock_irqsave(&hvs->mm_lock, irqflags);
++              ret = drm_mm_insert_node_generic(&hvs->upm_mm,
++                                               &vc4_state->upm[i],
++                                               upm_size, HVS_UBM_WORD_SIZE,
++                                               0, 0);
++              spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++              if (ret) {
++                      drm_err(drm, "Failed to allocate UPM entry: %d\n", ret);
++                      return ret;
++              }
++
++              ret = ida_alloc_range(&hvs->upm_handles, 1, 32, GFP_KERNEL);
++              if (ret < 0)
++                      return ret;
++
++              vc4_state->upm_handle[i] = ret;
++
++              vc4_state->dlist[vc4_state->ptr0_offset[i]] |=
++                      VC4_SET_FIELD(vc4_state->upm[i].start / HVS_UBM_WORD_SIZE,
++                                    SCALER6_PTR0_UPM_BASE) |
++                      VC4_SET_FIELD(vc4_state->upm_handle[i] - 1,
++                                    SCALER6_PTR0_UPM_HANDLE) |
++                      VC4_SET_FIELD(vc4_state->upm_buffer_lines,
++                                    SCALER6_PTR0_UPM_BUFF_SIZE);
++      }
++
++      return 0;
++}
++
+ /*
+  * The colorspace conversion matrices are held in 3 entries in the dlist.
+  * Create an array of them, with entries for each full and limited mode, and
+@@ -1355,6 +1567,413 @@ static int vc4_plane_mode_set(struct drm
+       return 0;
+ }
++static u32 vc6_plane_get_csc_mode(struct vc4_plane_state *vc4_state)
++{
++      struct drm_plane_state *state = &vc4_state->base;
++      u32 ret = 0;
++
++      if (vc4_state->is_yuv) {
++              enum drm_color_encoding color_encoding = state->color_encoding;
++              enum drm_color_range color_range = state->color_range;
++
++              ret |= SCALER6_CTL2_CSC_ENABLE;
++
++              /* CSC pre-loaded with:
++               * 0 = BT601 limited range
++               * 1 = BT709 limited range
++               * 2 = BT2020 limited range
++               * 3 = BT601 full range
++               * 4 = BT709 full range
++               * 5 = BT2020 full range
++               */
++              if (color_encoding > DRM_COLOR_YCBCR_BT2020)
++                      color_encoding = DRM_COLOR_YCBCR_BT601;
++              if (color_range > DRM_COLOR_YCBCR_FULL_RANGE)
++                      color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
++
++              ret |= VC4_SET_FIELD(color_encoding + (color_range * 3),
++                                   SCALER6_CTL2_BRCM_CFC_CONTROL);
++      }
++
++      return ret;
++}
++
++static int vc6_plane_mode_set(struct drm_plane *plane,
++                            struct drm_plane_state *state)
++{
++      struct drm_device *drm = plane->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
++      struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      struct drm_framebuffer *fb = state->fb;
++      const struct hvs_format *format = vc4_get_hvs_format(fb->format->format);
++      u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier);
++      int num_planes = fb->format->num_planes;
++      u32 h_subsample = fb->format->hsub;
++      u32 v_subsample = fb->format->vsub;
++      bool mix_plane_alpha;
++      bool covers_screen;
++      u32 scl0, scl1, pitch0;
++      u32 tiling, src_x, src_y;
++      u32 width, height;
++      u32 hvs_format = format->hvs;
++      u32 offsets[3] = { 0 };
++      unsigned int rotation;
++      int ret, i;
++
++      if (vc4_state->dlist_initialized)
++              return 0;
++
++      ret = vc4_plane_setup_clipping_and_scaling(state);
++      if (ret)
++              return ret;
++
++      width = vc4_state->src_w[0] >> 16;
++      height = vc4_state->src_h[0] >> 16;
++
++      /* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
++       * and 4:4:4, scl1 should be set to scl0 so both channels of
++       * the scaler do the same thing.  For YUV, the Y plane needs
++       * to be put in channel 1 and Cb/Cr in channel 0, so we swap
++       * the scl fields here.
++       */
++      if (num_planes == 1) {
++              scl0 = vc4_get_scl_field(state, 0);
++              scl1 = scl0;
++      } else {
++              scl0 = vc4_get_scl_field(state, 1);
++              scl1 = vc4_get_scl_field(state, 0);
++      }
++
++      rotation = drm_rotation_simplify(state->rotation,
++                                       DRM_MODE_ROTATE_0 |
++                                       DRM_MODE_REFLECT_X |
++                                       DRM_MODE_REFLECT_Y);
++
++      /* We must point to the last line when Y reflection is enabled. */
++      src_y = vc4_state->src_y >> 16;
++      if (rotation & DRM_MODE_REFLECT_Y)
++              src_y += height - 1;
++
++      src_x = vc4_state->src_x >> 16;
++
++      switch (base_format_mod) {
++      case DRM_FORMAT_MOD_LINEAR:
++              tiling = SCALER6_CTL0_ADDR_MODE_LINEAR;
++
++              /* Adjust the base pointer to the first pixel to be scanned
++               * out.
++               */
++              for (i = 0; i < num_planes; i++) {
++                      offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
++                      offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
++              }
++
++              break;
++
++      case DRM_FORMAT_MOD_BROADCOM_SAND128:
++      case DRM_FORMAT_MOD_BROADCOM_SAND256: {
++              uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
++              u32 components_per_word;
++              u32 starting_offset;
++              u32 fetch_count;
++
++              if (param > SCALER_TILE_HEIGHT_MASK) {
++                      DRM_DEBUG_KMS("SAND height too large (%d)\n",
++                                    param);
++                      return -EINVAL;
++              }
++
++              if (fb->format->format == DRM_FORMAT_P030) {
++                      hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
++                      tiling = SCALER6_CTL0_ADDR_MODE_128B;
++              } else {
++                      hvs_format = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE;
++
++                      switch (base_format_mod) {
++                      case DRM_FORMAT_MOD_BROADCOM_SAND128:
++                              tiling = SCALER6_CTL0_ADDR_MODE_128B;
++                              break;
++                      case DRM_FORMAT_MOD_BROADCOM_SAND256:
++                              tiling = SCALER6_CTL0_ADDR_MODE_256B;
++                              break;
++                      default:
++                              return -EINVAL;
++                      }
++              }
++
++              /* Adjust the base pointer to the first pixel to be scanned
++               * out.
++               *
++               * For P030, y_ptr [31:4] is the 128bit word for the start pixel
++               * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
++               * word that should be taken as the first pixel.
++               * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
++               * element within the 128bit word, eg for pixel 3 the value
++               * should be 6.
++               */
++              for (i = 0; i < num_planes; i++) {
++                      u32 tile_w, tile, x_off, pix_per_tile;
++
++                      if (fb->format->format == DRM_FORMAT_P030) {
++                              /*
++                               * Spec says: bits [31:4] of the given address
++                               * should point to the 128-bit word containing
++                               * the desired starting pixel, and bits[3:0]
++                               * should be between 0 and 11, indicating which
++                               * of the 12-pixels in that 128-bit word is the
++                               * first pixel to be used
++                               */
++                              u32 remaining_pixels = src_x % 96;
++                              u32 aligned = remaining_pixels / 12;
++                              u32 last_bits = remaining_pixels % 12;
++
++                              x_off = aligned * 16 + last_bits;
++                              tile_w = 128;
++                              pix_per_tile = 96;
++                      } else {
++                              switch (base_format_mod) {
++                              case DRM_FORMAT_MOD_BROADCOM_SAND128:
++                                      tile_w = 128;
++                                      break;
++                              case DRM_FORMAT_MOD_BROADCOM_SAND256:
++                                      tile_w = 256;
++                                      break;
++                              default:
++                                      return -EINVAL;
++                              }
++                              pix_per_tile = tile_w / fb->format->cpp[0];
++                              x_off = (src_x % pix_per_tile) /
++                                      (i ? h_subsample : 1) *
++                                      fb->format->cpp[i];
++                      }
++
++                      tile = src_x / pix_per_tile;
++
++                      offsets[i] += param * tile_w * tile;
++                      offsets[i] += src_y / (i ? v_subsample : 1) * tile_w;
++                      offsets[i] += x_off & ~(i ? 1 : 0);
++              }
++
++              components_per_word = fb->format->format == DRM_FORMAT_P030 ? 24 : 32;
++              starting_offset = src_x % components_per_word;
++              fetch_count = (width + starting_offset + components_per_word - 1) /
++                      components_per_word;
++
++              pitch0 = VC4_SET_FIELD(param, SCALER6_PTR2_PITCH) |
++                       VC4_SET_FIELD(fetch_count - 1, SCALER6_PTR2_FETCH_COUNT);
++              break;
++      }
++
++      default:
++              DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
++                            (long long)fb->modifier);
++              return -EINVAL;
++      }
++
++      /* fetch an extra pixel if we don't actually line up with the left edge. */
++      if ((vc4_state->src_x & 0xffff) && vc4_state->src_x < (state->fb->width << 16))
++              width++;
++
++      /* same for the right side */
++      if (((vc4_state->src_x + vc4_state->src_w[0]) & 0xffff) &&
++          vc4_state->src_x + vc4_state->src_w[0] < (state->fb->width << 16))
++              width++;
++
++      /* now for the top */
++      if ((vc4_state->src_y & 0xffff) && vc4_state->src_y < (state->fb->height << 16))
++              height++;
++
++      /* and the bottom */
++      if (((vc4_state->src_y + vc4_state->src_h[0]) & 0xffff) &&
++          vc4_state->src_y + vc4_state->src_h[0] < (state->fb->height << 16))
++              height++;
++
++      /* for YUV444 hardware wants double the width, otherwise it doesn't
++       * fetch full width of chroma
++       */
++      if (format->drm == DRM_FORMAT_YUV444 || format->drm == DRM_FORMAT_YVU444)
++              width <<= 1;
++
++      /* Don't waste cycles mixing with plane alpha if the set alpha
++       * is opaque or there is no per-pixel alpha information.
++       * In any case we use the alpha property value as the fixed alpha.
++       */
++      mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
++                        fb->format->has_alpha;
++
++      /* Control Word 0: Scaling Configuration & Element Validity*/
++      vc4_dlist_write(vc4_state,
++                      SCALER6_CTL0_VALID |
++                      VC4_SET_FIELD(tiling, SCALER6_CTL0_ADDR_MODE) |
++                      VC4_SET_FIELD(0, SCALER6_CTL0_ALPHA_MASK) |
++                      (vc4_state->is_unity ? SCALER6_CTL0_UNITY : 0) |
++                      VC4_SET_FIELD(format->pixel_order_hvs5, SCALER6_CTL0_ORDERRGBA) |
++                      VC4_SET_FIELD(scl1, SCALER6_CTL0_SCL1_MODE) |
++                      VC4_SET_FIELD(scl0, SCALER6_CTL0_SCL0_MODE) |
++                      VC4_SET_FIELD(hvs_format, SCALER6_CTL0_PIXEL_FORMAT));
++
++      /* Position Word 0: Image Position */
++      vc4_state->pos0_offset = vc4_state->dlist_count;
++      vc4_dlist_write(vc4_state,
++                      VC4_SET_FIELD(vc4_state->crtc_y, SCALER6_POS0_START_Y) |
++                      (rotation & DRM_MODE_REFLECT_X ? SCALER6_POS0_HFLIP : 0) |
++                      VC4_SET_FIELD(vc4_state->crtc_x, SCALER6_POS0_START_X));
++
++      /* Control Word 2: Alpha Value & CSC */
++      vc4_dlist_write(vc4_state,
++                      vc6_plane_get_csc_mode(vc4_state) |
++                      vc4_hvs5_get_alpha_blend_mode(state) |
++                      (mix_plane_alpha ? SCALER6_CTL2_ALPHA_MIX : 0) |
++                      VC4_SET_FIELD(state->alpha >> 4, SCALER5_CTL2_ALPHA));
++
++      /* Position Word 1: Scaled Image Dimensions */
++      if (!vc4_state->is_unity)
++              vc4_dlist_write(vc4_state,
++                              VC4_SET_FIELD(vc4_state->crtc_h - 1,
++                                            SCALER6_POS1_SCL_LINES) |
++                              VC4_SET_FIELD(vc4_state->crtc_w - 1,
++                                            SCALER6_POS1_SCL_WIDTH));
++
++      /* Position Word 2: Source Image Size */
++      vc4_state->pos2_offset = vc4_state->dlist_count;
++      vc4_dlist_write(vc4_state,
++                      VC4_SET_FIELD(height - 1,
++                                    SCALER6_POS2_SRC_LINES) |
++                      VC4_SET_FIELD(width - 1,
++                                    SCALER6_POS2_SRC_WIDTH));
++
++      /* Position Word 3: Context */
++      vc4_dlist_write(vc4_state, 0xc0c0c0c0);
++
++      /*
++       * TODO: This only covers Raster Scan Order planes
++       */
++      for (i = 0; i < num_planes; i++) {
++              dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++
++              paddr += offsets[i];
++
++              /* Pointer Word 0 */
++              vc4_state->ptr0_offset[i] = vc4_state->dlist_count;
++              vc4_dlist_write(vc4_state,
++                              (rotation & DRM_MODE_REFLECT_Y ? SCALER6_PTR0_VFLIP : 0) |
++                              /*
++                               * The UPM buffer will be allocated in
++                               * vc6_plane_allocate_upm().
++                               */
++                              VC4_SET_FIELD(upper_32_bits(paddr) & 0xf,
++                                            SCALER6_PTR0_UPPER_ADDR));
++
++              /* Pointer Word 1 */
++              vc4_dlist_write(vc4_state, lower_32_bits(paddr));
++
++              /* Pointer Word 2 */
++              if (base_format_mod != DRM_FORMAT_MOD_BROADCOM_SAND128 &&
++                  base_format_mod != DRM_FORMAT_MOD_BROADCOM_SAND256) {
++                      vc4_dlist_write(vc4_state,
++                                      VC4_SET_FIELD(fb->pitches[i],
++                                                    SCALER6_PTR2_PITCH));
++              } else {
++                      vc4_dlist_write(vc4_state, pitch0);
++              }
++      }
++
++      /*
++       * Palette Word 0
++       * TODO: We're not using the palette mode
++       */
++
++      /*
++       * Trans Word 0
++       * TODO: It's only relevant if we set the trans_rgb bit in the
++       * control word 0, and we don't at the moment.
++       */
++
++      vc4_state->lbm_offset = 0;
++
++      if (!vc4_state->is_unity || fb->format->is_yuv) {
++              /*
++               * Reserve a slot for the LBM Base Address. The real value will
++               * be set when calling vc4_plane_allocate_lbm().
++               */
++              if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
++                  vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
++                      vc4_state->lbm_offset = vc4_state->dlist_count;
++                      vc4_dlist_counter_increment(vc4_state);
++              }
++
++              if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
++                  vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
++                  vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
++                  vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
++                      if (num_planes > 1)
++                              /*
++                               * Emit Cb/Cr as channel 0 and Y as channel
++                               * 1. This matches how we set up scl0/scl1
++                               * above.
++                               */
++                              vc4_write_scaling_parameters(state, 1);
++
++                      vc4_write_scaling_parameters(state, 0);
++              }
++
++              /*
++               * If any PPF setup was done, then all the kernel
++               * pointers get uploaded.
++               */
++              if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
++                  vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
++                  vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
++                  vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
++                      u32 kernel =
++                              VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
++                                            SCALER_PPF_KERNEL_OFFSET);
++
++                      /* HPPF plane 0 */
++                      vc4_dlist_write(vc4_state, kernel);
++                      /* VPPF plane 0 */
++                      vc4_dlist_write(vc4_state, kernel);
++                      /* HPPF plane 1 */
++                      vc4_dlist_write(vc4_state, kernel);
++                      /* VPPF plane 1 */
++                              vc4_dlist_write(vc4_state, kernel);
++              }
++      }
++
++      vc4_dlist_write(vc4_state, SCALER6_CTL0_END);
++
++      vc4_state->dlist[0] |=
++              VC4_SET_FIELD(vc4_state->dlist_count, SCALER6_CTL0_NEXT);
++
++      /* crtc_* are already clipped coordinates. */
++      covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 &&
++                      vc4_state->crtc_w == state->crtc->mode.hdisplay &&
++                      vc4_state->crtc_h == state->crtc->mode.vdisplay;
++
++      /*
++       * Background fill might be necessary when the plane has per-pixel
++       * alpha content or a non-opaque plane alpha and could blend from the
++       * background or does not cover the entire screen.
++       */
++      vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen ||
++                                 state->alpha != DRM_BLEND_ALPHA_OPAQUE;
++
++      /*
++       * Flag the dlist as initialized to avoid checking it twice in case
++       * the async update check already called vc4_plane_mode_set() and
++       * decided to fallback to sync update because async update was not
++       * possible.
++       */
++      vc4_state->dlist_initialized = 1;
++
++      vc4_plane_calc_load(state);
++
++      drm_dbg_driver(drm, "[PLANE:%d:%s] Computed DLIST size: %u\n",
++                     plane->base.id, plane->name, vc4_state->dlist_count);
++
++      return 0;
++}
++
+ /* If a modeset involves changing the setup of a plane, the atomic
+  * infrastructure will call this to validate a proposed plane setup.
+  * However, if a plane isn't getting updated, this (and the
+@@ -1365,6 +1984,7 @@ static int vc4_plane_mode_set(struct drm
+ static int vc4_plane_atomic_check(struct drm_plane *plane,
+                                 struct drm_atomic_state *state)
+ {
++      struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(new_plane_state);
+@@ -1375,7 +1995,10 @@ static int vc4_plane_atomic_check(struct
+       if (!plane_enabled(new_plane_state))
+               return 0;
+-      ret = vc4_plane_mode_set(plane, new_plane_state);
++      if (vc4->gen >= VC4_GEN_6)
++              ret = vc6_plane_mode_set(plane, new_plane_state);
++      else
++              ret = vc4_plane_mode_set(plane, new_plane_state);
+       if (ret)
+               return ret;
+@@ -1383,6 +2006,12 @@ static int vc4_plane_atomic_check(struct
+       if (ret)
+               return ret;
++      if (vc4->gen >= VC4_GEN_6) {
++              ret = vc6_plane_allocate_upm(new_plane_state);
++              if (ret)
++                      return ret;
++      }
++
+       return 0;
+ }
+@@ -1716,7 +2345,7 @@ struct drm_plane *vc4_plane_init(struct
+       };
+       for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+-              if (!hvs_formats[i].hvs5_only || vc4->gen == VC4_GEN_5) {
++              if (!hvs_formats[i].hvs5_only || vc4->gen >= VC4_GEN_5) {
+                       formats[num_formats] = hvs_formats[i].drm;
+                       num_formats++;
+               }
+@@ -1731,7 +2360,7 @@ struct drm_plane *vc4_plane_init(struct
+               return ERR_CAST(vc4_plane);
+       plane = &vc4_plane->base;
+-      if (vc4->gen == VC4_GEN_5)
++      if (vc4->gen >= VC4_GEN_5)
+               drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
+       else
+               drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -536,6 +536,130 @@
+ #define SCALER5_DLIST_START                   0x00004000
++#define SCALER6_VERSION                               0x00000000
++#define SCALER6_CXM_SIZE                      0x00000004
++#define SCALER6_LBM_SIZE                      0x00000008
++#define SCALER6_UBM_SIZE                      0x0000000c
++#define SCALER6_COBA_SIZE                     0x00000010
++#define SCALER6_COB_SIZE                      0x00000014
++
++#define SCALER6_CONTROL                               0x00000020
++# define SCALER6_CONTROL_HVS_EN                       BIT(31)
++# define SCALER6_CONTROL_PF_LINES_MASK                VC4_MASK(22, 18)
++# define SCALER6_CONTROL_ABORT_ON_EMPTY               BIT(16)
++# define SCALER6_CONTROL_DSP1_TARGET_MASK     VC4_MASK(13, 12)
++# define SCALER6_CONTROL_MAX_REQS_MASK                VC4_MASK(7, 4)
++
++#define SCALER6_FETCHER_STATUS                        0x00000024
++#define SCALER6_FETCH_STATUS                  0x00000028
++#define SCALER6_HANDLE_ERROR                  0x0000002c
++
++#define SCALER6_DISP0_CTRL0                   0x00000030
++#define SCALER6_DISPX_CTRL0(x)                                                \
++      (SCALER6_DISP0_CTRL0 + ((x) * (SCALER6_DISP1_CTRL0 - SCALER6_DISP0_CTRL0)))
++# define SCALER6_DISPX_CTRL0_ENB              BIT(31)
++# define SCALER6_DISPX_CTRL0_RESET            BIT(30)
++# define SCALER6_DISPX_CTRL0_FWIDTH_MASK      VC4_MASK(28, 16)
++# define SCALER6_DISPX_CTRL0_ONESHOT          BIT(15)
++# define SCALER6_DISPX_CTRL0_ONECTX_MASK      VC4_MASK(14, 13)
++# define SCALER6_DISPX_CTRL0_LINES_MASK               VC4_MASK(12, 0)
++
++#define SCALER6_DISP0_CTRL1                   0x00000034
++#define SCALER6_DISPX_CTRL1(x)                                                \
++      (SCALER6_DISP0_CTRL1 + ((x) * (SCALER6_DISP1_CTRL1 - SCALER6_DISP0_CTRL1)))
++# define SCALER6_DISPX_CTRL1_BGENB            BIT(8)
++# define SCALER6_DISPX_CTRL1_INTLACE          BIT(0)
++
++#define SCALER6_DISP0_BGND                    0x00000038
++#define SCALER6_DISPX_BGND(x)                                         \
++      (SCALER6_DISP0_BGND + ((x) * (SCALER6_DISP1_BGND - SCALER6_DISP0_BGND)))
++
++#define SCALER6_DISP0_LPTRS                   0x0000003c
++#define SCALER6_DISPX_LPTRS(x)                                                \
++      (SCALER6_DISP0_LPTRS + ((x) * (SCALER6_DISP1_LPTRS - SCALER6_DISP0_LPTRS)))
++# define SCALER6_DISPX_LPTRS_HEADE_MASK               VC4_MASK(11, 0)
++
++#define SCALER6_DISP0_COB                     0x00000040
++#define SCALER6_DISPX_COB(x)                                          \
++      (SCALER6_DISP0_COB + ((x) * (SCALER6_DISP1_COB - SCALER6_DISP0_COB)))
++# define SCALER6_DISPX_COB_TOP_MASK           VC4_MASK(31, 16)
++# define SCALER6_DISPX_COB_BASE_MASK          VC4_MASK(15, 0)
++
++#define SCALER6_DISP0_STATUS                  0x00000044
++
++#define SCALER6_DISPX_STATUS(x)                                               \
++      (SCALER6_DISP0_STATUS + ((x) * (SCALER6_DISP1_STATUS - SCALER6_DISP0_STATUS)))
++# define SCALER6_DISPX_STATUS_EMPTY           BIT(22)
++# define SCALER6_DISPX_STATUS_FRCNT_MASK      VC4_MASK(21, 16)
++# define SCALER6_DISPX_STATUS_OFIELD          BIT(15)
++# define SCALER6_DISPX_STATUS_MODE_MASK               VC4_MASK(14, 13)
++# define SCALER6_DISPX_STATUS_MODE_DISABLED   0
++# define SCALER6_DISPX_STATUS_MODE_INIT               1
++# define SCALER6_DISPX_STATUS_MODE_RUN                2
++# define SCALER6_DISPX_STATUS_MODE_EOF                3
++# define SCALER6_DISPX_STATUS_YLINE_MASK      VC4_MASK(12, 0)
++
++#define SCALER6_DISP0_DL                      0x00000048
++
++#define SCALER6_DISPX_DL(x)                                           \
++      (SCALER6_DISP0_DL + ((x) * (SCALER6_DISP1_DL - SCALER6_DISP0_DL)))
++# define SCALER6_DISPX_DL_LACT_MASK           VC4_MASK(11, 0)
++
++#define SCALER6_DISP0_RUN                     0x0000004c
++#define SCALER6_DISP1_CTRL0                   0x00000050
++#define SCALER6_DISP1_CTRL1                   0x00000054
++#define SCALER6_DISP1_BGND                    0x00000058
++#define SCALER6_DISP1_LPTRS                   0x0000005c
++#define SCALER6_DISP1_COB                     0x00000060
++#define SCALER6_DISP1_STATUS                  0x00000064
++#define SCALER6_DISP1_DL                      0x00000068
++#define SCALER6_DISP1_RUN                     0x0000006c
++#define SCALER6_DISP2_CTRL0                   0x00000070
++#define SCALER6_DISP2_CTRL1                   0x00000074
++#define SCALER6_DISP2_BGND                    0x00000078
++#define SCALER6_DISP2_LPTRS                   0x0000007c
++#define SCALER6_DISP2_COB                     0x00000080
++#define SCALER6_DISP2_STATUS                  0x00000084
++#define SCALER6_DISP2_DL                      0x00000088
++#define SCALER6_DISP2_RUN                     0x0000008c
++#define SCALER6_EOLN                          0x00000090
++#define SCALER6_DL_STATUS                     0x00000094
++#define SCALER6_BFG_MISC                      0x0000009c
++#define SCALER6_QOS0                          0x000000a0
++#define SCALER6_PROF0                         0x000000a4
++#define SCALER6_QOS1                          0x000000a8
++#define SCALER6_PROF1                         0x000000ac
++#define SCALER6_QOS2                          0x000000b0
++#define SCALER6_PROF2                         0x000000b4
++#define SCALER6_PRI_MAP0                      0x000000b8
++#define SCALER6_PRI_MAP1                      0x000000bc
++#define SCALER6_HISTCTRL                      0x000000c0
++#define SCALER6_HISTBIN0                      0x000000c4
++#define SCALER6_HISTBIN1                      0x000000c8
++#define SCALER6_HISTBIN2                      0x000000cc
++#define SCALER6_HISTBIN3                      0x000000d0
++#define SCALER6_HISTBIN4                      0x000000d4
++#define SCALER6_HISTBIN5                      0x000000d8
++#define SCALER6_HISTBIN6                      0x000000dc
++#define SCALER6_HISTBIN7                      0x000000e0
++#define SCALER6_HDR_CFG_REMAP                 0x000000f4
++#define SCALER6_COL_SPACE                     0x000000f8
++#define SCALER6_HVS_ID                                0x000000fc
++#define SCALER6_CFC1                          0x00000100
++#define SCALER6_DISP_UPM_ISO0                 0x00000200
++#define SCALER6_DISP_UPM_ISO1                 0x00000204
++#define SCALER6_DISP_UPM_ISO2                 0x00000208
++#define SCALER6_DISP_LBM_ISO0                 0x0000020c
++#define SCALER6_DISP_LBM_ISO1                 0x00000210
++#define SCALER6_DISP_LBM_ISO2                 0x00000214
++#define SCALER6_DISP_COB_ISO0                 0x00000218
++#define SCALER6_DISP_COB_ISO1                 0x0000021c
++#define SCALER6_DISP_COB_ISO2                 0x00000220
++#define SCALER6_BAD_COB                               0x00000224
++#define SCALER6_BAD_LBM                               0x00000228
++#define SCALER6_BAD_UPM                               0x0000022c
++#define SCALER6_BAD_AXI                               0x00000230
++
+ # define VC4_HDMI_SW_RESET_FORMAT_DETECT      BIT(1)
+ # define VC4_HDMI_SW_RESET_HDMI                       BIT(0)
+@@ -1131,4 +1255,61 @@ enum hvs_pixel_format {
+ #define SCALER_PITCH0_TILE_WIDTH_R_MASK               VC4_MASK(6, 0)
+ #define SCALER_PITCH0_TILE_WIDTH_R_SHIFT      0
++#define SCALER6_CTL0_END                      BIT(31)
++#define SCALER6_CTL0_VALID                    BIT(30)
++#define SCALER6_CTL0_NEXT_MASK                        VC4_MASK(29, 24)
++#define SCALER6_CTL0_RGB_TRANS                        BIT(23)
++#define SCALER6_CTL0_ADDR_MODE_MASK           VC4_MASK(22, 20)
++#define SCALER6_CTL0_ADDR_MODE_LINEAR         0
++#define SCALER6_CTL0_ADDR_MODE_128B           1
++#define SCALER6_CTL0_ADDR_MODE_256B           2
++#define SCALER6_CTL0_ADDR_MODE_MAP8           3
++#define SCALER6_CTL0_ADDR_MODE_UIF            4
++
++#define SCALER6_CTL0_ALPHA_MASK_MASK          VC4_MASK(19, 18)
++#define SCALER6_CTL0_UNITY                    BIT(15)
++#define SCALER6_CTL0_ORDERRGBA_MASK           VC4_MASK(14, 13)
++#define SCALER6_CTL0_SCL1_MODE_MASK           VC4_MASK(10, 8)
++#define SCALER6_CTL0_SCL0_MODE_MASK           VC4_MASK(7, 5)
++#define SCALER6_CTL0_PIXEL_FORMAT_MASK                VC4_MASK(4, 0)
++
++#define SCALER6_POS0_START_Y_MASK             VC4_MASK(28, 16)
++#define SCALER6_POS0_HFLIP                    BIT(15)
++#define SCALER6_POS0_START_X_MASK             VC4_MASK(12, 0)
++
++#define SCALER6_CTL2_ALPHA_MODE_MASK          VC4_MASK(31, 30)
++#define SCALER6_CTL2_ALPHA_PREMULT            BIT(29)
++#define SCALER6_CTL2_ALPHA_MIX                        BIT(28)
++#define SCALER6_CTL2_BFG                      BIT(26)
++#define SCALER6_CTL2_CSC_ENABLE                       BIT(25)
++#define SCALER6_CTL2_BRCM_CFC_CONTROL_MASK    VC4_MASK(18, 16)
++#define SCALER6_CTL2_ALPHA_MASK                       VC4_MASK(15, 4)
++
++#define SCALER6_POS1_SCL_LINES_MASK           VC4_MASK(28, 16)
++#define SCALER6_POS1_SCL_WIDTH_MASK           VC4_MASK(12, 0)
++
++#define SCALER6_POS2_SRC_LINES_MASK           VC4_MASK(28, 16)
++#define SCALER6_POS2_SRC_WIDTH_MASK           VC4_MASK(12, 0)
++
++#define SCALER6_PTR0_VFLIP                    BIT(31)
++#define SCALER6_PTR0_UPM_BASE_MASK            VC4_MASK(28, 16)
++#define SCALER6_PTR0_UPM_HANDLE_MASK          VC4_MASK(14, 10)
++#define SCALER6_PTR0_UPM_BUFF_SIZE_MASK               VC4_MASK(9, 8)
++#define SCALER6_PTR0_UPM_BUFF_SIZE_16_LINES   3
++#define SCALER6_PTR0_UPM_BUFF_SIZE_8_LINES    2
++#define SCALER6_PTR0_UPM_BUFF_SIZE_4_LINES    1
++#define SCALER6_PTR0_UPM_BUFF_SIZE_2_LINES    0
++#define SCALER6_PTR0_UPPER_ADDR_MASK          VC4_MASK(7, 0)
++
++#define SCALER6_PTR2_ALPHA_BPP_MASK           VC4_MASK(31, 31)
++#define SCALER6_PTR2_ALPHA_BPP_1BPP           1
++#define SCALER6_PTR2_ALPHA_BPP_8BPP           0
++#define SCALER6_PTR2_ALPHA_ORDER_MASK         VC4_MASK(30, 30)
++#define SCALER6_PTR2_ALPHA_ORDER_MSB_TO_LSB   1
++#define SCALER6_PTR2_ALPHA_ORDER_LSB_TO_MSB   0
++#define SCALER6_PTR2_ALPHA_OFFS_MASK          VC4_MASK(29, 27)
++#define SCALER6_PTR2_LSKIP_MASK                       VC4_MASK(26, 24)
++#define SCALER6_PTR2_PITCH_MASK                       VC4_MASK(16, 0)
++#define SCALER6_PTR2_FETCH_COUNT_MASK         VC4_MASK(26, 16)
++
+ #endif /* VC4_REGS_H */
diff --git a/target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch b/target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch
new file mode 100644 (file)
index 0000000..1870158
--- /dev/null
@@ -0,0 +1,144 @@
+From 000f1b7d4dc5b515c755ee25db301e26bded00e1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:33:23 +0100
+Subject: [PATCH] drm/vc4: crtc: Add support for BCM2712 PixelValves
+
+The PixelValves found on the BCM2712 are similar to the ones found in
+the previous generation.
+
+Compared to BCM2711, the pixelvalves only drive one HDMI controller each
+and HDMI1 PixelValve has a FIFO long enough to support 4k at 60Hz.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 53 ++++++++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_drv.h  |  2 ++
+ drivers/gpu/drm/vc4/vc4_regs.h |  5 ++++
+ 3 files changed, 58 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -239,6 +239,11 @@ static u32 vc4_get_fifo_full_level(struc
+       const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
+       const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
+       struct vc4_dev *vc4 = to_vc4_dev(vc4_crtc->base.dev);
++
++      /*
++       * NOTE: Could we use register 0x68 (PV_HW_CFG1) to get the FIFO
++       * size?
++       */
+       u32 fifo_len_bytes = pv_data->fifo_depth;
+       /*
+@@ -393,6 +398,12 @@ static void vc4_crtc_config_pv(struct dr
+       vc4_crtc_pixelvalve_reset(crtc);
++      /*
++       * NOTE: The BCM2712 has a H_OTE (Horizontal Odd Timing Enable)
++       * bit that, when set, will allow to specify the timings in
++       * pixels instead of cycles, thus allowing to specify odd
++       * timings.
++       */
+       CRTC_WRITE(PV_HORZA,
+                  VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+                                PV_HORZA_HBP) |
+@@ -462,11 +473,17 @@ static void vc4_crtc_config_pv(struct dr
+       if (is_dsi)
+               CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
+-      if (vc4->gen == VC4_GEN_5)
++      if (vc4->gen >= VC4_GEN_5)
+               CRTC_WRITE(PV_MUX_CFG,
+                          VC4_SET_FIELD(PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP,
+                                        PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
++      if (vc4->gen >= VC4_GEN_6)
++              CRTC_WRITE(PV_PIPE_INIT_CTRL,
++                         VC4_SET_FIELD(1, PV_PIPE_INIT_CTRL_PV_INIT_WIDTH) |
++                         VC4_SET_FIELD(1, PV_PIPE_INIT_CTRL_PV_INIT_IDLE) |
++                         PV_PIPE_INIT_CTRL_PV_INIT_EN);
++
+       CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR |
+                  vc4_crtc_get_fifo_full_level_bits(vc4_crtc, format) |
+                  VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+@@ -565,7 +582,11 @@ int vc4_crtc_disable_at_boot(struct drm_
+       if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+                                     "brcm,bcm2711-pixelvalve2") ||
+             of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+-                                    "brcm,bcm2711-pixelvalve4")))
++                                    "brcm,bcm2711-pixelvalve4") ||
++            of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
++                                    "brcm,bcm2712-pixelvalve0") ||
++            of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
++                                    "brcm,bcm2712-pixelvalve1")))
+               return 0;
+       if (!(CRTC_READ(PV_CONTROL) & PV_CONTROL_EN))
+@@ -1304,6 +1325,32 @@ const struct vc4_pv_data bcm2711_pv4_dat
+       },
+ };
++const struct vc4_pv_data bcm2712_pv0_data = {
++      .base = {
++              .debugfs_name = "crtc0_regs",
++              .hvs_available_channels = BIT(0),
++              .hvs_output = 0,
++      },
++      .fifo_depth = 64,
++      .pixels_per_clock = 2,
++      .encoder_types = {
++              [0] = VC4_ENCODER_TYPE_HDMI0,
++      },
++};
++
++const struct vc4_pv_data bcm2712_pv1_data = {
++      .base = {
++              .debugfs_name = "crtc1_regs",
++              .hvs_available_channels = BIT(1),
++              .hvs_output = 1,
++      },
++      .fifo_depth = 64,
++      .pixels_per_clock = 2,
++      .encoder_types = {
++              [0] = VC4_ENCODER_TYPE_HDMI1,
++      },
++};
++
+ static const struct of_device_id vc4_crtc_dt_match[] = {
+       { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data },
+       { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data },
+@@ -1313,6 +1360,8 @@ static const struct of_device_id vc4_crt
+       { .compatible = "brcm,bcm2711-pixelvalve2", .data = &bcm2711_pv2_data },
+       { .compatible = "brcm,bcm2711-pixelvalve3", .data = &bcm2711_pv3_data },
+       { .compatible = "brcm,bcm2711-pixelvalve4", .data = &bcm2711_pv4_data },
++      { .compatible = "brcm,bcm2712-pixelvalve0", .data = &bcm2712_pv0_data },
++      { .compatible = "brcm,bcm2712-pixelvalve1", .data = &bcm2712_pv1_data },
+       {}
+ };
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -583,6 +583,8 @@ extern const struct vc4_pv_data bcm2711_
+ extern const struct vc4_pv_data bcm2711_pv2_data;
+ extern const struct vc4_pv_data bcm2711_pv3_data;
+ extern const struct vc4_pv_data bcm2711_pv4_data;
++extern const struct vc4_pv_data bcm2712_pv0_data;
++extern const struct vc4_pv_data bcm2712_pv1_data;
+ struct vc5_gamma_entry {
+       u32 x_c_terms;
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -215,6 +215,11 @@
+ # define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_SHIFT  2
+ # define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP        8
++#define PV_PIPE_INIT_CTRL                     0x94
++# define PV_PIPE_INIT_CTRL_PV_INIT_WIDTH_MASK VC4_MASK(11, 8)
++# define PV_PIPE_INIT_CTRL_PV_INIT_IDLE_MASK  VC4_MASK(7, 4)
++# define PV_PIPE_INIT_CTRL_PV_INIT_EN         BIT(0)
++
+ #define SCALER_CHANNELS_COUNT                 3
+ #define SCALER_DISPCTRL                         0x00000000
diff --git a/target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch b/target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch
new file mode 100644 (file)
index 0000000..7cb4e56
--- /dev/null
@@ -0,0 +1,1057 @@
+From 1bb54596ae2a9a36f4aa9f8f2ba941320f463811 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:34:30 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add support for BCM2712 HDMI controllers
+
+The HDMI controllers found in the BCM2712 are largely the ones found in
+the BCM2711 with a different PHY.
+
+There's some difference with how timings are split between registers,
+and HDMI1 is now able to run at 4k/60Hz.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c      |  82 +++-
+ drivers/gpu/drm/vc4/vc4_hdmi.h      |   4 +
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c  | 640 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 217 ++++++++++
+ 4 files changed, 937 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1127,6 +1127,7 @@ static void vc4_hdmi_encoder_post_crtc_d
+ {
+       struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+       struct drm_device *drm = vc4_hdmi->connector.dev;
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
+       unsigned long flags;
+       int idx;
+@@ -1143,14 +1144,25 @@ static void vc4_hdmi_encoder_post_crtc_d
+       HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB);
++      if (vc4->gen >= VC4_GEN_6)
++              HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) |
++                         VC4_HD_VID_CTL_BLANKPIX);
++
+       spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+       mdelay(1);
+-      spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+-      HDMI_WRITE(HDMI_VID_CTL,
+-                 HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+-      spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++      /*
++       * TODO: This should work on BCM2712, but doesn't for some
++       * reason and result in a system lockup.
++       */
++      if (vc4->gen < VC4_GEN_6) {
++              spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++              HDMI_WRITE(HDMI_VID_CTL,
++                         HDMI_READ(HDMI_VID_CTL) &
++                         ~VC4_HD_VID_CTL_ENABLE);
++              spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++      }
+       vc4_hdmi_disable_scrambling(encoder);
+@@ -1757,7 +1769,6 @@ static void vc4_hdmi_encoder_pre_crtc_co
+               goto err_put_runtime_pm;
+       }
+-
+       vc4_hdmi_cec_update_clk_div(vc4_hdmi);
+       if (tmds_char_rate > 297000000)
+@@ -1862,6 +1873,7 @@ static void vc4_hdmi_encoder_post_crtc_e
+       spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+       HDMI_WRITE(HDMI_VID_CTL,
++                 HDMI_READ(HDMI_VID_CTL) |
+                  VC4_HD_VID_CTL_ENABLE |
+                  VC4_HD_VID_CTL_CLRRGB |
+                  VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+@@ -3796,7 +3808,9 @@ static int vc4_hdmi_bind(struct device *
+               return ret;
+       if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") ||
+-           of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) &&
++           of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1") ||
++           of_device_is_compatible(dev->of_node, "brcm,bcm2712-hdmi0") ||
++           of_device_is_compatible(dev->of_node, "brcm,bcm2712-hdmi1")) &&
+           HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) {
+               clk_prepare_enable(vc4_hdmi->pixel_clock);
+               clk_prepare_enable(vc4_hdmi->hsm_clock);
+@@ -3931,10 +3945,66 @@ static const struct vc4_hdmi_variant bcm
+       .hp_detect              = vc5_hdmi_hp_detect,
+ };
++static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
++      .encoder_type           = VC4_ENCODER_TYPE_HDMI0,
++      .debugfs_name           = "hdmi0_regs",
++      .card_name              = "vc4-hdmi-0",
++      .max_pixel_clock        = 600000000,
++      .registers              = vc6_hdmi_hdmi0_fields,
++      .num_registers          = ARRAY_SIZE(vc6_hdmi_hdmi0_fields),
++      .phy_lane_mapping       = {
++              PHY_LANE_0,
++              PHY_LANE_1,
++              PHY_LANE_2,
++              PHY_LANE_CK,
++      },
++      .unsupported_odd_h_timings      = true,
++      .external_irq_controller        = true,
++
++      .init_resources         = vc5_hdmi_init_resources,
++      .csc_setup              = vc5_hdmi_csc_setup,
++      .reset                  = vc5_hdmi_reset,
++      .set_timings            = vc5_hdmi_set_timings,
++      .phy_init               = vc6_hdmi_phy_init,
++      .phy_disable            = vc6_hdmi_phy_disable,
++      .channel_map            = vc5_hdmi_channel_map,
++      .supports_hdr           = true,
++      .hp_detect              = vc5_hdmi_hp_detect,
++};
++
++static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
++      .encoder_type           = VC4_ENCODER_TYPE_HDMI1,
++      .debugfs_name           = "hdmi1_regs",
++      .card_name              = "vc4-hdmi-1",
++      .max_pixel_clock        = 600000000,
++      .registers              = vc6_hdmi_hdmi1_fields,
++      .num_registers          = ARRAY_SIZE(vc6_hdmi_hdmi1_fields),
++      .phy_lane_mapping       = {
++              PHY_LANE_0,
++              PHY_LANE_1,
++              PHY_LANE_2,
++              PHY_LANE_CK,
++      },
++      .unsupported_odd_h_timings      = true,
++      .external_irq_controller        = true,
++
++      .init_resources         = vc5_hdmi_init_resources,
++      .csc_setup              = vc5_hdmi_csc_setup,
++      .reset                  = vc5_hdmi_reset,
++      .set_timings            = vc5_hdmi_set_timings,
++      .phy_init               = vc6_hdmi_phy_init,
++      .phy_disable            = vc6_hdmi_phy_disable,
++      .channel_map            = vc5_hdmi_channel_map,
++      .supports_hdr           = true,
++      .hp_detect              = vc5_hdmi_hp_detect,
++};
++
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+       { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant },
+       { .compatible = "brcm,bcm2711-hdmi0", .data = &bcm2711_hdmi0_variant },
+       { .compatible = "brcm,bcm2711-hdmi1", .data = &bcm2711_hdmi1_variant },
++      { .compatible = "brcm,bcm2712-hdmi0", .data = &bcm2712_hdmi0_variant },
++      { .compatible = "brcm,bcm2712-hdmi1", .data = &bcm2712_hdmi1_variant },
+       {}
+ };
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -284,4 +284,8 @@ void vc5_hdmi_phy_disable(struct vc4_hdm
+ void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
+ void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
++void vc6_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++                     struct vc4_hdmi_connector_state *vc4_conn_state);
++void vc6_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
++
+ #endif /* _VC4_HDMI_H_ */
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -125,6 +125,48 @@
+ #define VC4_HDMI_RM_FORMAT_SHIFT_SHIFT                        24
+ #define VC4_HDMI_RM_FORMAT_SHIFT_MASK                 VC4_MASK(25, 24)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BG_PWRUP     BIT(8)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_LDO_PWRUP    BIT(7)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BIAS_PWRUP   BIT(6)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_RNDGEN_PWRUP BIT(4)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_CK_PWRUP  BIT(3)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_2_PWRUP   BIT(2)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_1_PWRUP   BIT(1)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_0_PWRUP   BIT(0)
++
++#define VC6_HDMI_TX_PHY_PLL_REFCLK_REFCLK_SEL_CMOS    BIT(13)
++#define VC6_HDMI_TX_PHY_PLL_REFCLK_REFFRQ_MASK                VC4_MASK(9, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_POST_KDIV_CLK0_SEL_MASK   VC4_MASK(3, 2)
++#define VC6_HDMI_TX_PHY_PLL_POST_KDIV_KDIV_MASK               VC4_MASK(1, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_EN      BIT(10)
++#define VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_MASK    VC4_MASK(9, 0)
++
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL_MASK    VC4_MASK(31, 28)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE_MASK         VC4_MASK(27, 27)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL_MASK      VC4_MASK(26, 26)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN_MASK    VC4_MASK(25, 25)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL_MASK     VC4_MASK(24, 23)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN_MASK  VC4_MASK(22, 22)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL_MASK           VC4_MASK(21, 21)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN_MASK      VC4_MASK(20, 20)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL_MASK           VC4_MASK(19, 18)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN_MASK VC4_MASK(17, 17)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN_MASK VC4_MASK(16, 16)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL_MASK    VC4_MASK(15, 12)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN_MASK      VC4_MASK(11, 11)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT_MASK    VC4_MASK(10, 8)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT_MASK    VC4_MASK(7, 5)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING_MASK      VC4_MASK(4, 3)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING_MASK      VC4_MASK(2, 1)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN_MASK     VC4_MASK(0, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_PLLPOST_RESETB      BIT(1)
++#define VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB      BIT(0)
++
++#define VC6_HDMI_TX_PHY_PLL_POWERUP_CTL_PLL_PWRUP     BIT(0)
++
+ #define OSCILLATOR_FREQUENCY  54000000
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+@@ -558,3 +600,601 @@ void vc5_hdmi_phy_rng_disable(struct vc4
+                  VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
+       spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
++
++#define VC6_VCO_MIN_FREQ      (8ULL * 1000 * 1000 * 1000)
++#define VC6_VCO_MAX_FREQ      (12ULL * 1000 * 1000 * 1000)
++
++static unsigned long long
++vc6_phy_get_vco_freq(unsigned long long tmds_rate, unsigned int *vco_div)
++{
++      unsigned int min_div;
++      unsigned int max_div;
++      unsigned int div;
++
++      div = 0;
++      while (tmds_rate * div * 10 < VC6_VCO_MIN_FREQ)
++              div++;
++      min_div = div;
++
++      while (tmds_rate * (div + 1) * 10 < VC6_VCO_MAX_FREQ)
++              div++;
++      max_div = div;
++
++      div = min_div + (max_div - min_div) / 2;
++
++      *vco_div = div;
++      return tmds_rate * div * 10;
++}
++
++struct vc6_phy_lane_settings {
++      unsigned int ext_current_ctl:4;
++      unsigned int ffe_enable:1;
++      unsigned int slew_rate_ctl:1;
++      unsigned int ffe_post_tap_en:1;
++      unsigned int ldmos_bias_ctl:2;
++      unsigned int com_mode_ldmos_en:1;
++      unsigned int edge_sel:1;
++      unsigned int ext_current_src_hs_en:1;
++      unsigned int term_ctl:2;
++      unsigned int ext_current_src_en:1;
++      unsigned int int_current_src_en:1;
++      unsigned int int_current_ctl:4;
++      unsigned int int_current_src_hs_en:1;
++      unsigned int main_tap_current_select:3;
++      unsigned int post_tap_current_select:3;
++      unsigned int slew_ctl_slow_loading:2;
++      unsigned int slew_ctl_slow_driving:2;
++      unsigned int ffe_pre_tap_en:1;
++};
++
++struct vc6_phy_settings {
++      unsigned long long min_rate;
++      unsigned long long max_rate;
++      struct vc6_phy_lane_settings channel[3];
++      struct vc6_phy_lane_settings clock;
++};
++
++static const struct vc6_phy_settings vc6_hdmi_phy_settings[] = {
++      {
++              0, 222000000,
++              {
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++              },
++              {
++                      /* 200mA */
++                      .ext_current_ctl = 8,
++
++                      /* 0.85V */
++                      .ldmos_bias_ctl = 1,
++
++                      /* Enable External Current Source */
++                      .ext_current_src_en = 1,
++
++                      /* 200mA */
++                      .int_current_ctl = 8,
++
++                      /* 17.6 mA */
++                      .main_tap_current_select = 7,
++              },
++      },
++      {
++              222000001, 297000000,
++              {
++                      {
++                              /* 200mA and 180mA ?! */
++                              .ext_current_ctl = 12,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 100 Ohm */
++                              .term_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++                      },
++                      {
++                              /* 200mA and 180mA ?! */
++                              .ext_current_ctl = 12,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 100 Ohm */
++                              .term_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++                      },
++                      {
++                              /* 200mA and 180mA ?! */
++                              .ext_current_ctl = 12,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 100 Ohm */
++                              .term_ctl = 1,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++                      },
++              },
++              {
++                      /* 200mA and 180mA ?! */
++                      .ext_current_ctl = 12,
++
++                      /* 0.85V */
++                      .ldmos_bias_ctl = 1,
++
++                      /* 100 Ohm */
++                      .term_ctl = 1,
++
++                      /* Enable External Current Source */
++                      .ext_current_src_en = 1,
++
++                      /* Enable Internal Current Source */
++                      .int_current_src_en = 1,
++
++                      /* Internal Current Source Half Swing Enable*/
++                      .int_current_src_hs_en = 1,
++              },
++      },
++      {
++              297000001, 597000044,
++              {
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* Normal Slew Rate Control */
++                              .slew_rate_ctl = 1,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 50 Ohms */
++                              .term_ctl = 3,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* Normal Slew Rate Control */
++                              .slew_rate_ctl = 1,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 50 Ohms */
++                              .term_ctl = 3,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++                      {
++                              /* 200mA */
++                              .ext_current_ctl = 8,
++
++                              /* Normal Slew Rate Control */
++                              .slew_rate_ctl = 1,
++
++                              /* 0.85V */
++                              .ldmos_bias_ctl = 1,
++
++                              /* 50 Ohms */
++                              .term_ctl = 3,
++
++                              /* Enable External Current Source */
++                              .ext_current_src_en = 1,
++
++                              /* Enable Internal Current Source */
++                              .int_current_src_en = 1,
++
++                              /* 200mA */
++                              .int_current_ctl = 8,
++
++                              /* 17.6 mA */
++                              .main_tap_current_select = 7,
++                      },
++              },
++              {
++                      /* 200mA */
++                      .ext_current_ctl = 8,
++
++                      /* Normal Slew Rate Control */
++                      .slew_rate_ctl = 1,
++
++                      /* 0.85V */
++                      .ldmos_bias_ctl = 1,
++
++                      /* External Current Source Half Swing Enable*/
++                      .ext_current_src_hs_en = 1,
++
++                      /* 50 Ohms */
++                      .term_ctl = 3,
++
++                      /* Enable External Current Source */
++                      .ext_current_src_en = 1,
++
++                      /* Enable Internal Current Source */
++                      .int_current_src_en = 1,
++
++                      /* 200mA */
++                      .int_current_ctl = 8,
++
++                      /* Internal Current Source Half Swing Enable*/
++                      .int_current_src_hs_en = 1,
++
++                      /* 17.6 mA */
++                      .main_tap_current_select = 7,
++              },
++      },
++};
++
++static const struct vc6_phy_settings *
++vc6_phy_get_settings(unsigned long long tmds_rate)
++{
++      unsigned int count = ARRAY_SIZE(vc6_hdmi_phy_settings);
++      unsigned int i;
++
++      for (i = 0; i < count; i++) {
++              const struct vc6_phy_settings *s = &vc6_hdmi_phy_settings[i];
++
++              if (tmds_rate >= s->min_rate && tmds_rate <= s->max_rate)
++                      return s;
++      }
++
++      /*
++       * If the pixel clock exceeds our max setting, try the max
++       * setting anyway.
++       */
++      return &vc6_hdmi_phy_settings[count - 1];
++}
++
++static const struct vc6_phy_lane_settings *
++vc6_phy_get_channel_settings(enum vc4_hdmi_phy_channel chan,
++                           unsigned long long tmds_rate)
++{
++      const struct vc6_phy_settings *settings = vc6_phy_get_settings(tmds_rate);
++
++      if (chan == PHY_LANE_CK)
++              return &settings->clock;
++
++      return &settings->channel[chan];
++}
++
++static void vc6_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
++{
++      lockdep_assert_held(&vc4_hdmi->hw_lock);
++
++      HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
++      HDMI_WRITE(HDMI_TX_PHY_POWERUP_CTL, 0);
++}
++
++void vc6_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++                     struct vc4_hdmi_connector_state *conn_state)
++{
++      const struct vc6_phy_lane_settings *chan0_settings;
++      const struct vc6_phy_lane_settings *chan1_settings;
++      const struct vc6_phy_lane_settings *chan2_settings;
++      const struct vc6_phy_lane_settings *clock_settings;
++      const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
++      unsigned long long pixel_freq = conn_state->tmds_char_rate;
++      unsigned long long vco_freq;
++      unsigned char word_sel;
++      unsigned long flags;
++      unsigned int vco_div;
++
++      vco_freq = vc6_phy_get_vco_freq(pixel_freq, &vco_div);
++
++      spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
++      vc6_hdmi_reset_phy(vc4_hdmi);
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_0, 0x810c6000);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_1, 0x00b8c451);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_2, 0x46402e31);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_3, 0x00b8c005);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_4, 0x42410261);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_5, 0xcc021001);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_6, 0xc8301c80);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_7, 0xb0804444);
++      HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_8, 0xf80f8000);
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_REFCLK,
++                 VC6_HDMI_TX_PHY_PLL_REFCLK_REFCLK_SEL_CMOS |
++                 VC4_SET_FIELD(54, VC6_HDMI_TX_PHY_PLL_REFCLK_REFFRQ));
++
++      HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x7f);
++
++      HDMI_WRITE(HDMI_RM_OFFSET,
++                 VC4_HDMI_RM_OFFSET_ONLY |
++                 VC4_SET_FIELD(phy_get_rm_offset(vco_freq),
++                               VC4_HDMI_RM_OFFSET_OFFSET));
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_VCOCLK_DIV,
++                 VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_EN |
++                 VC4_SET_FIELD(vco_div,
++                               VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV));
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_CFG,
++                 VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CFG_PDIV));
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_POST_KDIV,
++                 VC4_SET_FIELD(2, VC6_HDMI_TX_PHY_PLL_POST_KDIV_CLK0_SEL) |
++                 VC4_SET_FIELD(1, VC6_HDMI_TX_PHY_PLL_POST_KDIV_KDIV));
++
++      chan0_settings =
++              vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_0],
++                                           pixel_freq);
++      HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++                 VC4_SET_FIELD(chan0_settings->ext_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan0_settings->ffe_enable,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++                 VC4_SET_FIELD(chan0_settings->slew_rate_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++                 VC4_SET_FIELD(chan0_settings->ffe_post_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++                 VC4_SET_FIELD(chan0_settings->ldmos_bias_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++                 VC4_SET_FIELD(chan0_settings->com_mode_ldmos_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++                 VC4_SET_FIELD(chan0_settings->edge_sel,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++                 VC4_SET_FIELD(chan0_settings->ext_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan0_settings->term_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++                 VC4_SET_FIELD(chan0_settings->ext_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan0_settings->int_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan0_settings->int_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan0_settings->int_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan0_settings->main_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan0_settings->post_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan0_settings->slew_ctl_slow_loading,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++                 VC4_SET_FIELD(chan0_settings->slew_ctl_slow_driving,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++                 VC4_SET_FIELD(chan0_settings->ffe_pre_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++      chan1_settings =
++              vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_1],
++                                           pixel_freq);
++      HDMI_WRITE(HDMI_TX_PHY_CTL_1,
++                 VC4_SET_FIELD(chan1_settings->ext_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan1_settings->ffe_enable,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++                 VC4_SET_FIELD(chan1_settings->slew_rate_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++                 VC4_SET_FIELD(chan1_settings->ffe_post_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++                 VC4_SET_FIELD(chan1_settings->ldmos_bias_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++                 VC4_SET_FIELD(chan1_settings->com_mode_ldmos_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++                 VC4_SET_FIELD(chan1_settings->edge_sel,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++                 VC4_SET_FIELD(chan1_settings->ext_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan1_settings->term_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++                 VC4_SET_FIELD(chan1_settings->ext_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan1_settings->int_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan1_settings->int_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan1_settings->int_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan1_settings->main_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan1_settings->post_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan1_settings->slew_ctl_slow_loading,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++                 VC4_SET_FIELD(chan1_settings->slew_ctl_slow_driving,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++                 VC4_SET_FIELD(chan1_settings->ffe_pre_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++      chan2_settings =
++              vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_2],
++                                           pixel_freq);
++      HDMI_WRITE(HDMI_TX_PHY_CTL_2,
++                 VC4_SET_FIELD(chan2_settings->ext_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan2_settings->ffe_enable,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++                 VC4_SET_FIELD(chan2_settings->slew_rate_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++                 VC4_SET_FIELD(chan2_settings->ffe_post_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++                 VC4_SET_FIELD(chan2_settings->ldmos_bias_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++                 VC4_SET_FIELD(chan2_settings->com_mode_ldmos_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++                 VC4_SET_FIELD(chan2_settings->edge_sel,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++                 VC4_SET_FIELD(chan2_settings->ext_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan2_settings->term_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++                 VC4_SET_FIELD(chan2_settings->ext_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan2_settings->int_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(chan2_settings->int_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++                 VC4_SET_FIELD(chan2_settings->int_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(chan2_settings->main_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan2_settings->post_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(chan2_settings->slew_ctl_slow_loading,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++                 VC4_SET_FIELD(chan2_settings->slew_ctl_slow_driving,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++                 VC4_SET_FIELD(chan2_settings->ffe_pre_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++      clock_settings =
++              vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_CK],
++                                           pixel_freq);
++      HDMI_WRITE(HDMI_TX_PHY_CTL_CK,
++                 VC4_SET_FIELD(clock_settings->ext_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++                 VC4_SET_FIELD(clock_settings->ffe_enable,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++                 VC4_SET_FIELD(clock_settings->slew_rate_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++                 VC4_SET_FIELD(clock_settings->ffe_post_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++                 VC4_SET_FIELD(clock_settings->ldmos_bias_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++                 VC4_SET_FIELD(clock_settings->com_mode_ldmos_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++                 VC4_SET_FIELD(clock_settings->edge_sel,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++                 VC4_SET_FIELD(clock_settings->ext_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(clock_settings->term_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++                 VC4_SET_FIELD(clock_settings->ext_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(clock_settings->int_current_src_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++                 VC4_SET_FIELD(clock_settings->int_current_ctl,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++                 VC4_SET_FIELD(clock_settings->int_current_src_hs_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++                 VC4_SET_FIELD(clock_settings->main_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(clock_settings->post_tap_current_select,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++                 VC4_SET_FIELD(clock_settings->slew_ctl_slow_loading,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++                 VC4_SET_FIELD(clock_settings->slew_ctl_slow_driving,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++                 VC4_SET_FIELD(clock_settings->ffe_pre_tap_en,
++                               VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++      if (pixel_freq >= 340000000)
++              word_sel = 3;
++      else
++              word_sel = 0;
++      HDMI_WRITE(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, word_sel);
++
++      HDMI_WRITE(HDMI_TX_PHY_POWERUP_CTL,
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BG_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_LDO_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BIAS_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_CK_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_2_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_1_PWRUP |
++                 VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_0_PWRUP);
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_POWERUP_CTL,
++                 VC6_HDMI_TX_PHY_PLL_POWERUP_CTL_PLL_PWRUP);
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_RESET_CTL,
++                 HDMI_READ(HDMI_TX_PHY_PLL_RESET_CTL) &
++                 ~VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB);
++
++      HDMI_WRITE(HDMI_TX_PHY_PLL_RESET_CTL,
++                 HDMI_READ(HDMI_TX_PHY_PLL_RESET_CTL) |
++                 VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB);
++
++      spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++}
++
++void vc6_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
++{
++}
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -111,13 +111,30 @@ enum vc4_hdmi_field {
+       HDMI_TX_PHY_CTL_1,
+       HDMI_TX_PHY_CTL_2,
+       HDMI_TX_PHY_CTL_3,
++      HDMI_TX_PHY_CTL_CK,
+       HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
+       HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
+       HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
+       HDMI_TX_PHY_PLL_CFG,
++      HDMI_TX_PHY_PLL_CFG_PDIV,
+       HDMI_TX_PHY_PLL_CTL_0,
+       HDMI_TX_PHY_PLL_CTL_1,
++      HDMI_TX_PHY_PLL_MISC_0,
++      HDMI_TX_PHY_PLL_MISC_1,
++      HDMI_TX_PHY_PLL_MISC_2,
++      HDMI_TX_PHY_PLL_MISC_3,
++      HDMI_TX_PHY_PLL_MISC_4,
++      HDMI_TX_PHY_PLL_MISC_5,
++      HDMI_TX_PHY_PLL_MISC_6,
++      HDMI_TX_PHY_PLL_MISC_7,
++      HDMI_TX_PHY_PLL_MISC_8,
++      HDMI_TX_PHY_PLL_POST_KDIV,
++      HDMI_TX_PHY_PLL_POWERUP_CTL,
++      HDMI_TX_PHY_PLL_REFCLK,
++      HDMI_TX_PHY_PLL_RESET_CTL,
++      HDMI_TX_PHY_PLL_VCOCLK_DIV,
+       HDMI_TX_PHY_POWERDOWN_CTL,
++      HDMI_TX_PHY_POWERUP_CTL,
+       HDMI_TX_PHY_RESET_CTL,
+       HDMI_TX_PHY_TMDS_CLK_WORD_SEL,
+       HDMI_VEC_INTERFACE_CFG,
+@@ -383,6 +400,206 @@ static const struct vc4_hdmi_register __
+       VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
+       VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++      VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++      VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++      VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++      VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++      VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++      VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++      VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++      VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++      VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++      VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++      VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
++};
++
++static const struct vc4_hdmi_register __maybe_unused vc6_hdmi_hdmi0_fields[] = {
++      VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++      VC4_HD_REG(HDMI_MAI_CTL, 0x0010),
++      VC4_HD_REG(HDMI_MAI_THR, 0x0014),
++      VC4_HD_REG(HDMI_MAI_FMT, 0x0018),
++      VC4_HD_REG(HDMI_MAI_DATA, 0x001c),
++      VC4_HD_REG(HDMI_MAI_SMP, 0x0020),
++      VC4_HD_REG(HDMI_VID_CTL, 0x0044),
++      VC4_HD_REG(HDMI_FRAME_COUNT, 0x0060),
++
++      VC4_HDMI_REG(HDMI_FIFO_CTL, 0x07c),
++      VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0c0),
++      VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0c4),
++      VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0cc),
++      VC4_HDMI_REG(HDMI_CRP_CFG, 0x0d0),
++      VC4_HDMI_REG(HDMI_CTS_0, 0x0d4),
++      VC4_HDMI_REG(HDMI_CTS_1, 0x0d8),
++      VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e8),
++      VC4_HDMI_REG(HDMI_HORZA, 0x0ec),
++      VC4_HDMI_REG(HDMI_HORZB, 0x0f0),
++      VC4_HDMI_REG(HDMI_VERTA0, 0x0f4),
++      VC4_HDMI_REG(HDMI_VERTB0, 0x0f8),
++      VC4_HDMI_REG(HDMI_VERTA1, 0x100),
++      VC4_HDMI_REG(HDMI_VERTB1, 0x104),
++      VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x114),
++      VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0a4),
++      VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a8),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x148),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x14c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x150),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x158),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x15c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x160),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x164),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x168),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x16c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x170),
++      VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x18c),
++      VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x194),
++      VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x198),
++      VC4_HDMI_REG(HDMI_HOTPLUG, 0x1c8),
++      VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1e4),
++
++      VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++      VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0f0),
++      VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f4),
++
++      VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++      VC5_PHY_REG(HDMI_TX_PHY_POWERUP_CTL, 0x004),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_CK, 0x014),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_REFCLK, 0x01c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_POST_KDIV, 0x028),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_VCOCLK_DIV, 0x02c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x044),
++      VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x054),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_0, 0x060),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_1, 0x064),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_2, 0x068),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_3, 0x06c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_4, 0x070),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_5, 0x074),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_6, 0x078),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_7, 0x07c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_8, 0x080),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_RESET_CTL, 0x190),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_POWERUP_CTL, 0x194),
++
++      VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++      VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++      VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++      VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++      VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++      VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++      VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++      VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++      VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++      VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++      VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++      VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++      VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++      VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++      VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++      VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
++};
++
++static const struct vc4_hdmi_register __maybe_unused vc6_hdmi_hdmi1_fields[] = {
++      VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++      VC4_HD_REG(HDMI_MAI_CTL, 0x0030),
++      VC4_HD_REG(HDMI_MAI_THR, 0x0034),
++      VC4_HD_REG(HDMI_MAI_FMT, 0x0038),
++      VC4_HD_REG(HDMI_MAI_DATA, 0x003c),
++      VC4_HD_REG(HDMI_MAI_SMP, 0x0040),
++      VC4_HD_REG(HDMI_VID_CTL, 0x0048),
++      VC4_HD_REG(HDMI_FRAME_COUNT, 0x0064),
++
++      VC4_HDMI_REG(HDMI_FIFO_CTL, 0x07c),
++      VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0c0),
++      VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0c4),
++      VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0cc),
++      VC4_HDMI_REG(HDMI_CRP_CFG, 0x0d0),
++      VC4_HDMI_REG(HDMI_CTS_0, 0x0d4),
++      VC4_HDMI_REG(HDMI_CTS_1, 0x0d8),
++      VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e8),
++      VC4_HDMI_REG(HDMI_HORZA, 0x0ec),
++      VC4_HDMI_REG(HDMI_HORZB, 0x0f0),
++      VC4_HDMI_REG(HDMI_VERTA0, 0x0f4),
++      VC4_HDMI_REG(HDMI_VERTB0, 0x0f8),
++      VC4_HDMI_REG(HDMI_VERTA1, 0x100),
++      VC4_HDMI_REG(HDMI_VERTB1, 0x104),
++      VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x114),
++      VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0a4),
++      VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a8),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x148),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x14c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x150),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x158),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x15c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x160),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x164),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x168),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x16c),
++      VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x170),
++      VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x18c),
++      VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x194),
++      VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x198),
++      VC4_HDMI_REG(HDMI_HOTPLUG, 0x1c8),
++      VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1e4),
++
++      VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++      VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0f0),
++      VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f4),
++
++      VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++      VC5_PHY_REG(HDMI_TX_PHY_POWERUP_CTL, 0x004),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++      VC5_PHY_REG(HDMI_TX_PHY_CTL_CK, 0x014),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_REFCLK, 0x01c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_POST_KDIV, 0x028),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_VCOCLK_DIV, 0x02c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x044),
++      VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x054),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_0, 0x060),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_1, 0x064),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_2, 0x068),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_3, 0x06c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_4, 0x070),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_5, 0x074),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_6, 0x078),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_7, 0x07c),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_8, 0x080),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_RESET_CTL, 0x190),
++      VC5_PHY_REG(HDMI_TX_PHY_PLL_POWERUP_CTL, 0x194),
++
++      VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++      VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
+       VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
+       VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
diff --git a/target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch b/target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch
new file mode 100644 (file)
index 0000000..c54ab0d
--- /dev/null
@@ -0,0 +1,117 @@
+From a68e8ffc3314612b0d00d491c8cdd61ae1d9af4e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 25 Apr 2023 10:12:32 +0200
+Subject: [PATCH] drm/vc4: txp: Introduce structure to deal with revision
+ differences
+
+The BCM2712 will have several TXP with small differences. Let's add a
+structure tied to the compatible to deal with those differences.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c |  4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.h        |  6 +++++-
+ drivers/gpu/drm/vc4/vc4_txp.c        | 23 ++++++++++++++++-------
+ 3 files changed, 23 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -51,7 +51,7 @@ struct vc4_mock_desc {
+ static const struct vc4_mock_desc vc4_mock =
+       VC4_MOCK_DESC(
+-              VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
++              VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
+                                  VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+@@ -77,7 +77,7 @@ static const struct vc4_mock_desc vc4_mo
+ static const struct vc4_mock_desc vc5_mock =
+       VC4_MOCK_DESC(
+-              VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
++              VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
+                                  VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -561,7 +561,11 @@ struct vc4_crtc_data {
+       int hvs_output;
+ };
+-extern const struct vc4_crtc_data vc4_txp_crtc_data;
++struct vc4_txp_data {
++      struct vc4_crtc_data    base;
++};
++
++extern const struct vc4_txp_data vc4_txp_data;
+ struct vc4_pv_data {
+       struct vc4_crtc_data    base;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -159,6 +159,7 @@
+ struct vc4_txp {
+       struct vc4_crtc base;
++      const struct vc4_txp_data *data;
+       struct platform_device *pdev;
+@@ -488,17 +489,20 @@ static irqreturn_t vc4_txp_interrupt(int
+       return IRQ_HANDLED;
+ }
+-const struct vc4_crtc_data vc4_txp_crtc_data = {
+-      .name = "txp",
+-      .debugfs_name = "txp_regs",
+-      .hvs_available_channels = BIT(2),
+-      .hvs_output = 2,
++const struct vc4_txp_data vc4_txp_data = {
++      .base = {
++              .name = "txp",
++              .debugfs_name = "txp_regs",
++              .hvs_available_channels = BIT(2),
++              .hvs_output = 2,
++      },
+ };
+ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
+ {
+       struct platform_device *pdev = to_platform_device(dev);
+       struct drm_device *drm = dev_get_drvdata(master);
++      const struct vc4_txp_data *txp_data;
+       struct vc4_encoder *vc4_encoder;
+       struct drm_encoder *encoder;
+       struct vc4_crtc *vc4_crtc;
+@@ -513,6 +517,11 @@ static int vc4_txp_bind(struct device *d
+       if (!txp)
+               return -ENOMEM;
++      txp_data = of_device_get_match_data(dev);
++      if (!txp_data)
++              return -ENODEV;
++
++      txp->data = txp_data;
+       txp->pdev = pdev;
+       txp->regs = vc4_ioremap_regs(pdev, 0);
+       if (IS_ERR(txp->regs))
+@@ -523,7 +532,7 @@ static int vc4_txp_bind(struct device *d
+       vc4_crtc->regset.regs = txp_regs;
+       vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
+-      ret = vc4_crtc_init(drm, pdev, vc4_crtc, &vc4_txp_crtc_data,
++      ret = vc4_crtc_init(drm, pdev, vc4_crtc, &txp_data->base,
+                           &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs, true);
+       if (ret)
+               return ret;
+@@ -584,7 +593,7 @@ static int vc4_txp_remove(struct platfor
+ }
+ static const struct of_device_id vc4_txp_dt_match[] = {
+-      { .compatible = "brcm,bcm2835-txp" },
++      { .compatible = "brcm,bcm2835-txp", .data = &vc4_txp_data },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch b/target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch
new file mode 100644 (file)
index 0000000..9303f86
--- /dev/null
@@ -0,0 +1,66 @@
+From 7d345345b70f00bf4c673a68da7d1cf0faf5cc47 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 25 Apr 2023 10:21:53 +0200
+Subject: [PATCH] drm/vc4: txp: Rename TXP data structure
+
+The TXP data structure has a name too generic for the multiple variants
+we'll have to support. Let's rename it to mention the SoC it applies to.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.h        | 2 +-
+ drivers/gpu/drm/vc4/vc4_txp.c        | 4 ++--
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -51,7 +51,7 @@ struct vc4_mock_desc {
+ static const struct vc4_mock_desc vc4_mock =
+       VC4_MOCK_DESC(
+-              VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
++              VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+                                  VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+@@ -77,7 +77,7 @@ static const struct vc4_mock_desc vc4_mo
+ static const struct vc4_mock_desc vc5_mock =
+       VC4_MOCK_DESC(
+-              VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
++              VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+                                  VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -565,7 +565,7 @@ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
+ };
+-extern const struct vc4_txp_data vc4_txp_data;
++extern const struct vc4_txp_data bcm2835_txp_data;
+ struct vc4_pv_data {
+       struct vc4_crtc_data    base;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -489,7 +489,7 @@ static irqreturn_t vc4_txp_interrupt(int
+       return IRQ_HANDLED;
+ }
+-const struct vc4_txp_data vc4_txp_data = {
++const struct vc4_txp_data bcm2835_txp_data = {
+       .base = {
+               .name = "txp",
+               .debugfs_name = "txp_regs",
+@@ -593,7 +593,7 @@ static int vc4_txp_remove(struct platfor
+ }
+ static const struct of_device_id vc4_txp_dt_match[] = {
+-      { .compatible = "brcm,bcm2835-txp", .data = &vc4_txp_data },
++      { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch b/target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch
new file mode 100644 (file)
index 0000000..cea9062
--- /dev/null
@@ -0,0 +1,56 @@
+From e8dbad6d506b6fac992fdf74a7e3a66a38e554c3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:30:33 +0200
+Subject: [PATCH] drm/vc4: txp: Add byte enable toggle bit
+
+The MOPLET doesn't have the BYTE_ENABLE field to set, but the TXP and
+MOP do, so let's add a boolean to control whether or not we need to set
+it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 6 +++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -563,6 +563,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
++      unsigned int has_byte_enable:1;
+ };
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -291,6 +291,7 @@ static void vc4_txp_connector_atomic_com
+       struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
+                                                                                   conn);
+       struct vc4_txp *txp = connector_to_vc4_txp(conn);
++      const struct vc4_txp_data *txp_data = txp->data;
+       struct drm_gem_dma_object *gem;
+       struct drm_display_mode *mode;
+       struct drm_framebuffer *fb;
+@@ -313,9 +314,11 @@ static void vc4_txp_connector_atomic_com
+               return;
+       ctrl = TXP_GO | TXP_EI |
+-             VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) |
+              VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT);
++      if (txp_data->has_byte_enable)
++              ctrl |= VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE);
++
+       if (fb->format->has_alpha)
+               ctrl |= TXP_ALPHA_ENABLE;
+       else
+@@ -496,6 +499,7 @@ const struct vc4_txp_data bcm2835_txp_da
+               .hvs_available_channels = BIT(2),
+               .hvs_output = 2,
+       },
++      .has_byte_enable = true,
+ };
+ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
diff --git a/target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch b/target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch
new file mode 100644 (file)
index 0000000..290473e
--- /dev/null
@@ -0,0 +1,59 @@
+From a51e4acdab01540e1006e43f38e5befb40002de0 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:47:54 +0200
+Subject: [PATCH] drm/vc4: txp: Add horizontal and vertical size offset toggle
+ bit
+
+The new writeback controllers that can be found on the BCM2712 require
+to have their horizontal and vertical size reduced by one.
+
+Let's tie that behaviour to the compatible so we can support both the
+new and old controllers.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h |  1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 14 ++++++++++++--
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -564,6 +564,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
+       unsigned int has_byte_enable:1;
++      unsigned int size_minus_one:1;
+ };
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -295,6 +295,8 @@ static void vc4_txp_connector_atomic_com
+       struct drm_gem_dma_object *gem;
+       struct drm_display_mode *mode;
+       struct drm_framebuffer *fb;
++      unsigned int hdisplay;
++      unsigned int vdisplay;
+       u32 ctrl;
+       int idx;
+       int i;
+@@ -334,9 +336,17 @@ static void vc4_txp_connector_atomic_com
+       gem = drm_fb_dma_get_gem_obj(fb, 0);
+       TXP_WRITE(TXP_DST_PTR, gem->dma_addr + fb->offsets[0]);
+       TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
++
++      hdisplay = mode->hdisplay ?: 1;
++      vdisplay = mode->vdisplay ?: 1;
++      if (txp_data->size_minus_one) {
++              hdisplay -= 1;
++              vdisplay -= 1;
++      }
++
+       TXP_WRITE(TXP_DIM,
+-                VC4_SET_FIELD(mode->hdisplay, TXP_WIDTH) |
+-                VC4_SET_FIELD(mode->vdisplay, TXP_HEIGHT));
++                VC4_SET_FIELD(hdisplay, TXP_WIDTH) |
++                VC4_SET_FIELD(vdisplay, TXP_HEIGHT));
+       TXP_WRITE(TXP_DST_CTRL, ctrl);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch b/target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch
new file mode 100644 (file)
index 0000000..5d1fbcb
--- /dev/null
@@ -0,0 +1,59 @@
+From ddb9aa80692ed5d35e4ee4688c36789620f78c5c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 17:47:11 +0200
+Subject: [PATCH] drm/vc4: txp: Handle 40-bits DMA Addresses
+
+The BCM2712 MOP and MOPLET can handle addresses larger than 32bits
+through an extra register. We can easily support it and make it
+conditional based on the compatible through a boolean in our variant
+structure.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h |  1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 10 +++++++++-
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -565,6 +565,7 @@ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
+       unsigned int has_byte_enable:1;
+       unsigned int size_minus_one:1;
++      unsigned int supports_40bit_addresses:1;
+ };
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -145,6 +145,8 @@
+ /* Number of lines received and committed to memory. */
+ #define TXP_PROGRESS          0x10
++#define TXP_DST_PTR_HIGH      0x1c
++
+ #define TXP_READ(offset)                                                              \
+       ({                                                                              \
+               kunit_fail_current_test("Accessing a register in a unit test!\n");      \
+@@ -297,6 +299,7 @@ static void vc4_txp_connector_atomic_com
+       struct drm_framebuffer *fb;
+       unsigned int hdisplay;
+       unsigned int vdisplay;
++      dma_addr_t addr;
+       u32 ctrl;
+       int idx;
+       int i;
+@@ -334,7 +337,12 @@ static void vc4_txp_connector_atomic_com
+               return;
+       gem = drm_fb_dma_get_gem_obj(fb, 0);
+-      TXP_WRITE(TXP_DST_PTR, gem->dma_addr + fb->offsets[0]);
++      addr = gem->dma_addr + fb->offsets[0];
++      TXP_WRITE(TXP_DST_PTR, lower_32_bits(addr));
++
++      if (txp_data->supports_40bit_addresses)
++              TXP_WRITE(TXP_DST_PTR_HIGH, upper_32_bits(addr) & 0xff);
++
+       TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
+       hdisplay = mode->hdisplay ?: 1;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch b/target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch
new file mode 100644 (file)
index 0000000..0445c4f
--- /dev/null
@@ -0,0 +1,44 @@
+From 2e8f4fa23af4bb794e9b2284a53aa40bbfdd3cbb Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 11:26:10 +0200
+Subject: [PATCH] drm/vc4: txp: Move the encoder type in the variant structure
+
+We'll have multiple TXP instances in the BCM2712, so we can't use a
+single encoder type anymore. Let's tie the encoder type to the
+compatible.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 3 ++-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -563,6 +563,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
++      enum vc4_encoder_type encoder_type;
+       unsigned int has_byte_enable:1;
+       unsigned int size_minus_one:1;
+       unsigned int supports_40bit_addresses:1;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -517,6 +517,7 @@ const struct vc4_txp_data bcm2835_txp_da
+               .hvs_available_channels = BIT(2),
+               .hvs_output = 2,
+       },
++      .encoder_type = VC4_ENCODER_TYPE_TXP,
+       .has_byte_enable = true,
+ };
+@@ -560,7 +561,7 @@ static int vc4_txp_bind(struct device *d
+               return ret;
+       vc4_encoder = &txp->encoder;
+-      txp->encoder.type = VC4_ENCODER_TYPE_TXP;
++      txp->encoder.type = txp_data->encoder_type;
+       encoder = &vc4_encoder->base;
+       encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch b/target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch
new file mode 100644 (file)
index 0000000..f832fc1
--- /dev/null
@@ -0,0 +1,475 @@
+From 68a00ca7b1d7809ac7be736c02238c142e629127 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 11:49:28 +0200
+Subject: [PATCH] drm/vc4: txp: Add a new TXP encoder type
+
+Starting with BCM2712, we'll have a two TXP. Let's follow the HDMI
+example and add two encoder types for TXP: TXP0 and TXP1.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c          |   4 +-
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c    | 106 +++++++++---------
+ drivers/gpu/drm/vc4/vc4_drv.h                 |   3 +-
+ drivers/gpu/drm/vc4/vc4_kms.c                 |   2 +-
+ drivers/gpu/drm/vc4/vc4_txp.c                 |   2 +-
+ 5 files changed, 59 insertions(+), 58 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -52,7 +52,7 @@ struct vc4_mock_desc {
+ static const struct vc4_mock_desc vc4_mock =
+       VC4_MOCK_DESC(
+               VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+-                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
++                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+               VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv0_data,
+@@ -78,7 +78,7 @@ static const struct vc4_mock_desc vc4_mo
+ static const struct vc4_mock_desc vc5_mock =
+       VC4_MOCK_DESC(
+               VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+-                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
++                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
+                                                       DRM_MODE_ENCODER_VIRTUAL,
+                                                       DRM_MODE_CONNECTOR_WRITEBACK)),
+               VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv0_data,
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -91,7 +91,7 @@ static const struct encoder_constraint v
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 1),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+-      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 2),
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 2),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 2),
+ };
+@@ -99,7 +99,7 @@ static const struct encoder_constraint v
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+-      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 0, 2),
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 0, 2),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 0, 1, 2),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0, 1, 2),
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+@@ -208,7 +208,7 @@ static const struct pv_muxing_param vc4_
+       VC4_PV_MUXING_TEST("1 output: DSI1",
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("1 output: TXP",
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_HDMI0),
+@@ -220,7 +220,7 @@ static const struct pv_muxing_param vc4_
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_HDMI0),
+@@ -232,19 +232,19 @@ static const struct pv_muxing_param vc4_
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("2 outputs: DPI, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("2 outputs: HDMI0, DSI1",
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+                          VC4_ENCODER_TYPE_HDMI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("2 outputs: VEC, DSI1",
+                          VC4_ENCODER_TYPE_VEC,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("2 outputs: VEC, TXP",
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, DSI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_HDMI0,
+@@ -252,7 +252,7 @@ static const struct pv_muxing_param vc4_
+       VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_HDMI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+@@ -260,7 +260,7 @@ static const struct pv_muxing_param vc4_
+       VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, DSI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_HDMI0,
+@@ -268,7 +268,7 @@ static const struct pv_muxing_param vc4_
+       VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_HDMI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+@@ -276,7 +276,7 @@ static const struct pv_muxing_param vc4_
+       VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+ };
+ KUNIT_ARRAY_PARAM(vc4_test_pv_muxing,
+@@ -288,7 +288,7 @@ static const struct pv_muxing_param vc4_
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_DSI0),
+       VC4_PV_MUXING_TEST("TXP/DSI1 Conflict",
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC4_PV_MUXING_TEST("HDMI0/VEC Conflict",
+                          VC4_ENCODER_TYPE_HDMI0,
+@@ -297,22 +297,22 @@ static const struct pv_muxing_param vc4_
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_DSI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+                          VC4_ENCODER_TYPE_DSI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("More than 3 outputs: DPI, HDMI0, DSI1, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_DSI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC4_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+                          VC4_ENCODER_TYPE_DSI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+ };
+ KUNIT_ARRAY_PARAM(vc4_test_pv_muxing_invalid,
+@@ -343,7 +343,7 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("2 outputs: DPI, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("2 outputs: DPI, VEC",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC),
+@@ -361,7 +361,7 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("2 outputs: DSI0, VEC",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC),
+@@ -373,7 +373,7 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_VEC),
+       VC5_PV_MUXING_TEST("2 outputs: DSI1, TXP",
+                          VC4_ENCODER_TYPE_DSI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0),
+@@ -385,7 +385,7 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_VEC),
+       VC5_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+                          VC4_ENCODER_TYPE_HDMI0,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+@@ -394,14 +394,14 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_VEC),
+       VC5_PV_MUXING_TEST("2 outputs: HDMI1, TXP",
+                          VC4_ENCODER_TYPE_HDMI1,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("2 outputs: TXP, VEC",
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_VEC),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+@@ -416,15 +416,15 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, DSI1",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+@@ -441,7 +441,7 @@ static const struct pv_muxing_param vc5_
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP),
++                         VC4_ENCODER_TYPE_TXP0),
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+@@ -456,15 +456,15 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, DSI1",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+@@ -491,17 +491,17 @@ static const struct pv_muxing_param vc5_
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+@@ -520,17 +520,17 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, DSI1, HDMI0, HDMI1",
+@@ -541,19 +541,19 @@ static const struct pv_muxing_param vc5_
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0, HDMI1",
+@@ -564,24 +564,24 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+@@ -600,17 +600,17 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, DSI1, HDMI0, HDMI1",
+@@ -621,19 +621,19 @@ static const struct pv_muxing_param vc5_
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0, HDMI1",
+@@ -644,27 +644,27 @@ static const struct pv_muxing_param vc5_
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: VEC, TXP, DSI1, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DPI,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+       VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0, HDMI1",
+                          VC4_ENCODER_TYPE_DSI0,
+                          VC4_ENCODER_TYPE_VEC,
+-                         VC4_ENCODER_TYPE_TXP,
++                         VC4_ENCODER_TYPE_TXP0,
+                          VC4_ENCODER_TYPE_DSI1,
+                          VC4_ENCODER_TYPE_HDMI0,
+                          VC4_ENCODER_TYPE_HDMI1),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -511,7 +511,8 @@ enum vc4_encoder_type {
+       VC4_ENCODER_TYPE_DSI1,
+       VC4_ENCODER_TYPE_SMI,
+       VC4_ENCODER_TYPE_DPI,
+-      VC4_ENCODER_TYPE_TXP,
++      VC4_ENCODER_TYPE_TXP0,
++      VC4_ENCODER_TYPE_TXP1,
+ };
+ struct vc4_encoder {
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -359,7 +359,7 @@ static void vc6_hvs_pv_muxing_commit(str
+                       mux = 0;
+                       break;
+-              case VC4_ENCODER_TYPE_TXP:
++              case VC4_ENCODER_TYPE_TXP0:
+                       mux = 2;
+                       break;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -517,7 +517,7 @@ const struct vc4_txp_data bcm2835_txp_da
+               .hvs_available_channels = BIT(2),
+               .hvs_output = 2,
+       },
+-      .encoder_type = VC4_ENCODER_TYPE_TXP,
++      .encoder_type = VC4_ENCODER_TYPE_TXP0,
+       .has_byte_enable = true,
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch b/target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch
new file mode 100644 (file)
index 0000000..aec0753
--- /dev/null
@@ -0,0 +1,64 @@
+From 831c6e8c68a66678e8329a382823dc83c483dcc8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:30:49 +0200
+Subject: [PATCH] drm/vc4: txp: Add support for BCM2712 MOP
+
+The BCM2712 has an evolution of what used to be called TXP in the
+earlier SoCs, but is now called MOP.
+
+There's a few differences still, so we can add a new compatible to deal
+with them easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -388,6 +388,7 @@ static const struct drm_connector_funcs
+ static void vc4_txp_encoder_disable(struct drm_encoder *encoder)
+ {
+       struct drm_device *drm = encoder->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct vc4_txp *txp = encoder_to_vc4_txp(encoder);
+       int idx;
+@@ -406,7 +407,8 @@ static void vc4_txp_encoder_disable(stru
+               WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY);
+       }
+-      TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
++      if (vc4->gen < VC4_GEN_6)
++              TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
+       drm_dev_exit(idx);
+ }
+@@ -510,6 +512,19 @@ static irqreturn_t vc4_txp_interrupt(int
+       return IRQ_HANDLED;
+ }
++const struct vc4_txp_data bcm2712_mop_data = {
++      .base = {
++              .name = "mop",
++              .debugfs_name = "mop_regs",
++              .hvs_available_channels = BIT(2),
++              .hvs_output = 2,
++      },
++      .encoder_type = VC4_ENCODER_TYPE_TXP0,
++      .has_byte_enable = true,
++      .size_minus_one = true,
++      .supports_40bit_addresses = true,
++};
++
+ const struct vc4_txp_data bcm2835_txp_data = {
+       .base = {
+               .name = "txp",
+@@ -616,6 +631,7 @@ static int vc4_txp_remove(struct platfor
+ }
+ static const struct of_device_id vc4_txp_dt_match[] = {
++      { .compatible = "brcm,bcm2712-mop", .data = &bcm2712_mop_data },
+       { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch b/target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch
new file mode 100644 (file)
index 0000000..b2a28bd
--- /dev/null
@@ -0,0 +1,42 @@
+From b239fc6a68fea2b073c4cb48884fbb697014ac2b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 20 Feb 2023 17:16:01 +0100
+Subject: [PATCH] drm/vc4: txp: Add BCM2712 MOPLET support
+
+The BCM2712 features a simpler TXP called MOPLET. Let's add support for
+it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -525,6 +525,18 @@ const struct vc4_txp_data bcm2712_mop_da
+       .supports_40bit_addresses = true,
+ };
++const struct vc4_txp_data bcm2712_moplet_data = {
++      .base = {
++              .name = "moplet",
++              .debugfs_name = "moplet_regs",
++              .hvs_available_channels = BIT(1),
++              .hvs_output = 4,
++      },
++      .encoder_type = VC4_ENCODER_TYPE_TXP1,
++      .size_minus_one = true,
++      .supports_40bit_addresses = true,
++};
++
+ const struct vc4_txp_data bcm2835_txp_data = {
+       .base = {
+               .name = "txp",
+@@ -632,6 +644,7 @@ static int vc4_txp_remove(struct platfor
+ static const struct of_device_id vc4_txp_dt_match[] = {
+       { .compatible = "brcm,bcm2712-mop", .data = &bcm2712_mop_data },
++      { .compatible = "brcm,bcm2712-moplet", .data = &bcm2712_moplet_data },
+       { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch b/target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch
new file mode 100644 (file)
index 0000000..0fc95e5
--- /dev/null
@@ -0,0 +1,240 @@
+From bb05ccd66342643b1cd9a0a48cec3ebdc3eed511 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 21 Feb 2023 14:38:32 +0100
+Subject: [PATCH] drm/vc4: Add additional warn_on
+
+Some code path in vc4 are conditional to a generation and cannot be
+executed on others. Let's put a WARN_ON if that ever happens.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c   | 32 ++++++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_kms.c   |  6 ++++++
+ drivers/gpu/drm/vc4/vc4_plane.c | 19 +++++++++++++++++++
+ 3 files changed, 55 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -417,12 +417,15 @@ static int vc4_hvs_upload_linear_kernel(
+ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
+                            struct vc4_crtc *vc4_crtc)
+ {
+-      struct drm_device *drm = &hvs->vc4->base;
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
+       struct drm_crtc *crtc = &vc4_crtc->base;
+       struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+       int idx;
+       u32 i;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+       if (!drm_dev_enter(drm, &idx))
+               return;
+@@ -758,6 +761,8 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+       u8 field = 0;
+       int idx;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       if (!drm_dev_enter(drm, &idx))
+               return 0;
+@@ -791,6 +796,8 @@ int vc4_hvs_get_fifo_from_output(struct
+       u32 reg;
+       int ret;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       switch (vc4->gen) {
+       case VC4_GEN_4:
+               return output;
+@@ -880,6 +887,8 @@ static int vc4_hvs_init_channel(struct v
+       u32 dispctrl;
+       int idx;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+       if (!drm_dev_enter(drm, &idx))
+               return -ENODEV;
+@@ -947,6 +956,8 @@ static int vc6_hvs_init_channel(struct v
+       u32 disp_ctrl1;
+       int idx;
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
+       if (!drm_dev_enter(drm, &idx))
+               return -ENODEV;
+@@ -972,9 +983,12 @@ static int vc6_hvs_init_channel(struct v
+ static void __vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
+ {
+-      struct drm_device *drm = &hvs->vc4->base;
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
+       int idx;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+       if (!drm_dev_enter(drm, &idx))
+               return;
+@@ -1007,6 +1021,8 @@ static void __vc6_hvs_stop_channel(struc
+       struct drm_device *drm = &vc4->base;
+       int idx;
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
+       if (!drm_dev_enter(drm, &idx))
+               return;
+@@ -1234,6 +1250,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+       bool found = false;
+       int idx;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       if (!drm_dev_enter(dev, &idx)) {
+               vc4_crtc_send_vblank(crtc);
+               return;
+@@ -1324,6 +1342,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+       if (crtc->state->color_mgmt_changed) {
+               u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
++              WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+               if (crtc->state->gamma_lut) {
+                       if (vc4->gen == VC4_GEN_4) {
+                               vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
+@@ -1363,6 +1383,8 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+       u32 dispctrl;
+       int idx;
++      WARN_ON(vc4->gen > VC4_GEN_5);
++
+       if (!drm_dev_enter(drm, &idx))
+               return;
+@@ -1383,6 +1405,8 @@ void vc4_hvs_unmask_underrun(struct vc4_
+       u32 dispctrl;
+       int idx;
++      WARN_ON(vc4->gen > VC4_GEN_5);
++
+       if (!drm_dev_enter(drm, &idx))
+               return;
+@@ -1417,6 +1441,8 @@ static irqreturn_t vc4_hvs_irq_handler(i
+       u32 status;
+       u32 dspeislur;
++      WARN_ON(vc4->gen > VC4_GEN_5);
++
+       /*
+        * NOTE: We don't need to protect the register access using
+        * drm_dev_enter() there because the interrupt handler lifetime
+@@ -1466,6 +1492,8 @@ static irqreturn_t vc6_hvs_eof_irq_handl
+       struct vc4_hvs *hvs = vc4->hvs;
+       unsigned int i;
++      WARN_ON(vc4->gen < VC4_GEN_6);
++
+       for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+               if (!hvs->eof_irq[i].enabled)
+                       continue;
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -147,6 +147,8 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru
+       if (vc4->firmware_kms)
+               return;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+       if (ctm_state->fifo) {
+               HVS_WRITE(SCALER_OLEDCOEF2,
+                         VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]),
+@@ -222,6 +224,8 @@ static void vc4_hvs_pv_muxing_commit(str
+       struct drm_crtc *crtc;
+       unsigned int i;
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_4);
++
+       for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+               struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+               struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+@@ -265,6 +269,8 @@ static void vc5_hvs_pv_muxing_commit(str
+       unsigned int i;
+       u32 reg;
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_5);
++
+       for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+               struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+               struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -555,8 +555,11 @@ static int vc4_plane_setup_clipping_and_
+ static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
+ {
++      struct vc4_dev *vc4 = to_vc4_dev(vc4_state->base.plane->dev);
+       u32 scale, recip;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       scale = src / dst;
+       /* The specs note that while the reciprocal would be defined
+@@ -581,10 +584,13 @@ static void vc4_write_tpz(struct vc4_pla
+ static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel, int chroma_offset)
+ {
++      struct vc4_dev *vc4 = to_vc4_dev(vc4_state->base.plane->dev);
+       u32 scale = src / dst;
+       s32 offset, offset2;
+       s32 phase;
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       /* Start the phase at 1/2 pixel from the 1st pixel at src_x.
+          1/4 pixel for YUV, plus the offset for chroma siting */
+       if (channel) {
+@@ -801,8 +807,11 @@ static size_t vc6_upm_size(const struct
+ static void vc4_write_scaling_parameters(struct drm_plane_state *state,
+                                        int channel)
+ {
++      struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+       /* Ch0 H-PPF Word 0: Scaling Parameters */
+       if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
+               vc4_write_ppf(vc4_state,
+@@ -1040,6 +1049,11 @@ static const u32 colorspace_coeffs[2][DR
+ static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state)
+ {
++      struct drm_device *dev = state->state->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_4);
++
+       if (!state->fb->format->has_alpha)
+               return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
+                                    SCALER_POS2_ALPHA_MODE);
+@@ -1061,6 +1075,11 @@ static u32 vc4_hvs4_get_alpha_blend_mode
+ static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state)
+ {
++      struct drm_device *dev = state->state->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++
++      WARN_ON_ONCE(vc4->gen != VC4_GEN_5 && vc4->gen != VC4_GEN_6);
++
+       if (!state->fb->format->has_alpha)
+               return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
+                                    SCALER5_CTL2_ALPHA_MODE);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch b/target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch
new file mode 100644 (file)
index 0000000..0751d46
--- /dev/null
@@ -0,0 +1,47 @@
+From 336917ca87807b8a4bb08855b4dcb0477289c765 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:35:16 +0100
+Subject: [PATCH] drm/vc4: tests: Switch generation mockup to a switch
+
+Testing whether the VideoCore generation we want to mock is vc5 or vc4
+worked so far, but will be difficult to extend to support BCM2712 (VC6).
+
+Convert to a switch.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -155,13 +155,27 @@ static int __build_mock(struct kunit *te
+ static struct vc4_dev *__mock_device(struct kunit *test, enum vc4_gen gen)
+ {
++      const struct vc4_mock_desc *desc;
++      const struct drm_driver *drv;
+       struct drm_device *drm;
+-      const struct drm_driver *drv = (gen == VC4_GEN_5) ? &vc5_drm_driver : &vc4_drm_driver;
+-      const struct vc4_mock_desc *desc = (gen == VC4_GEN_5) ? &vc5_mock : &vc4_mock;
+       struct vc4_dev *vc4;
+       struct device *dev;
+       int ret;
++      switch (gen) {
++      case VC4_GEN_4:
++              drv = &vc4_drm_driver;
++              desc = &vc4_mock;
++              break;
++      case VC4_GEN_5:
++              drv = &vc5_drm_driver;
++              desc = &vc5_mock;
++              break;
++
++      default:
++              return NULL;
++      }
++
+       dev = drm_kunit_helper_alloc_device(test);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch b/target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch
new file mode 100644 (file)
index 0000000..42c4be9
--- /dev/null
@@ -0,0 +1,63 @@
+From 70e906d3c688491e181446afa27bea32ce241d6a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 09:58:15 +0100
+Subject: [PATCH] drm/vc4: tests: Drop drm parameter for
+ vc4_find_crtc_for_encoder
+
+The DRM device pointer and the DRM encoder pointer are redundant, since
+the latter is attached to the former and we can just follow the
+drm_encoder->dev pointer.
+
+Let's remove the drm_device pointer argument.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h           | 2 +-
+ drivers/gpu/drm/vc4/tests/vc4_mock_output.c    | 4 ++--
+ drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 2 +-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -7,9 +7,9 @@
+ static inline
+ struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
+-                                         struct drm_device *drm,
+                                          struct drm_encoder *encoder)
+ {
++      struct drm_device *drm = encoder->dev;
+       struct drm_crtc *crtc;
+       KUNIT_ASSERT_EQ(test, hweight32(encoder->possible_crtcs), 1);
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
+@@ -77,7 +77,7 @@ int vc4_mock_atomic_add_output(struct ku
+       encoder = vc4_find_encoder_by_type(drm, type);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+-      crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++      crtc = vc4_find_crtc_for_encoder(test, encoder);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+       output = container_of(encoder, struct vc4_dummy_output, encoder.base);
+@@ -115,7 +115,7 @@ int vc4_mock_atomic_del_output(struct ku
+       encoder = vc4_find_encoder_by_type(drm, type);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+-      crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++      crtc = vc4_find_crtc_for_encoder(test, encoder);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+       crtc_state = drm_atomic_get_crtc_state(state, crtc);
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -132,7 +132,7 @@ get_vc4_crtc_state_for_encoder(struct ku
+       encoder = vc4_find_encoder_by_type(drm, type);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+-      crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++      crtc = vc4_find_crtc_for_encoder(test, encoder);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+       new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch b/target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch
new file mode 100644 (file)
index 0000000..6f49d52
--- /dev/null
@@ -0,0 +1,174 @@
+From 14e97c5765579eaab3c8372701750ffa30e4c7da Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 10:02:59 +0100
+Subject: [PATCH] drm/vc4: tests: Return the allocated output
+
+Some tests will need to retrieve the output that was just allocated by
+vc4_mock_atomic_add_output().
+
+Instead of making them look them up in the DRM device, we can simply
+make vc4_mock_atomic_add_output() return an error pointer that holds the
+allocated output instead of the error code.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h          |  7 ++--
+ drivers/gpu/drm/vc4/tests/vc4_mock_output.c   |  9 +++--
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c    | 37 +++++++++++--------
+ 3 files changed, 30 insertions(+), 23 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -53,9 +53,10 @@ struct vc4_dummy_output *vc4_dummy_outpu
+ struct vc4_dev *vc4_mock_device(struct kunit *test);
+ struct vc4_dev *vc5_mock_device(struct kunit *test);
+-int vc4_mock_atomic_add_output(struct kunit *test,
+-                             struct drm_atomic_state *state,
+-                             enum vc4_encoder_type type);
++struct vc4_dummy_output *
++vc4_mock_atomic_add_output(struct kunit *test,
++                         struct drm_atomic_state *state,
++                         enum vc4_encoder_type type);
+ int vc4_mock_atomic_del_output(struct kunit *test,
+                              struct drm_atomic_state *state,
+                              enum vc4_encoder_type type);
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
+@@ -61,9 +61,10 @@ static const struct drm_display_mode def
+       DRM_SIMPLE_MODE(640, 480, 64, 48)
+ };
+-int vc4_mock_atomic_add_output(struct kunit *test,
+-                             struct drm_atomic_state *state,
+-                             enum vc4_encoder_type type)
++struct vc4_dummy_output *
++vc4_mock_atomic_add_output(struct kunit *test,
++                         struct drm_atomic_state *state,
++                         enum vc4_encoder_type type)
+ {
+       struct drm_device *drm = state->dev;
+       struct drm_connector_state *conn_state;
+@@ -96,7 +97,7 @@ int vc4_mock_atomic_add_output(struct ku
+       crtc_state->active = true;
+-      return 0;
++      return output;
+ }
+ int vc4_mock_atomic_del_output(struct kunit *test,
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -683,10 +683,11 @@ static void drm_vc4_test_pv_muxing(struc
+       int ret;
+       for (i = 0; i < params->nencoders; i++) {
++              struct vc4_dummy_output *output;
+               enum vc4_encoder_type enc_type = params->encoders[i];
+-              ret = vc4_mock_atomic_add_output(test, state, enc_type);
+-              KUNIT_ASSERT_EQ(test, ret, 0);
++              output = vc4_mock_atomic_add_output(test, state, enc_type);
++              KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       }
+       ret = drm_atomic_check_only(state);
+@@ -712,10 +713,11 @@ static void drm_vc4_test_pv_muxing_inval
+       int ret;
+       for (i = 0; i < params->nencoders; i++) {
++              struct vc4_dummy_output *output;
+               enum vc4_encoder_type enc_type = params->encoders[i];
+-              ret = vc4_mock_atomic_add_output(test, state, enc_type);
+-              KUNIT_ASSERT_EQ(test, ret, 0);
++              output = vc4_mock_atomic_add_output(test, state, enc_type);
++              KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       }
+       ret = drm_atomic_check_only(state);
+@@ -804,6 +806,7 @@ static void drm_test_vc5_pv_muxing_bugs_
+ {
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_atomic_state *state;
++      struct vc4_dummy_output *output;
+       struct vc4_crtc_state *new_vc4_crtc_state;
+       struct vc4_hvs_state *new_hvs_state;
+       unsigned int hdmi0_channel;
+@@ -823,8 +826,8 @@ static void drm_test_vc5_pv_muxing_bugs_
+       state->acquire_ctx = &ctx;
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -850,8 +853,8 @@ static void drm_test_vc5_pv_muxing_bugs_
+       state->acquire_ctx = &ctx;
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -880,6 +883,7 @@ static void drm_test_vc5_pv_muxing_bugs_
+ {
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_atomic_state *state;
++      struct vc4_dummy_output *output;
+       struct vc4_crtc_state *new_vc4_crtc_state;
+       struct vc4_hvs_state *new_hvs_state;
+       unsigned int old_hdmi0_channel;
+@@ -899,11 +903,11 @@ static void drm_test_vc5_pv_muxing_bugs_
+       state->acquire_ctx = &ctx;
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -971,6 +975,7 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+ {
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_atomic_state *state;
++      struct vc4_dummy_output *output;
+       struct vc4_crtc_state *new_vc4_crtc_state;
+       struct drm_device *drm;
+       struct vc4_dev *vc4;
+@@ -987,8 +992,8 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+       state->acquire_ctx = &ctx;
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -1003,8 +1008,8 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+       state->acquire_ctx = &ctx;
+-      ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+-      KUNIT_ASSERT_EQ(test, ret, 0);
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch
new file mode 100644 (file)
index 0000000..8c12375
--- /dev/null
@@ -0,0 +1,87 @@
+From 04bec005b049604f862765b35ebd71c2a69b9e7c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:38:10 +0100
+Subject: [PATCH] drm/vc4: tests: Add BCM2712 mock driver
+
+The BCM2712 has a simpler pipeline that can only output to a writeback
+connector and two HDMI controllers.
+
+Let's allow our kunit tests to create a mock of that pipeline.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 29 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/vc4/tests/vc4_mock.h |  1 +
+ drivers/gpu/drm/vc4/vc4_drv.h        |  2 ++
+ 3 files changed, 32 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -106,6 +106,26 @@ static const struct vc4_mock_desc vc5_mo
+                                                             DRM_MODE_CONNECTOR_HDMIA)),
+ );
++static const struct vc4_mock_desc vc6_mock =
++      VC4_MOCK_DESC(
++              VC4_MOCK_CRTC_DESC(&bcm2712_mop_data.base,
++                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
++                                                      DRM_MODE_ENCODER_VIRTUAL,
++                                                      DRM_MODE_CONNECTOR_WRITEBACK)),
++              VC4_MOCK_CRTC_DESC(&bcm2712_moplet_data.base,
++                                 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP1,
++                                                      DRM_MODE_ENCODER_VIRTUAL,
++                                                      DRM_MODE_CONNECTOR_WRITEBACK)),
++              VC4_MOCK_PIXELVALVE_DESC(&bcm2712_pv0_data,
++                                       VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
++                                                            DRM_MODE_ENCODER_TMDS,
++                                                            DRM_MODE_CONNECTOR_HDMIA)),
++              VC4_MOCK_PIXELVALVE_DESC(&bcm2712_pv1_data,
++                                       VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI1,
++                                                            DRM_MODE_ENCODER_TMDS,
++                                                            DRM_MODE_CONNECTOR_HDMIA)),
++);
++
+ static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
+                           const struct vc4_mock_pipe_desc *pipe)
+ {
+@@ -171,6 +191,10 @@ static struct vc4_dev *__mock_device(str
+               drv = &vc5_drm_driver;
+               desc = &vc5_mock;
+               break;
++      case VC4_GEN_6:
++              drv = &vc5_drm_driver;
++              desc = &vc6_mock;
++              break;
+       default:
+               return NULL;
+@@ -212,3 +236,8 @@ struct vc4_dev *vc5_mock_device(struct k
+ {
+       return __mock_device(test, VC4_GEN_5);
+ }
++
++struct vc4_dev *vc6_mock_device(struct kunit *test)
++{
++      return __mock_device(test, VC4_GEN_6);
++}
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -52,6 +52,7 @@ struct vc4_dummy_output *vc4_dummy_outpu
+ struct vc4_dev *vc4_mock_device(struct kunit *test);
+ struct vc4_dev *vc5_mock_device(struct kunit *test);
++struct vc4_dev *vc6_mock_device(struct kunit *test);
+ struct vc4_dummy_output *
+ vc4_mock_atomic_add_output(struct kunit *test,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -570,6 +570,8 @@ struct vc4_txp_data {
+       unsigned int supports_40bit_addresses:1;
+ };
++extern const struct vc4_txp_data bcm2712_mop_data;
++extern const struct vc4_txp_data bcm2712_moplet_data;
+ extern const struct vc4_txp_data bcm2835_txp_data;
+ struct vc4_pv_data {
diff --git a/target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch b/target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch
new file mode 100644 (file)
index 0000000..6c0db59
--- /dev/null
@@ -0,0 +1,138 @@
+From 77c14764ae164b7969e65437a87aca25b30d8b80 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:14:22 +0200
+Subject: [PATCH] drm/vc4: tests: Add tests for BCM2712 PixelValve Muxing
+
+The BCM2712 has a simpler pipeline than the BCM2711, and thus the muxing
+requirements are different. Create some tests to make sure we get proper
+muxing decisions.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c    | 81 +++++++++++++++++++
+ 1 file changed, 81 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -105,6 +105,13 @@ static const struct encoder_constraint v
+       ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+ };
++static const struct encoder_constraint vc6_encoder_constraints[] = {
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0),
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 1),
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP1, 1),
++      ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 2),
++};
++
+ static bool check_vc4_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
+ {
+       return __check_encoder_constraints(vc4_encoder_constraints,
+@@ -119,6 +126,13 @@ static bool check_vc5_encoder_constraint
+                                          type, channel);
+ }
++static bool check_vc6_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
++{
++      return __check_encoder_constraints(vc6_encoder_constraints,
++                                         ARRAY_SIZE(vc6_encoder_constraints),
++                                         type, channel);
++}
++
+ static struct vc4_crtc_state *
+ get_vc4_crtc_state_for_encoder(struct kunit *test,
+                              const struct drm_atomic_state *state,
+@@ -196,6 +210,9 @@ static void vc4_test_pv_muxing_desc(cons
+ #define VC5_PV_MUXING_TEST(_name, ...)                \
+       PV_MUXING_TEST(_name, vc5_mock_device, check_vc5_encoder_constraints, __VA_ARGS__)
++#define VC6_PV_MUXING_TEST(_name, ...)                \
++      PV_MUXING_TEST(_name, vc6_mock_device, check_vc6_encoder_constraints, __VA_ARGS__)
++
+ static const struct pv_muxing_param vc4_test_pv_muxing_params[] = {
+       VC4_PV_MUXING_TEST("1 output: DSI0",
+                          VC4_ENCODER_TYPE_DSI0),
+@@ -674,6 +691,54 @@ KUNIT_ARRAY_PARAM(vc5_test_pv_muxing_inv
+                 vc5_test_pv_muxing_invalid_params,
+                 vc4_test_pv_muxing_desc);
++static const struct pv_muxing_param vc6_test_pv_muxing_params[] = {
++      VC6_PV_MUXING_TEST("1 output: HDMI0",
++                         VC4_ENCODER_TYPE_HDMI0),
++      VC6_PV_MUXING_TEST("1 output: HDMI1",
++                         VC4_ENCODER_TYPE_HDMI1),
++      VC6_PV_MUXING_TEST("1 output: MOPLET",
++                         VC4_ENCODER_TYPE_TXP1),
++      VC6_PV_MUXING_TEST("1 output: MOP",
++                         VC4_ENCODER_TYPE_TXP0),
++      VC6_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
++                         VC4_ENCODER_TYPE_HDMI0,
++                         VC4_ENCODER_TYPE_HDMI1),
++      VC6_PV_MUXING_TEST("2 outputs: HDMI0, MOPLET",
++                         VC4_ENCODER_TYPE_HDMI0,
++                         VC4_ENCODER_TYPE_TXP1),
++      VC6_PV_MUXING_TEST("2 outputs: HDMI0, MOP",
++                         VC4_ENCODER_TYPE_HDMI0,
++                         VC4_ENCODER_TYPE_TXP0),
++      VC6_PV_MUXING_TEST("2 outputs: HDMI1, MOP",
++                         VC4_ENCODER_TYPE_HDMI1,
++                         VC4_ENCODER_TYPE_TXP0),
++      VC6_PV_MUXING_TEST("2 outputs: MOPLET, MOP",
++                         VC4_ENCODER_TYPE_TXP1,
++                         VC4_ENCODER_TYPE_TXP0),
++      VC6_PV_MUXING_TEST("3 outputs: HDMI0, HDMI1, MOP",
++                         VC4_ENCODER_TYPE_HDMI0,
++                         VC4_ENCODER_TYPE_HDMI1,
++                         VC4_ENCODER_TYPE_TXP0),
++      VC6_PV_MUXING_TEST("3 outputs: HDMI0, MOPLET, MOP",
++                         VC4_ENCODER_TYPE_HDMI0,
++                         VC4_ENCODER_TYPE_TXP1,
++                         VC4_ENCODER_TYPE_TXP0),
++};
++
++KUNIT_ARRAY_PARAM(vc6_test_pv_muxing,
++                vc6_test_pv_muxing_params,
++                vc4_test_pv_muxing_desc);
++
++static const struct pv_muxing_param vc6_test_pv_muxing_invalid_params[] = {
++      VC6_PV_MUXING_TEST("HDMI1/MOPLET Conflict",
++                         VC4_ENCODER_TYPE_HDMI1,
++                         VC4_ENCODER_TYPE_TXP1),
++};
++
++KUNIT_ARRAY_PARAM(vc6_test_pv_muxing_invalid,
++                vc6_test_pv_muxing_invalid_params,
++                vc4_test_pv_muxing_desc);
++
+ static void drm_vc4_test_pv_muxing(struct kunit *test)
+ {
+       const struct pv_muxing_param *params = test->param_value;
+@@ -797,6 +862,21 @@ static struct kunit_suite vc5_pv_muxing_
+       .test_cases = vc5_pv_muxing_tests,
+ };
++static struct kunit_case vc6_pv_muxing_tests[] = {
++      KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
++                       vc6_test_pv_muxing_gen_params),
++      KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
++                       vc6_test_pv_muxing_invalid_gen_params),
++      {}
++};
++
++static struct kunit_suite vc6_pv_muxing_test_suite = {
++      .name = "vc6-pv-muxing-combinations",
++      .init = vc4_pv_muxing_test_init,
++      .exit = vc4_pv_muxing_test_exit,
++      .test_cases = vc6_pv_muxing_tests,
++};
++
+ /* See
+  * https://lore.kernel.org/all/3e113525-aa89-b1e2-56b7-ca55bd41d057@samsung.com/
+  * and
+@@ -1040,5 +1120,6 @@ static struct kunit_suite vc5_pv_muxing_
+ kunit_test_suites(
+       &vc4_pv_muxing_test_suite,
+       &vc5_pv_muxing_test_suite,
++      &vc6_pv_muxing_test_suite,
+       &vc5_pv_muxing_bugs_test_suite
+ );
diff --git a/target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch b/target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch
new file mode 100644 (file)
index 0000000..455c64a
--- /dev/null
@@ -0,0 +1,64 @@
+From 3d849ab48cecab55862a4f2742f11937d66ba54b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:21:34 +0200
+Subject: [PATCH] drm/vc4: fkms: Rename plane related functions
+
+The name collide with the Full KMS functions that are going to be made
+public.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -670,8 +670,8 @@ static int vc4_plane_to_mb(struct drm_pl
+       return 0;
+ }
+-static int vc4_plane_atomic_check(struct drm_plane *plane,
+-                                struct drm_atomic_state *state)
++static int vc4_fkms_plane_atomic_check(struct drm_plane *plane,
++                                     struct drm_atomic_state *state)
+ {
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
+@@ -728,7 +728,7 @@ static int vc4_plane_atomic_async_check(
+ }
+ /* Called during init to allocate the plane's atomic state. */
+-static void vc4_plane_reset(struct drm_plane *plane)
++static void vc4_fkms_plane_reset(struct drm_plane *plane)
+ {
+       struct vc4_plane_state *vc4_state;
+@@ -788,7 +788,7 @@ static bool vc4_fkms_format_mod_supporte
+       }
+ }
+-static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
++static struct drm_plane_state *vc4_fkms_plane_duplicate_state(struct drm_plane *plane)
+ {
+       struct vc4_plane_state *vc4_state;
+@@ -809,8 +809,8 @@ static const struct drm_plane_funcs vc4_
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .destroy = vc4_plane_destroy,
+       .set_property = NULL,
+-      .reset = vc4_plane_reset,
+-      .atomic_duplicate_state = vc4_plane_duplicate_state,
++      .reset = vc4_fkms_plane_reset,
++      .atomic_duplicate_state = vc4_fkms_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+       .format_mod_supported = vc4_fkms_format_mod_supported,
+ };
+@@ -818,7 +818,7 @@ static const struct drm_plane_funcs vc4_
+ static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
+       .prepare_fb = drm_gem_plane_helper_prepare_fb,
+       .cleanup_fb = NULL,
+-      .atomic_check = vc4_plane_atomic_check,
++      .atomic_check = vc4_fkms_plane_atomic_check,
+       .atomic_update = vc4_plane_atomic_update,
+       .atomic_disable = vc4_plane_atomic_disable,
+       .atomic_async_check = vc4_plane_atomic_async_check,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch b/target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch
new file mode 100644 (file)
index 0000000..0105753
--- /dev/null
@@ -0,0 +1,95 @@
+From 14fe42ff341741d60ba338c401855dee7fb68754 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:24:37 +0200
+Subject: [PATCH] drm/vc4: tests: Use custom plane state for mock
+
+The current mock planes were just using the regular drm_plane_state,
+while the driver expect struct vc4_plane_state that subclasses
+drm_plane_state.
+
+Hook the proper implementations of reset, duplicate_state, destroy and
+atomic_check to create vc4_plane_state.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c |  7 ++++---
+ drivers/gpu/drm/vc4/vc4_drv.h              |  6 ++++++
+ drivers/gpu/drm/vc4/vc4_plane.c            | 12 ++++++------
+ 3 files changed, 16 insertions(+), 9 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -10,12 +10,13 @@
+ #include "vc4_mock.h"
+ static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = {
++      .atomic_check = vc4_plane_atomic_check,
+ };
+ static const struct drm_plane_funcs vc4_dummy_plane_funcs = {
+-      .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
+-      .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+-      .reset                  = drm_atomic_helper_plane_reset,
++      .atomic_destroy_state   = vc4_plane_destroy_state,
++      .atomic_duplicate_state = vc4_plane_duplicate_state,
++      .reset                  = vc4_plane_reset,
+ };
+ static const uint32_t vc4_dummy_plane_formats[] = {
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -1145,6 +1145,12 @@ int vc4_kms_load(struct drm_device *dev)
+ struct drm_plane *vc4_plane_init(struct drm_device *dev,
+                                enum drm_plane_type type,
+                                uint32_t possible_crtcs);
++void vc4_plane_reset(struct drm_plane *plane);
++void vc4_plane_destroy_state(struct drm_plane *plane,
++                           struct drm_plane_state *state);
++struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane);
++int vc4_plane_atomic_check(struct drm_plane *plane,
++                         struct drm_atomic_state *state);
+ int vc4_plane_create_additional_planes(struct drm_device *dev);
+ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+ u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -276,7 +276,7 @@ static bool plane_enabled(struct drm_pla
+       return state->fb && !WARN_ON(!state->crtc);
+ }
+-static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
++struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+ {
+       struct vc4_plane_state *vc4_state;
+       unsigned int i;
+@@ -312,8 +312,8 @@ static struct drm_plane_state *vc4_plane
+       return &vc4_state->base;
+ }
+-static void vc4_plane_destroy_state(struct drm_plane *plane,
+-                                  struct drm_plane_state *state)
++void vc4_plane_destroy_state(struct drm_plane *plane,
++                           struct drm_plane_state *state)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+       struct vc4_hvs *hvs = vc4->hvs;
+@@ -348,7 +348,7 @@ static void vc4_plane_destroy_state(stru
+ }
+ /* Called during init to allocate the plane's atomic state. */
+-static void vc4_plane_reset(struct drm_plane *plane)
++void vc4_plane_reset(struct drm_plane *plane)
+ {
+       struct vc4_plane_state *vc4_state;
+@@ -2000,8 +2000,8 @@ static int vc6_plane_mode_set(struct drm
+  * compute the dlist here and have all active plane dlists get updated
+  * in the CRTC's flush.
+  */
+-static int vc4_plane_atomic_check(struct drm_plane *plane,
+-                                struct drm_atomic_state *state)
++int vc4_plane_atomic_check(struct drm_plane *plane,
++                         struct drm_atomic_state *state)
+ {
+       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch b/target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch
new file mode 100644 (file)
index 0000000..ebab218
--- /dev/null
@@ -0,0 +1,36 @@
+From 3320e449e40eeb49b601dcbbd4bd72b8cb8f3054 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:26:58 +0200
+Subject: [PATCH] drm/vc4: tests: Add function to lookup a plane for a CRTC
+
+Some tests will need to find a plane to run a test on for a given CRTC.
+Let's create a small helper to do that.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -21,6 +21,20 @@ struct drm_crtc *vc4_find_crtc_for_encod
+       return NULL;
+ }
++static inline
++struct drm_plane *vc4_mock_find_plane_for_crtc(struct kunit *test,
++                                             struct drm_crtc *crtc)
++{
++      struct drm_device *drm = crtc->dev;
++      struct drm_plane *plane;
++
++      drm_for_each_plane(plane, drm)
++              if (plane->possible_crtcs & drm_crtc_mask(crtc))
++                      return plane;
++
++      return NULL;
++}
++
+ struct vc4_dummy_plane {
+       struct vc4_plane plane;
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch b/target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch
new file mode 100644 (file)
index 0000000..7d23029
--- /dev/null
@@ -0,0 +1,62 @@
+From a2f912c44b98acb6c10c977db105e199011c09b5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 12:57:53 +0200
+Subject: [PATCH] drm/vc4: tests: Add helper to add a new plane to a state
+
+We'll start to add some tests for the plane state logic, so let's create
+a helper to add a plane to an existing atomic state.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h       |  4 ++++
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 22 ++++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -42,6 +42,10 @@ struct vc4_dummy_plane {
+ struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
+                                       struct drm_device *drm,
+                                       enum drm_plane_type type);
++struct drm_plane *
++vc4_mock_atomic_add_plane(struct kunit *test,
++                        struct drm_atomic_state *state,
++                        struct drm_crtc *crtc);
+ struct vc4_dummy_crtc {
+       struct vc4_crtc crtc;
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0
+ #include <drm/drm_atomic_state_helper.h>
++#include <drm/drm_atomic_uapi.h>
+ #include <drm/drm_fourcc.h>
+ #include <drm/drm_modeset_helper_vtables.h>
+ #include <drm/drm_plane.h>
+@@ -46,3 +47,24 @@ struct vc4_dummy_plane *vc4_dummy_plane(
+       return dummy_plane;
+ }
++
++struct drm_plane *
++vc4_mock_atomic_add_plane(struct kunit *test,
++                        struct drm_atomic_state *state,
++                        struct drm_crtc *crtc)
++{
++      struct drm_plane_state *plane_state;
++      struct drm_plane *plane;
++      int ret;
++
++      plane = vc4_mock_find_plane_for_crtc(test, crtc);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
++
++      plane_state = drm_atomic_get_plane_state(state, plane);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_state);
++
++      ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
++      KUNIT_EXPECT_EQ(test, ret, 0);
++
++      return plane;
++}
diff --git a/target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch b/target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch
new file mode 100644 (file)
index 0000000..191181d
--- /dev/null
@@ -0,0 +1,26 @@
+From 418d2e0e652c3870c29dd2e462f052a88fa027da Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 12:59:05 +0200
+Subject: [PATCH] drm/vc4: tests: Support a few more plane formats
+
+We'll start testing our planes code in situations where we will use more
+than XRGB8888, so let's add a few common pixel formats.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -21,7 +21,10 @@ static const struct drm_plane_funcs vc4_
+ };
+ static const uint32_t vc4_dummy_plane_formats[] = {
++      DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_XRGB8888,
++      DRM_FORMAT_YUV420,
++      DRM_FORMAT_YUV422,
+ };
+ struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch b/target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch
new file mode 100644 (file)
index 0000000..5902d14
--- /dev/null
@@ -0,0 +1,358 @@
+From 39ae36d19bf6e59e3091f8f0ec6f4eac59aaf6f2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 13:43:32 +0200
+Subject: [PATCH] drm/vc4: tests: Introduce a test for LBM buffer size
+
+The BCM2712 comes with a different LBM size computation than the
+previous generations, so let's add the few examples provided as kunit
+tests to make sure we always satisfy those requirements.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/Makefile                  |   3 +-
+ drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c | 327 ++++++++++++++++++
+ 2 files changed, 329 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+
+--- a/drivers/gpu/drm/vc4/Makefile
++++ b/drivers/gpu/drm/vc4/Makefile
+@@ -31,7 +31,8 @@ vc4-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
+       tests/vc4_mock_crtc.o \
+       tests/vc4_mock_output.o \
+       tests/vc4_mock_plane.o \
+-      tests/vc4_test_pv_muxing.o
++      tests/vc4_test_pv_muxing.o \
++      tests/vc4_test_lbm_size.o
+ vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
+--- /dev/null
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+@@ -0,0 +1,327 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_atomic_uapi.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_plane.h>
++#include <drm/drm_kunit_helpers.h>
++
++#include "../../drm_crtc_internal.h"
++#include "../../drm_internal.h"
++
++#include <kunit/test.h>
++
++#include "../vc4_drv.h"
++
++#include "vc4_mock.h"
++
++u32 vc4_lbm_size(struct drm_plane_state *state);
++
++struct vc4_lbm_size_priv {
++      struct vc4_dev *vc4;
++      struct drm_file *file;
++      struct drm_modeset_acquire_ctx ctx;
++      struct drm_atomic_state *state;
++};
++
++struct vc4_lbm_size_param {
++      unsigned int src_w, src_h;
++      unsigned int crtc_w, crtc_h;
++      bool forced_alpha;
++      u32 fourcc;
++      enum vc4_scaling_mode expected_x_scaling[2];
++      enum vc4_scaling_mode expected_y_scaling[2];
++      unsigned int expected_lbm_size;
++};
++
++static const struct vc4_lbm_size_param vc4_test_lbm_size_params[] = {
++      {
++              .src_w = 256,
++              .crtc_w = 256,
++              .src_h = 256,
++              .crtc_h = 512,
++              .fourcc = DRM_FORMAT_ARGB8888,
++              .expected_x_scaling = { VC4_SCALING_NONE, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 32,
++      },
++      {
++              .src_w = 256,
++              .crtc_w = 179,
++              .src_h = 256,
++              .crtc_h = 512,
++              .fourcc = DRM_FORMAT_ARGB8888,
++              .expected_x_scaling = { VC4_SCALING_PPF, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 23,
++      },
++      {
++              .src_w = 256,
++              .crtc_w = 256,
++              .src_h = 256,
++              .crtc_h = 512,
++              .fourcc = DRM_FORMAT_XRGB8888,
++              .expected_x_scaling = { VC4_SCALING_NONE, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 24,
++      },
++      {
++              .src_w = 100,
++              .crtc_w = 73,
++              .src_h = 100,
++              .crtc_h = 73,
++              .fourcc = DRM_FORMAT_XRGB8888,
++              .expected_x_scaling = { VC4_SCALING_PPF, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 8,
++      },
++      {
++              .src_w = 256,
++              .crtc_w = 256,
++              .src_h = 256,
++              .crtc_h = 512,
++              .forced_alpha = true,
++              .fourcc = DRM_FORMAT_ARGB8888,
++              .expected_x_scaling = { VC4_SCALING_NONE, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 24,
++      },
++      {
++              .src_w = 100,
++              .crtc_w = 73,
++              .src_h = 100,
++              .crtc_h = 73,
++              .forced_alpha = true,
++              .fourcc = DRM_FORMAT_ARGB8888,
++              .expected_x_scaling = { VC4_SCALING_PPF, },
++              .expected_y_scaling = { VC4_SCALING_PPF, },
++              .expected_lbm_size = 8,
++      },
++      {
++              .src_w = 256,
++              .crtc_w = 94,
++              .src_h = 256,
++              .crtc_h = 94,
++              .fourcc = DRM_FORMAT_ARGB8888,
++              .expected_x_scaling = { VC4_SCALING_TPZ, },
++              .expected_y_scaling = { VC4_SCALING_TPZ, },
++              .expected_lbm_size = 6,
++      },
++
++/*
++ * TODO: Those tests reflect the LBM size calculation examples, but the
++ * driver ends up taking different scaler filters decisions, and thus
++ * doesn't end up with the same sizes. It would be valuable to have
++ * those tests, but the driver doesn't take a bad decision either, so
++ * it's not clear what we should do at this point.
++ */
++#if 0
++      {
++              .src_w = 320,
++              .crtc_w = 320,
++              .src_h = 320,
++              .crtc_h = 320,
++              .fourcc = DRM_FORMAT_YUV420,
++              .expected_x_scaling = { VC4_SCALING_NONE, VC4_SCALING_NONE, },
++              .expected_y_scaling = { VC4_SCALING_NONE, VC4_SCALING_PPF, },
++              .expected_lbm_size = 10,
++      },
++      {
++              .src_w = 512,
++              .crtc_w = 512,
++              .src_h = 512,
++              .crtc_h = 256,
++              .fourcc = DRM_FORMAT_YUV420,
++              .expected_x_scaling = { VC4_SCALING_NONE, VC4_SCALING_NONE, },
++              .expected_y_scaling = { VC4_SCALING_TPZ, VC4_SCALING_NONE, },
++              .expected_lbm_size = 5,
++      },
++      {
++              .src_w = 486,
++              .crtc_w = 157,
++              .src_h = 404,
++              .crtc_h = 929,
++              .fourcc = DRM_FORMAT_YUV422,
++              .expected_x_scaling = { VC4_SCALING_PPF, VC4_SCALING_PPF, },
++              .expected_y_scaling = { VC4_SCALING_PPF, VC4_SCALING_PPF, },
++              .expected_lbm_size = 20,
++      },
++      {
++              .src_w = 320,
++              .crtc_w = 128,
++              .src_h = 176,
++              .crtc_h = 70,
++              .fourcc = DRM_FORMAT_YUV420,
++              .expected_x_scaling = { VC4_SCALING_TPZ, VC4_SCALING_TPZ, },
++              .expected_y_scaling = { VC4_SCALING_TPZ, VC4_SCALING_TPZ, },
++              .expected_lbm_size = 8,
++      },
++#endif
++};
++
++static void vc4_test_lbm_size_desc(const struct vc4_lbm_size_param *t, char *desc)
++{
++      snprintf(desc, KUNIT_PARAM_DESC_SIZE,
++               "%ux%u to %ux%u %s(%p4cc)",
++               t->src_w, t->src_h,
++               t->crtc_w, t->crtc_h,
++               t->forced_alpha ? "with forced alpha " : "",
++               &t->fourcc);
++}
++
++KUNIT_ARRAY_PARAM(vc4_test_lbm_size,
++                vc4_test_lbm_size_params,
++                vc4_test_lbm_size_desc);
++
++static void drm_vc4_test_vc4_lbm_size(struct kunit *test)
++{
++      const struct vc4_lbm_size_param *params = test->param_value;
++      const struct vc4_lbm_size_priv *priv = test->priv;
++      const struct drm_format_info *info;
++      struct drm_mode_fb_cmd2 fb_req = { };
++      struct drm_atomic_state *state = priv->state;
++      struct vc4_plane_state *vc4_plane_state;
++      struct drm_plane_state *plane_state;
++      struct vc4_dummy_output *output;
++      struct drm_framebuffer *fb;
++      struct drm_plane *plane;
++      struct drm_crtc *crtc;
++      unsigned int i;
++      int ret;
++
++      info = drm_format_info(params->fourcc);
++      KUNIT_ASSERT_NOT_NULL(test, info);
++
++      output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
++
++      crtc = vc4_find_crtc_for_encoder(test, &output->encoder.base);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
++
++      plane = vc4_mock_atomic_add_plane(test, state, crtc);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
++
++      plane_state = drm_atomic_get_plane_state(state, plane);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_state);
++
++      vc4_plane_state = to_vc4_plane_state(plane_state);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4_plane_state);
++
++      fb_req.pixel_format = params->fourcc;
++      fb_req.width = params->src_w;
++      fb_req.height = params->src_h;
++
++      for (i = 0; i < info->num_planes; i++) {
++              struct drm_mode_create_dumb dumb_args = { };
++
++              dumb_args.width = params->src_w;
++              dumb_args.height = params->src_h;
++              dumb_args.bpp = drm_format_info_bpp(info, i);
++
++              ret = drm_mode_create_dumb(state->dev, &dumb_args, priv->file);
++              KUNIT_ASSERT_EQ(test, ret, 0);
++
++              fb_req.handles[i] = dumb_args.handle;
++              fb_req.pitches[i] = dumb_args.pitch;
++      }
++
++      fb = drm_internal_framebuffer_create(state->dev, &fb_req, priv->file);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb);
++
++      drm_atomic_set_fb_for_plane(plane_state, fb);
++
++      plane_state->src_x = 0;
++      plane_state->src_y = 0;
++      plane_state->src_h = params->src_h << 16;
++      plane_state->src_w = params->src_w << 16;
++
++      plane_state->crtc_x = 0;
++      plane_state->crtc_y = 0;
++      plane_state->crtc_h = params->crtc_h;
++      plane_state->crtc_w = params->crtc_w;
++
++      if (params->forced_alpha)
++              plane_state->alpha = 128;
++
++      ret = drm_atomic_check_only(state);
++      KUNIT_ASSERT_EQ(test, ret, 0);
++
++      KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm.size, params->expected_lbm_size);
++
++      for (i = 0; i < 2; i++) {
++              KUNIT_EXPECT_EQ(test,
++                              vc4_plane_state->x_scaling[i],
++                              params->expected_x_scaling[i]);
++              KUNIT_EXPECT_EQ(test,
++                              vc4_plane_state->y_scaling[i],
++                              params->expected_y_scaling[i]);
++      }
++
++      drm_framebuffer_put(fb);
++
++      for (i = 0; i < info->num_planes; i++)
++              drm_mode_destroy_dumb(state->dev, fb_req.handles[i], priv->file);
++}
++
++static struct kunit_case vc4_lbm_size_tests[] = {
++      KUNIT_CASE_PARAM(drm_vc4_test_vc4_lbm_size,
++                       vc4_test_lbm_size_gen_params),
++      {}
++};
++
++static int vc4_lbm_size_test_init(struct kunit *test)
++{
++      struct drm_atomic_state *state;
++      struct vc4_lbm_size_priv *priv;
++      struct drm_device *drm;
++      struct vc4_dev *vc4;
++
++      priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
++      KUNIT_ASSERT_NOT_NULL(test, priv);
++      test->priv = priv;
++
++      vc4 = vc6_mock_device(test);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
++      priv->vc4 = vc4;
++
++      priv->file = drm_file_alloc(priv->vc4->base.primary);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->file);
++
++      drm_modeset_acquire_init(&priv->ctx, 0);
++
++      drm = &vc4->base;
++      state = drm_atomic_state_alloc(drm);
++      KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
++
++      state->acquire_ctx = &priv->ctx;
++
++      priv->state = state;
++
++      return 0;
++}
++
++static void vc4_lbm_size_test_exit(struct kunit *test)
++{
++      struct vc4_lbm_size_priv *priv = test->priv;
++      struct vc4_dev *vc4 = priv->vc4;
++      struct drm_device *drm = &vc4->base;
++      struct drm_atomic_state *state = priv->state;
++
++      drm_atomic_state_put(state);
++      drm_modeset_drop_locks(&priv->ctx);
++      drm_modeset_acquire_fini(&priv->ctx);
++      drm_file_free(priv->file);
++      drm_dev_unregister(drm);
++      drm_kunit_helper_free_device(test, vc4->dev);
++}
++
++static struct kunit_suite vc4_lbm_size_test_suite = {
++      .name = "vc4-lbm-size",
++      .init = vc4_lbm_size_test_init,
++      .exit = vc4_lbm_size_test_exit,
++      .test_cases = vc4_lbm_size_tests,
++};
++
++kunit_test_suite(vc4_lbm_size_test_suite);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch b/target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch
new file mode 100644 (file)
index 0000000..f20b0cf
--- /dev/null
@@ -0,0 +1,40 @@
+From 352d96c9e50012f2b5e5dde9933af8d570e7dc81 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 17 Jul 2023 17:45:32 +0100
+Subject: [PATCH] drm/vc4: kms: Avoid setting core and disp clocks for hdmi
+ modes
+
+On 2712, the firmware always runs these clock at a speed sufficient
+for dual 4kp60.
+
+The requests here prevent the gpu from going into its lowest voltage
+mode, so just skip the clock requests.
+
+With this applied the idle voltage on my pi 5 reduces from 0.7424V
+to 0.72V.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -435,7 +435,7 @@ static void vc4_atomic_commit_tail(struc
+               old_hvs_state->fifo_state[channel].pending_commit = NULL;
+       }
+-      if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
++      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+                                              new_hvs_state->core_clock_rate);
+               unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -489,7 +489,7 @@ static void vc4_atomic_commit_tail(struc
+       drm_atomic_helper_cleanup_planes(dev, state);
+-      if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
++      if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+               unsigned long core_rate = min_t(unsigned long,
+                                               hvs->max_core_rate,
+                                               new_hvs_state->core_clock_rate);
diff --git a/target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch b/target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch
new file mode 100644 (file)
index 0000000..b489bbc
--- /dev/null
@@ -0,0 +1,240 @@
+From bb0839405b61da6e6ae7141f7433f6a121725e6f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 31 Aug 2023 11:45:38 +0100
+Subject: [PATCH] drm/vc4: Assign LBM memory during atomic_flush.
+
+Avoid double buffering LBM allocations by making the
+allocation a single alloc per crtc at atomic_flush.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c |  2 +-
+ drivers/gpu/drm/vc4/vc4_drv.h                 |  8 ++--
+ drivers/gpu/drm/vc4/vc4_hvs.c                 | 47 ++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc4_plane.c               | 38 +++------------
+ 4 files changed, 58 insertions(+), 37 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+@@ -248,7 +248,7 @@ static void drm_vc4_test_vc4_lbm_size(st
+       ret = drm_atomic_check_only(state);
+       KUNIT_ASSERT_EQ(test, ret, 0);
+-      KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm.size, params->expected_lbm_size);
++      KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm_size, params->expected_lbm_size);
+       for (i = 0; i < 2; i++) {
+               KUNIT_EXPECT_EQ(test,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -437,6 +437,8 @@ struct vc4_plane_state {
+       u32 dlist_size; /* Number of dwords allocated for the display list */
+       u32 dlist_count; /* Number of used dwords in the display list. */
++      u32 lbm_size; /* LBM requirements for this plane */
++
+       /* Offset in the dlist to various words, for pageflip or
+        * cursor updates.
+        */
+@@ -462,9 +464,6 @@ struct vc4_plane_state {
+       bool is_unity;
+       bool is_yuv;
+-      /* Our allocation in LBM for temporary storage during scaling. */
+-      struct drm_mm_node lbm;
+-
+       /* Our allocation in UPM for prefetching. */
+       struct drm_mm_node upm[DRM_FORMAT_MAX_PLANES];
+@@ -661,6 +660,9 @@ struct vc4_crtc {
+        * access to that value.
+        */
+       unsigned int current_hvs_channel;
++
++      /* @lbm: Our allocation in LBM for temporary storage during scaling. */
++      struct drm_mm_node lbm;
+ };
+ static inline struct vc4_crtc *
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1103,6 +1103,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
+       struct drm_plane *plane;
+       const struct drm_plane_state *plane_state;
+       u32 dlist_count = 0;
++      u32 lbm_count = 0;
+       /* The pixelvalve can only feed one encoder (and encoders are
+        * 1:1 with connectors.)
+@@ -1111,6 +1112,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
+               return -EINVAL;
+       drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) {
++              const struct vc4_plane_state *vc4_plane_state =
++                                              to_vc4_plane_state(plane_state);
+               u32 plane_dlist_count = vc4_plane_dlist_size(plane_state);
+               drm_dbg_driver(dev, "[CRTC:%d:%s] Found [PLANE:%d:%s] with DLIST size: %u\n",
+@@ -1119,6 +1122,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
+                              plane_dlist_count);
+               dlist_count += plane_dlist_count;
++              lbm_count += vc4_plane_state->lbm_size;
+       }
+       dlist_count++; /* Account for SCALER_CTL0_END. */
+@@ -1132,6 +1136,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
+       vc4_state->mm = alloc;
++      /* FIXME: Check total lbm allocation here */
++
+       return vc4_hvs_gamma_check(crtc, state);
+ }
+@@ -1246,7 +1252,10 @@ void vc4_hvs_atomic_flush(struct drm_crt
+       bool debug_dump_regs = false;
+       bool enable_bg_fill = false;
+       u32 __iomem *dlist_start, *dlist_next;
++      unsigned long irqflags;
+       unsigned int zpos = 0;
++      u32 lbm_offset = 0;
++      u32 lbm_size = 0;
+       bool found = false;
+       int idx;
+@@ -1265,6 +1274,35 @@ void vc4_hvs_atomic_flush(struct drm_crt
+               vc4_hvs_dump_state(hvs);
+       }
++      drm_atomic_crtc_for_each_plane(plane, crtc) {
++              vc4_plane_state = to_vc4_plane_state(plane->state);
++              lbm_size += vc4_plane_state->lbm_size;
++      }
++
++      if (drm_mm_node_allocated(&vc4_crtc->lbm)) {
++              spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
++              drm_mm_remove_node(&vc4_crtc->lbm);
++              spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
++      }
++
++      if (lbm_size) {
++              int ret;
++
++              spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
++              ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
++                                               &vc4_crtc->lbm,
++                                               lbm_size, 1,
++                                               0, 0);
++              spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
++
++              if (ret) {
++                      pr_err("Failed to allocate LBM ret %d\n", ret);
++                      return;
++              }
++      }
++
++      lbm_offset = vc4_crtc->lbm.start;
++
+       dlist_start = vc4->hvs->dlist + vc4_state->mm->mm_node.start;
+       dlist_next = dlist_start;
+@@ -1276,6 +1314,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+                       if (plane->state->normalized_zpos != zpos)
+                               continue;
++                      vc4_plane_state = to_vc4_plane_state(plane->state);
++
+                       /* Is this the first active plane? */
+                       if (dlist_next == dlist_start) {
+                               /* We need to enable background fill when a plane
+@@ -1286,10 +1326,15 @@ void vc4_hvs_atomic_flush(struct drm_crt
+                                * already needs it or all planes on top blend from
+                                * the first or a lower plane.
+                                */
+-                              vc4_plane_state = to_vc4_plane_state(plane->state);
+                               enable_bg_fill = vc4_plane_state->needs_bg_fill;
+                       }
++                      if (vc4_plane_state->lbm_size) {
++                              vc4_plane_state->dlist[vc4_plane_state->lbm_offset] =
++                                                              lbm_offset;
++                              lbm_offset += vc4_plane_state->lbm_size;
++                      }
++
+                       dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+                       found = true;
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -288,7 +288,6 @@ struct drm_plane_state *vc4_plane_duplic
+       if (!vc4_state)
+               return NULL;
+-      memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
+       memset(&vc4_state->upm, 0, sizeof(vc4_state->upm));
+       for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++)
+@@ -320,14 +319,6 @@ void vc4_plane_destroy_state(struct drm_
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+       unsigned int i;
+-      if (drm_mm_node_allocated(&vc4_state->lbm)) {
+-              unsigned long irqflags;
+-
+-              spin_lock_irqsave(&hvs->mm_lock, irqflags);
+-              drm_mm_remove_node(&vc4_state->lbm);
+-              spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
+-      }
+-
+       for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) {
+               unsigned long irqflags;
+@@ -903,12 +894,13 @@ static int vc4_plane_allocate_lbm(struct
+       struct vc4_dev *vc4 = to_vc4_dev(drm);
+       struct drm_plane *plane = state->plane;
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+-      unsigned long irqflags;
+       u32 lbm_size;
+       lbm_size = vc4_lbm_size(state);
+-      if (!lbm_size)
++      if (!lbm_size) {
++              vc4_state->lbm_size = 0;
+               return 0;
++      }
+       /*
+        * NOTE: BCM2712 doesn't need to be aligned, since the size
+@@ -925,28 +917,10 @@ static int vc4_plane_allocate_lbm(struct
+       if (WARN_ON(!vc4_state->lbm_offset))
+               return -EINVAL;
+-      /* Allocate the LBM memory that the HVS will use for temporary
+-       * storage due to our scaling/format conversion.
++      /* FIXME: Add loop here that ensures that the total LBM assigned in this
++       *  state is less than the total lbm size
+        */
+-      if (!drm_mm_node_allocated(&vc4_state->lbm)) {
+-              int ret;
+-
+-              spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
+-              ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+-                                               &vc4_state->lbm,
+-                                               lbm_size, 1,
+-                                               0, 0);
+-              spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+-
+-              if (ret) {
+-                      drm_err(drm, "Failed to allocate LBM entry: %d\n", ret);
+-                      return ret;
+-              }
+-      } else {
+-              WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
+-      }
+-
+-      vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start;
++      vc4_state->lbm_size = lbm_size;
+       return 0;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch b/target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch
new file mode 100644 (file)
index 0000000..ef70de7
--- /dev/null
@@ -0,0 +1,33 @@
+From b1bd2f406eab321be642decd6aee6b6222aec62b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 28 Jul 2023 17:40:27 +0100
+Subject: [PATCH] drm/panel: simple: Alter the timing for the Pi 7" DSI display
+
+vc4 has always fixed up the timing, so the values defined have
+never actually appeared on the wire.
+The display appears to want a slightly longer HFP, so extend
+the timings and recompute the clock to give the same frame rate.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-simple.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-simple.c
++++ b/drivers/gpu/drm/panel/panel-simple.c
+@@ -3241,11 +3241,11 @@ static const struct panel_desc qishenglo
+ };
+ static const struct drm_display_mode raspberrypi_7inch_mode = {
+-      .clock = 25979400 / 1000,
++      .clock = 27777,
+       .hdisplay = 800,
+-      .hsync_start = 800 + 2,
+-      .hsync_end = 800 + 2 + 2,
+-      .htotal = 800 + 2 + 2 + 46,
++      .hsync_start = 800 + 59,
++      .hsync_end = 800 + 59 + 2,
++      .htotal = 800 + 59 + 2 + 46,
+       .vdisplay = 480,
+       .vsync_start = 480 + 7,
+       .vsync_end = 480 + 7 + 2,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch b/target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch
new file mode 100644 (file)
index 0000000..0a4203f
--- /dev/null
@@ -0,0 +1,33 @@
+From c7cf33911d477fe55a91a9e4d84dad857b244ae3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 28 Jul 2023 18:10:53 +0100
+Subject: [PATCH] drm/panel: waveshare: Fix up timings for 10.1" panel
+
+The 10.1" panel doesn't work with the timings defined. vc4
+will always have been fixing up the timing due to the limited
+integer divider, so compute the fixed up mode and use it
+directly.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -112,11 +112,11 @@ static const struct drm_display_mode ws_
+  * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm
+  */
+ static const struct drm_display_mode ws_panel_10_1_mode = {
+-      .clock = 76800,
++      .clock = 83333,
+       .hdisplay = 1280,
+-      .hsync_start = 1280 + 40,
+-      .hsync_end = 1280 + 40 + 20,
+-      .htotal = 1280 + 40 + 20 + 40,
++      .hsync_start = 1280 + 156,
++      .hsync_end = 1280 + 156 + 20,
++      .htotal = 1280 + 156 + 20 + 40,
+       .vdisplay = 800,
+       .vsync_start = 800 + 40,
+       .vsync_end = 800 + 40 + 48,
diff --git a/target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch b/target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch
new file mode 100644 (file)
index 0000000..9d7869a
--- /dev/null
@@ -0,0 +1,34 @@
+From 72c25bbb761de2b2acd9f8b652d63e2a2f1caeed Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Mon, 11 Sep 2023 12:17:25 +0300
+Subject: [PATCH] media: i2c: imx477: Fix locking in imx477_init_controls()
+
+The driver does not lock the imx477 mutex when calling
+imx477_set_framing_limits(), leading to:
+
+WARNING: CPU: 3 PID: 426 at drivers/media/v4l2-core/v4l2-ctrls-api.c:934 __v4l2_ctrl_modify_range+0x1a0/0x210 [
+videodev]
+
+Fix this by taking the lock.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/i2c/imx477.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2069,9 +2069,13 @@ static int imx477_init_controls(struct i
+       imx477->sd.ctrl_handler = ctrl_hdlr;
++      mutex_lock(&imx477->mutex);
++
+       /* Setup exposure and frame/line length limits. */
+       imx477_set_framing_limits(imx477);
++      mutex_unlock(&imx477->mutex);
++
+       return 0;
+ error:
diff --git a/target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch b/target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch
new file mode 100644 (file)
index 0000000..10339e2
--- /dev/null
@@ -0,0 +1,57 @@
+From 2ff65ffbdeb0c8764985af19df2a687a126136f4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 29 Sep 2023 16:55:28 +0100
+Subject: [PATCH] overlays: Fix vc4-kms-dsi-7inch
+
+Fix the touchscreen.
+
+See: https://github.com/raspberrypi/linux/issues/5619
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi          | 13 ++++---------
+ .../boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts |  2 +-
+ 2 files changed, 5 insertions(+), 10 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -22,11 +22,13 @@
+               };
+       };
+-      fragment@12 {
+-              target = <&i2cbus>;
++      ts_i2c_frag: fragment@12 {
++              target = <&i2c_csi_dsi>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
++                      status = "okay";
++
+                       ft5406: ts@38 {
+                               compatible = "edt,edt-ft5506";
+                               reg = <0x38>;
+@@ -37,13 +39,6 @@
+               };
+       };
+-      ts_i2c_frag: fragment@13 {
+-              target = <&i2c_csi_dsi>;
+-              i2cbus: __overlay__ {
+-                      status = "okay";
+-              };
+-      };
+-
+       __overrides__ {
+               sizex = <&ft5406>,"touchscreen-size-x:0";
+               sizey = <&ft5406>,"touchscreen-size-y:0";
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
+@@ -119,6 +119,6 @@
+                      <&panel_disp>, "reg:0=0",
+                      <&reg_bridge>, "reg:0=0",
+                      <&reg_bridge>, "regulator-name=bridge_reg_0";
+-              disable_touch = <0>, "-10-11-12";
++              disable_touch = <&ft5406>, "status=disabled";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch b/target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch
new file mode 100644 (file)
index 0000000..c8fd2a2
--- /dev/null
@@ -0,0 +1,56 @@
+From a1caea3c996f6bfa8c9568f521257f4e473285fb Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Fri, 29 Sep 2023 21:50:28 +0200
+Subject: [PATCH] ASoC: hdmi-codec: Fix broken channel map reporting
+
+Commit b84b53149476b22cc3b8677b771fb4cf06d1d455 upstream.
+
+Commit 4e0871333661 ("ASoC: hdmi-codec: fix channel info for
+compressed formats") accidentally changed hcp->chmap_idx from
+ca_id, the CEA channel allocation ID, to idx, the index to
+the table of channel mappings ordered by preference.
+
+This resulted in wrong channel maps being reported to userspace,
+eg for 5.1 "FL,FR,LFE,FC" was reported instead of the expected
+"FL,FR,LFE,FC,RL,RR":
+
+~ # speaker-test -c 6 -t sine
+...
+ 0 - Front Left
+ 3 - Front Center
+ 1 - Front Right
+ 2 - LFE
+ 4 - Unknown
+ 5 - Unknown
+
+~ # amixer cget iface=PCM,name='Playback Channel Map' | grep ': values'
+  : values=3,4,8,7,0,0,0,0
+
+Switch this back to ca_id in case of PCM audio so the correct channel
+map is reported again and set it to HDMI_CODEC_CHMAP_IDX_UNKNOWN in
+case of non-PCM audio so the PCM channel map control returns "Unknown"
+channels (value 0).
+
+Fixes: 4e0871333661 ("ASoC: hdmi-codec: fix channel info for compressed formats")
+Cc: stable@vger.kernel.org
+Signed-off-by: Matthias Reichl <hias@horus.com>
+Link: https://lore.kernel.org/r/20230929195027.97136-1-hias@horus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/codecs/hdmi-codec.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/sound/soc/codecs/hdmi-codec.c
++++ b/sound/soc/codecs/hdmi-codec.c
+@@ -520,7 +520,10 @@ static int hdmi_codec_fill_codec_params(
+       hp->sample_rate = sample_rate;
+       hp->channels = channels;
+-      hcp->chmap_idx = idx;
++      if (pcm_audio)
++              hcp->chmap_idx = ca_id;
++      else
++              hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+       return 0;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch b/target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch
new file mode 100644 (file)
index 0000000..7f1b505
--- /dev/null
@@ -0,0 +1,48 @@
+From 3922bebc11fcc8459c798cfcb582828f9bbaa9e9 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 11:33:53 +0300
+Subject: [PATCH] media: rp1: cfe: Fix use of freed memory on errors
+
+cfe_probe_complete() calls cfe_put() on both success and fail code paths.
+This works for the success path, but causes the cfe_device struct to be
+freed, even if it will be used later in the teardown code.
+
+Fix this by making the ref handling a bit saner: Let the video nodes
+have the refs as they do now, but also keep a ref in the "main" driver,
+released only at cfe_remove() time. This way the driver does not depend
+on the video nodes keeping the refs.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1837,17 +1837,10 @@ static int cfe_probe_complete(struct cfe
+               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;
+ }
+@@ -2129,6 +2122,8 @@ static int cfe_remove(struct platform_de
+       v4l2_device_unregister(&cfe->v4l2_dev);
++      cfe_put(cfe);
++
+       return 0;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch b/target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch
new file mode 100644 (file)
index 0000000..d49856c
--- /dev/null
@@ -0,0 +1,88 @@
+From 84c9958dd71b8a4dcf16cbf6fdb867c668652634 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 27 Sep 2023 16:00:39 +0300
+Subject: [PATCH] media: rp1: cfe: Fix width & height in cfe_start_channel()
+
+The logic for handling width & height in cfe_start_channel() is somewhat
+odd and, afaics, broken. The code reads:
+
+bool start_fe = is_fe_enabled(cfe) &&
+                test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+if (start_fe || is_image_output_node(node)) {
+        width = node->fmt.fmt.pix.width;
+        height = node->fmt.fmt.pix.height;
+}
+
+cfe_start_channel() is called for all video nodes that will be used. So
+this means that if, say, fe_stats is enabled as the last node, start_fe
+will be true, and width and height will be taken from fe_stats' node.
+The width and height will thus contain garbage, which then gets
+programmed to the csi2 registers.
+
+It seems that this often still works fine, though, probably if the width
+& height are large enough.
+
+Drop the above code, and instead get the width & height from the csi2
+subdev's sink pad for the csi2 channel that is used. For metadata the
+width & height will be 0 as before.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -763,20 +763,16 @@ static void cfe_start_channel(struct cfe
+       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) {
++              unsigned int width, height;
++
+               WARN_ON(!is_fe_enabled(cfe));
+               cfe_dbg("%s: %s using csi2 channel %d\n",
+                       __func__, node_desc[FE_OUT0].name,
+@@ -785,6 +781,9 @@ static void cfe_start_channel(struct cfe
+               source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, cfe->fe_csi2_channel);
+               fmt = find_format_by_code(source_fmt->code);
++              width = source_fmt->width;
++              height = source_fmt->height;
++
+               /*
+                * Start the associated CSI2 Channel as well.
+                *
+@@ -800,6 +799,8 @@ static void cfe_start_channel(struct cfe
+       }
+       if (is_csi2_node(node)) {
++              unsigned int width = 0, height = 0;
++
+               u32 mode = CSI2_MODE_NORMAL;
+               source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
+@@ -807,6 +808,9 @@ static void cfe_start_channel(struct cfe
+               fmt = find_format_by_code(source_fmt->code);
+               if (is_image_output_node(node)) {
++                      width = source_fmt->width;
++                      height = source_fmt->height;
++
+                       if (node->fmt.fmt.pix.pixelformat ==
+                                       fmt->remap[CFE_REMAP_16BIT])
+                               mode = CSI2_MODE_REMAP;
diff --git a/target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch b/target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch
new file mode 100644 (file)
index 0000000..2a3915b
--- /dev/null
@@ -0,0 +1,36 @@
+From 62e8ab88d2c230dad122aabe2ad0e227d7ceba40 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 10:42:22 +0300
+Subject: [PATCH] media: rp1: csi2: Fix missing reg writes
+
+The driver has two places where it writes a register based on a
+condition, and when that condition is false, the driver presumes that
+the register has the reset value. This is not a good idea, so fix those
+places to always write the register.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -253,6 +253,7 @@ void csi2_start_channel(struct csi2_devi
+                */
+               set_field(&ctrl, 0x3ff, LC_MASK);
+               set_field(&ctrl, 0x00, CH_MODE_MASK);
++              csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+       }
+       set_field(&ctrl, dt, DT_MASK);
+@@ -277,8 +278,8 @@ void csi2_open_rx(struct csi2_device *cs
+ {
+       dphy_start(&csi2->dphy);
+-      if (!csi2->multipacket_line)
+-              csi2_reg_write(csi2, CSI2_CTRL, EOP_IS_EOL);
++      csi2_reg_write(csi2, CSI2_CTRL,
++                     csi2->multipacket_line ? 0 : EOP_IS_EOL);
+ }
+ void csi2_close_rx(struct csi2_device *csi2)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch b/target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch
new file mode 100644 (file)
index 0000000..f468b7a
--- /dev/null
@@ -0,0 +1,33 @@
+From 455c4ae2c70348a5842835d2f67f7cd8e665a2a6 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 16:03:07 +0300
+Subject: [PATCH] media: rp1: fe: Use ~0, not -1, when working with unsigned
+ values
+
+Use ~0, not -1, when working with unsigned values (-1 is not unsigned).
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -372,7 +372,7 @@ void pisp_fe_submit_job(struct pisp_fe_d
+ 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_STATUS, ~0);
+       pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1);
+       fe->inframe_count = 0;
+ }
+@@ -383,7 +383,7 @@ void pisp_fe_stop(struct pisp_fe_device
+       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);
++      pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ }
+ static struct pisp_fe_device *to_pisp_fe_device(struct v4l2_subdev *subdev)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch b/target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch
new file mode 100644 (file)
index 0000000..3344c32
--- /dev/null
@@ -0,0 +1,26 @@
+From bf1709d2cf2b57c4ea98b8363156835249ae02cc Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 12:41:35 +0300
+Subject: [PATCH] media: rp1: cfe: Fix verbose debug print
+
+The debug print in cfe_schedule_next_csi2_job() is printed every frame,
+and should thus use cfe_dbg_irq() to avoid spamming, rather than cfe_dbg().
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -518,8 +518,8 @@ static void cfe_schedule_next_csi2_job(s
+               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);
++              cfe_dbg_irq("%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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch b/target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch
new file mode 100644 (file)
index 0000000..14fa044
--- /dev/null
@@ -0,0 +1,227 @@
+From a1ea528e187ee045aeff929ff0f4b2e53fdd970f Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 10:12:37 +0300
+Subject: [PATCH] media: rp1: cfe: Rename xxx_dbg_irq() to xxx_dbg_verbose()
+
+Rename the xxx_dbg_irq() macros to xxx_dbg_verbose(), as they can be
+used to verbose debugs outside irq context too.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 40 +++++++++----------
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h  |  2 +-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 26 ++++++------
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c    | 12 +++---
+ 4 files changed, 40 insertions(+), 40 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -49,11 +49,11 @@
+ #define CFE_MODULE_NAME       "rp1-cfe"
+ #define CFE_VERSION   "1.0"
+-bool cfe_debug_irq;
++bool cfe_debug_verbose;
+-#define cfe_dbg_irq(fmt, arg...)                              \
++#define cfe_dbg_verbose(fmt, arg...)                          \
+       do {                                                  \
+-              if (cfe_debug_irq)                            \
++              if (cfe_debug_verbose)                        \
+                       dev_dbg(&cfe->pdev->dev, fmt, ##arg); \
+       } while (0)
+ #define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg)
+@@ -518,8 +518,8 @@ static void cfe_schedule_next_csi2_job(s
+               node->next_frm = buf;
+               list_del(&buf->list);
+-              cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
+-                          node_desc[node->id].name, &buf->vb.vb2_buf);
++              cfe_dbg_verbose("%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;
+@@ -550,8 +550,8 @@ static void cfe_schedule_next_pisp_job(s
+               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);
++              cfe_dbg_verbose("%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;
+@@ -573,8 +573,8 @@ static bool cfe_check_job_ready(struct c
+                       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);
++                      cfe_dbg_verbose("%s: [%s] has no buffer, unable to schedule job\n",
++                              __func__, node_desc[i].name);
+                       return false;
+               }
+       }
+@@ -592,7 +592,7 @@ static void cfe_prepare_next_job(struct
+       /* 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__);
++      cfe_dbg_verbose("%s: end with scheduled job\n", __func__);
+ }
+ static void cfe_process_buffer_complete(struct cfe_node *node,
+@@ -600,8 +600,8 @@ static void cfe_process_buffer_complete(
+ {
+       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);
++      cfe_dbg_verbose("%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);
+@@ -621,8 +621,8 @@ static void cfe_sof_isr_handler(struct c
+ {
+       struct cfe_device *cfe = node->cfe;
+-      cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+-                  cfe->sequence);
++      cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++                      cfe->sequence);
+       node->cur_frm = node->next_frm;
+       node->next_frm = NULL;
+@@ -651,8 +651,8 @@ static void cfe_eof_isr_handler(struct c
+ {
+       struct cfe_device *cfe = node->cfe;
+-      cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+-                  cfe->sequence);
++      cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++                      cfe->sequence);
+       if (node->cur_frm)
+               cfe_process_buffer_complete(node, cfe->sequence);
+@@ -921,8 +921,8 @@ static int cfe_buffer_prepare(struct vb2
+       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);
++      cfe_dbg_verbose("%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;
+@@ -954,8 +954,8 @@ static void cfe_buffer_queue(struct vb2_
+       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);
++      cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++                      node_desc[node->id].name, vb);
+       spin_lock_irqsave(&cfe->state_lock, flags);
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -11,7 +11,7 @@
+ #include <linux/media-bus-format.h>
+ #include <linux/videodev2.h>
+-extern bool cfe_debug_irq;
++extern bool cfe_debug_verbose;
+ enum cfe_remap_types {
+       CFE_REMAP_16BIT,
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -16,9 +16,9 @@
+ #include "csi2.h"
+ #include "cfe.h"
+-#define csi2_dbg_irq(fmt, arg...)                                 \
++#define csi2_dbg_verbose(fmt, arg...)                             \
+       do {                                                      \
+-              if (cfe_debug_irq)                                \
++              if (cfe_debug_verbose)                            \
+                       dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \
+       } while (0)
+ #define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg)
+@@ -154,7 +154,7 @@ void csi2_isr(struct csi2_device *csi2,
+       u32 status;
+       status = csi2_reg_read(csi2, CSI2_STATUS);
+-      csi2_dbg_irq("ISR: STA: 0x%x\n", status);
++      csi2_dbg_verbose("ISR: STA: 0x%x\n", status);
+       /* Write value back to clear the interrupts */
+       csi2_reg_write(csi2, CSI2_STATUS, status);
+@@ -167,16 +167,16 @@ void csi2_isr(struct csi2_device *csi2,
+               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);
++              csi2_dbg_verbose("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));
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -114,9 +114,9 @@ static const struct pisp_fe_config_param
+                                       sizeof(struct pisp_fe_output_config)         },
+ };
+-#define pisp_fe_dbg_irq(fmt, arg...)                            \
++#define pisp_fe_dbg_verbose(fmt, arg...)                        \
+       do {                                                    \
+-              if (cfe_debug_irq)                              \
++              if (cfe_debug_verbose)                          \
+                       dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \
+       } while (0)
+ #define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg)
+@@ -202,9 +202,9 @@ void pisp_fe_isr(struct pisp_fe_device *
+       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);
++      pisp_fe_dbg_verbose("%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++) {
+@@ -339,7 +339,7 @@ void pisp_fe_submit_job(struct pisp_fe_d
+        * 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);
++      pisp_fe_dbg_verbose("%s: status = 0x%x\n", __func__, status);
+       if (WARN_ON(status & FE_STATUS_QUEUED))
+               return;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch b/target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch
new file mode 100644 (file)
index 0000000..c86f7a3
--- /dev/null
@@ -0,0 +1,41 @@
+From 8240f1328ead0152f116b385b3169f8f010a7869 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 12:39:33 +0300
+Subject: [PATCH] media: rp1: Add back reg write debug prints
+
+Add back debug prints in csi2 and pisp_fe reg_write() functions, but use
+the 'irq' variants to avoid spamming in normal situation.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.c    | 1 +
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 2 ++
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -92,6 +92,7 @@ static inline u32 csi2_reg_read(struct c
+ static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
+ {
+       writel(val, csi2->base + offset);
++      csi2_dbg_verbose("csi2: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+ static inline void set_field(u32 *valp, u32 field, u32 mask)
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -132,12 +132,14 @@ static inline void pisp_fe_reg_write(str
+                                    u32 val)
+ {
+       writel(val, fe->base + offset);
++      pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+ static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset,
+                                            u32 val)
+ {
+       writel_relaxed(val, fe->base + offset);
++      pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+ static int pisp_regs_show(struct seq_file *s, void *data)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch
new file mode 100644 (file)
index 0000000..f44cb50
--- /dev/null
@@ -0,0 +1,23 @@
+From f2553458c0c1942731447aac3878f51aa1b326a7 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 10:19:47 +0300
+Subject: [PATCH] media: rp1: cfe: Add verbose debug module parameter
+
+Expose the verbose debug flag as a module parameter.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -50,6 +50,8 @@
+ #define CFE_VERSION   "1.0"
+ bool cfe_debug_verbose;
++module_param_named(verbose_debug, cfe_debug_verbose, bool, 0644);
++MODULE_PARM_DESC(verbose_debug, "verbose debugging messages");
+ #define cfe_dbg_verbose(fmt, arg...)                          \
+       do {                                                  \
diff --git a/target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch b/target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch
new file mode 100644 (file)
index 0000000..9d8da81
--- /dev/null
@@ -0,0 +1,245 @@
+From b19c2b5f88f141e58044e5d1012f867d46f74bf3 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 18:18:53 +0300
+Subject: [PATCH] media: rp1: csi2: Track CSI-2 errors
+
+Track the errors from the CSI-2 receiver: overflows and discards. These
+are recorded in a table which can be read by the userspace via debugfs.
+
+As tracking the errors may cause much more interrupt load, the tracking
+needs to be enabled with a module parameter.
+
+Note that the recording is not perfect: we only record the last
+discarded DT for each discard type, instead of recording all of them.
+This means that e.g. if the device is discarding two unmatched DTs, the
+debugfs file only shows the last one recorded. Recording all of them
+would need a more sophisticated recording system to avoid the need of a
+very large table, or dynamic allocation.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 123 ++++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h |  16 +++
+ 2 files changed, 139 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -16,6 +16,10 @@
+ #include "csi2.h"
+ #include "cfe.h"
++static bool csi2_track_errors;
++module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
++MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
++
+ #define csi2_dbg_verbose(fmt, arg...)                             \
+       do {                                                      \
+               if (cfe_debug_verbose)                            \
+@@ -32,9 +36,28 @@
+ #define CSI2_DISCARDS_INACTIVE        0x00c
+ #define CSI2_DISCARDS_UNMATCHED       0x010
+ #define CSI2_DISCARDS_LEN_LIMIT       0x014
++
++#define CSI2_DISCARDS_AMOUNT_SHIFT    0
++#define CSI2_DISCARDS_AMOUNT_MASK     GENMASK(23, 0)
++#define CSI2_DISCARDS_DT_SHIFT                24
++#define CSI2_DISCARDS_DT_MASK         GENMASK(29, 24)
++#define CSI2_DISCARDS_VC_SHIFT                30
++#define CSI2_DISCARDS_VC_MASK         GENMASK(31, 30)
++
+ #define CSI2_LLEV_PANICS      0x018
+ #define CSI2_ULEV_PANICS      0x01c
+ #define CSI2_IRQ_MASK         0x020
++#define CSI2_IRQ_MASK_IRQ_OVERFLOW            BIT(0)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW    BIT(1)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT        BIT(2)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED   BIT(3)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE    BIT(4)
++#define CSI2_IRQ_MASK_IRQ_ALL                                              \
++      (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
++       CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT |                          \
++       CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED |                             \
++       CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
++
+ #define CSI2_CTRL             0x024
+ #define CSI2_CH_CTRL(x)               ((x) * 0x40 + 0x28)
+ #define CSI2_CH_ADDR0(x)      ((x) * 0x40 + 0x2c)
+@@ -149,6 +172,92 @@ static int csi2_regs_show(struct seq_fil
+ DEFINE_SHOW_ATTRIBUTE(csi2_regs);
++static int csi2_errors_show(struct seq_file *s, void *data)
++{
++      struct csi2_device *csi2 = s->private;
++      unsigned long flags;
++      u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
++      u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
++      u32 overflows;
++
++      spin_lock_irqsave(&csi2->errors_lock, flags);
++
++      memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
++      memcpy(discards_dt_table, csi2->discards_dt_table,
++             sizeof(discards_dt_table));
++      overflows = csi2->overflows;
++
++      csi2->overflows = 0;
++      memset(csi2->discards_table, 0, sizeof(discards_table));
++      memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
++
++      spin_unlock_irqrestore(&csi2->errors_lock, flags);
++
++      seq_printf(s, "Overflows %u\n", overflows);
++      seq_puts(s, "Discards:\n");
++      seq_puts(s, "VC            OVLF        LEN  UNMATCHED   INACTIVE\n");
++
++      for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
++              seq_printf(s, "%u       %10u %10u %10u %10u\n", vc,
++                         discards_table[vc][DISCARDS_TABLE_OVERFLOW],
++                         discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
++                         discards_table[vc][DISCARDS_TABLE_UNMATCHED],
++                         discards_table[vc][DISCARDS_TABLE_INACTIVE]);
++      }
++
++      seq_printf(s, "Last DT %10u %10u %10u %10u\n",
++                 discards_dt_table[DISCARDS_TABLE_OVERFLOW],
++                 discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
++                 discards_dt_table[DISCARDS_TABLE_UNMATCHED],
++                 discards_dt_table[DISCARDS_TABLE_INACTIVE]);
++
++      return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(csi2_errors);
++
++static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
++{
++      spin_lock(&csi2->errors_lock);
++
++      if (status & IRQ_OVERFLOW)
++              csi2->overflows++;
++
++      for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
++              static const u32 discard_bits[] = {
++                      IRQ_DISCARD_OVERFLOW,
++                      IRQ_DISCARD_LEN_LIMIT,
++                      IRQ_DISCARD_UNMATCHED,
++                      IRQ_DISCARD_INACTIVE,
++              };
++              static const u8 discard_regs[] = {
++                      CSI2_DISCARDS_OVERFLOW,
++                      CSI2_DISCARDS_LEN_LIMIT,
++                      CSI2_DISCARDS_UNMATCHED,
++                      CSI2_DISCARDS_INACTIVE,
++              };
++              u32 amount;
++              u8 dt, vc;
++              u32 v;
++
++              if (!(status & discard_bits[i]))
++                      continue;
++
++              v = csi2_reg_read(csi2, discard_regs[i]);
++              csi2_reg_write(csi2, discard_regs[i], 0);
++
++              amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
++                       CSI2_DISCARDS_AMOUNT_SHIFT;
++              dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
++              vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
++
++              csi2->discards_table[vc][i] += amount;
++              csi2->discards_dt_table[i] = dt;
++      }
++
++      spin_unlock(&csi2->errors_lock);
++}
++
+ void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
+ {
+       unsigned int i;
+@@ -183,6 +292,9 @@ void csi2_isr(struct csi2_device *csi2,
+               eof[i] = !!(status & IRQ_FE_ACK(i));
+               lci[i] = !!(status & IRQ_LE_ACK(i));
+       }
++
++      if (csi2_track_errors)
++              csi2_isr_handle_errors(csi2, status);
+ }
+ void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+@@ -277,6 +389,9 @@ void csi2_stop_channel(struct csi2_devic
+ void csi2_open_rx(struct csi2_device *csi2)
+ {
++      csi2_reg_write(csi2, CSI2_IRQ_MASK,
++                     csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
++
+       dphy_start(&csi2->dphy);
+       csi2_reg_write(csi2, CSI2_CTRL,
+@@ -286,6 +401,8 @@ void csi2_open_rx(struct csi2_device *cs
+ void csi2_close_rx(struct csi2_device *csi2)
+ {
+       dphy_stop(&csi2->dphy);
++
++      csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
+ }
+ static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
+@@ -398,11 +515,17 @@ int csi2_init(struct csi2_device *csi2,
+ {
+       unsigned int i, ret;
++      spin_lock_init(&csi2->errors_lock);
++
+       csi2->dphy.dev = csi2->v4l2_dev->dev;
+       dphy_probe(&csi2->dphy);
+       debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);
++      if (csi2_track_errors)
++              debugfs_create_file("csi2_errors", 0444, debugfs, csi2,
++                                  &csi2_errors_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;
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -17,6 +17,8 @@
+ #define CSI2_NUM_CHANNELS 4
++#define DISCARDS_TABLE_NUM_VCS 4
++
+ enum csi2_mode {
+       CSI2_MODE_NORMAL,
+       CSI2_MODE_REMAP,
+@@ -37,6 +39,14 @@ struct csi2_cfg {
+       u32 buffer_size;
+ };
++enum discards_table_index {
++      DISCARDS_TABLE_OVERFLOW = 0,
++      DISCARDS_TABLE_LENGTH_LIMIT,
++      DISCARDS_TABLE_UNMATCHED,
++      DISCARDS_TABLE_INACTIVE,
++      DISCARDS_TABLE_NUM_ENTRIES,
++};
++
+ struct csi2_device {
+       /* Parent V4l2 device */
+       struct v4l2_device *v4l2_dev;
+@@ -53,6 +63,12 @@ struct csi2_device {
+       struct media_pad pad[CSI2_NUM_CHANNELS * 2];
+       struct v4l2_subdev sd;
++
++      /* lock for csi2 errors counters */
++      spinlock_t errors_lock;
++      u32 overflows;
++      u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
++      u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+ };
+ void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch b/target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch
new file mode 100644 (file)
index 0000000..b53a5b6
--- /dev/null
@@ -0,0 +1,31 @@
+From d6bd676b49cf10046665efde820b0cc00dc43994 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 17:04:07 +0300
+Subject: [PATCH] media: rp1: cfe: Drop unused field
+
+Drop 'sensor_embedded_data' field, as it is unused.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -292,7 +292,6 @@ struct cfe_device {
+       struct csi2_device csi2;
+       struct pisp_fe_device fe;
+-      bool sensor_embedded_data;
+       int fe_csi2_channel;
+       unsigned int sequence;
+@@ -1821,8 +1820,6 @@ static int cfe_probe_complete(struct cfe
+       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) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch b/target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch
new file mode 100644 (file)
index 0000000..126ee2c
--- /dev/null
@@ -0,0 +1,30 @@
+From c56b53d62116b4672962124afab02f3beada4244 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 12:57:23 +0300
+Subject: [PATCH] media: rp1: csi2: Set values for enum csi2_mode
+
+Set hardcoded values for enum csi2_mode, as the values will be
+programmed to HW registers.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -20,10 +20,10 @@
+ #define DISCARDS_TABLE_NUM_VCS 4
+ enum csi2_mode {
+-      CSI2_MODE_NORMAL,
+-      CSI2_MODE_REMAP,
+-      CSI2_MODE_COMPRESSED,
+-      CSI2_MODE_FE_STREAMING
++      CSI2_MODE_NORMAL = 0,
++      CSI2_MODE_REMAP = 1,
++      CSI2_MODE_COMPRESSED = 2,
++      CSI2_MODE_FE_STREAMING = 3,
+ };
+ enum csi2_compression_mode {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch b/target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch
new file mode 100644 (file)
index 0000000..e4ea9e1
--- /dev/null
@@ -0,0 +1,27 @@
+From 5edacf33de9e0349dd5a02ad684bfceae1d5565f Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 13:29:15 +0300
+Subject: [PATCH] media: rp1: fe: Fix default mbus code
+
+When pisp_fe_pad_set_fmt() is given an mbus code that CFE does not
+support, it currently defaults to MEDIA_BUS_FMT_SBGGR10_1X10. This is
+not correct, as FE does not support SBGGR10.
+
+Set the default to MEDIA_BUS_FMT_SRGGB16_1X16 instead.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -437,7 +437,7 @@ static int pisp_fe_pad_set_fmt(struct v4
+       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);
++                      cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
+               format->format.code = cfe_fmt->code;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch b/target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch
new file mode 100644 (file)
index 0000000..2ac5e0e
--- /dev/null
@@ -0,0 +1,25 @@
+From 47fd9528bc1d93dd07ce9baa19c736966b536bd9 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Mon, 2 Oct 2023 14:38:07 +0300
+Subject: [PATCH] media: rp1: cfe: Fix default meta format's field
+
+Set default meta format's field to V4L2_FIELD_NONE, instead of zeroing
+it which indicates V4L2_FIELD_ANY. Metadata doesn't have fields, so NONE
+makes sense, and furthermore the default v4l2 link validation will check
+for matching fields, or that the sink field is NONE.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -110,6 +110,7 @@ const struct v4l2_mbus_framefmt cfe_defa
+       .width = 8192,
+       .height = 1,
+       .code = MEDIA_BUS_FMT_SENSOR_DATA,
++      .field = V4L2_FIELD_NONE,
+ };
+ enum node_ids {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch b/target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch
new file mode 100644 (file)
index 0000000..de469c3
--- /dev/null
@@ -0,0 +1,31 @@
+From 3e2203b265ddd8630fea0fbb69b3a2ec1496f773 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 4 Oct 2023 09:39:59 +0100
+Subject: [PATCH] media: rp1: cfe: Fail streaming if FE_CONFIG node is not
+ enabled
+
+When the FE is enabled, ensure that the FE_CONFIG node is enabled.
+Otherwise fail cfe_start_streaming() entirely.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -997,6 +997,14 @@ static int cfe_start_streaming(struct vb
+               goto err_streaming;
+       }
++      /* When using the Frontend, we must enable the FE_CONFIG node. */
++      if (is_fe_enabled(cfe) &&
++          !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) {
++              cfe_err("FE enabled, but FE_CONFIG node is not\n");
++              ret = -EINVAL;
++              goto err_streaming;
++      }
++
+       ret = media_pipeline_start(&node->pad, &cfe->pipe);
+       if (ret < 0) {
+               cfe_err("Failed to start media pipeline: %d\n", ret);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch b/target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch
new file mode 100644 (file)
index 0000000..37e1a86
--- /dev/null
@@ -0,0 +1,51 @@
+From 52811174f534824f5e5bcdb681f0c508aa335244 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 4 Oct 2023 11:09:10 +0100
+Subject: [PATCH] media: i2c: Move Kconfig entry for IMX477 to the camera
+ sensor section
+
+It was accidentally placed in the audio decoder section.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/Kconfig | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -270,6 +270,17 @@ config VIDEO_IMX412
+         To compile this driver as a module, choose M here: the
+         module will be called imx412.
++config VIDEO_IMX477
++      tristate "Sony IMX477 sensor support"
++      depends on I2C && VIDEO_DEV
++      select VIDEO_V4L2_SUBDEV_API
++      help
++        This is a Video4Linux2 sensor driver for the Sony
++        IMX477 camera. Also supports the Sony IMX378.
++
++        To compile this driver as a module, choose M here: the
++        module will be called imx477.
++
+ config VIDEO_IMX519
+       tristate "Arducam IMX519 sensor support"
+       depends on I2C && VIDEO_DEV
+@@ -1106,17 +1117,6 @@ config VIDEO_UDA1342
+         To compile this driver as a module, choose M here: the
+         module will be called uda1342.
+-config VIDEO_IMX477
+-      tristate "Sony IMX477 sensor support"
+-      depends on I2C && VIDEO_DEV
+-      select VIDEO_V4L2_SUBDEV_API
+-      help
+-        This is a Video4Linux2 sensor driver for the Sony
+-        IMX477 camera. Also supports the Sony IMX378.
+-
+-        To compile this driver as a module, choose M here: the
+-        module will be called imx477.
+-
+ config VIDEO_VP27SMPX
+       tristate "Panasonic VP27's internal MPX"
+       depends on VIDEO_DEV && I2C
diff --git a/target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch b/target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch
new file mode 100644 (file)
index 0000000..8de6c37
--- /dev/null
@@ -0,0 +1,109 @@
+From 3aa1f2477545ea6298bc6f2d7ffae68f090af9b8 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 28 Sep 2023 18:27:09 +0100
+Subject: [PATCH] drm: Look for an alias for the displays to use as the DRM
+ device name
+
+Allow DT aliases of eg DSI2 to force make DRM allocate the
+display with the requested name.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 60 ++++++++++++++++++++++++++++++---
+ 1 file changed, 56 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -81,6 +81,7 @@ struct drm_conn_prop_enum_list {
+       int type;
+       const char *name;
+       struct ida ida;
++      int first_dyn_num;
+ };
+ /*
+@@ -110,12 +111,41 @@ static struct drm_conn_prop_enum_list dr
+       { DRM_MODE_CONNECTOR_USB, "USB" },
+ };
++#define MAX_DT_NODE_NAME_LEN  20
++#define DT_DRM_NODE_PREFIX    "drm_"
++
++static void drm_connector_get_of_name(int type, char *node_name, int length)
++{
++      int i = 0;
++
++      strcpy(node_name, DT_DRM_NODE_PREFIX);
++
++      do {
++              node_name[i + strlen(DT_DRM_NODE_PREFIX)] =
++                              tolower(drm_connector_enum_list[type].name[i]);
++
++      } while (drm_connector_enum_list[type].name[i++] &&
++               i < length);
++
++      node_name[length - 1] = '\0';
++}
++
+ void drm_connector_ida_init(void)
+ {
+-      int i;
++      int i, id;
++      char node_name[MAX_DT_NODE_NAME_LEN];
+-      for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
++      for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) {
+               ida_init(&drm_connector_enum_list[i].ida);
++
++              drm_connector_get_of_name(i, node_name, MAX_DT_NODE_NAME_LEN);
++
++              id = of_alias_get_highest_id(node_name);
++              if (id > 0)
++                      drm_connector_enum_list[i].first_dyn_num = id + 1;
++              else
++                      drm_connector_enum_list[i].first_dyn_num = 1;
++      }
+ }
+ void drm_connector_ida_destroy(void)
+@@ -222,7 +252,9 @@ static int __drm_connector_init(struct d
+                               struct i2c_adapter *ddc)
+ {
+       struct drm_mode_config *config = &dev->mode_config;
++      char node_name[MAX_DT_NODE_NAME_LEN];
+       int ret;
++      int id;
+       struct ida *connector_ida =
+               &drm_connector_enum_list[connector_type].ida;
+@@ -252,8 +284,28 @@ static int __drm_connector_init(struct d
+       ret = 0;
+       connector->connector_type = connector_type;
+-      connector->connector_type_id =
+-              ida_alloc_min(connector_ida, 1, GFP_KERNEL);
++      connector->connector_type_id = 0;
++
++      drm_connector_get_of_name(connector_type, node_name, MAX_DT_NODE_NAME_LEN);
++      id = of_alias_get_id(dev->dev->of_node, node_name);
++      if (id > 0) {
++              /* Try and allocate the requested ID
++               * Valid range is 1 to 31, hence ignoring 0 as an error
++               */
++              int type_id = ida_alloc_range(connector_ida, id, id, GFP_KERNEL);
++
++              if (type_id > 0)
++                      connector->connector_type_id = type_id;
++              else
++                      drm_err(dev, "Failed to acquire type ID %d for interface type %s, ret %d\n",
++                              id, drm_connector_enum_list[connector_type].name,
++                              type_id);
++      }
++      if (!connector->connector_type_id)
++              connector->connector_type_id =
++                              ida_alloc_min(connector_ida,
++                                            drm_connector_enum_list[connector_type].first_dyn_num,
++                                            GFP_KERNEL);
+       if (connector->connector_type_id < 0) {
+               ret = connector->connector_type_id;
+               goto out_put_id;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch b/target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch
new file mode 100644 (file)
index 0000000..6d79cb6
--- /dev/null
@@ -0,0 +1,24 @@
+From 7ec42740a45b21bca859cde5b7cbe2f09ef3d586 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 28 Sep 2023 18:40:36 +0100
+Subject: [PATCH] dt: Add DSI1 and DSI2 aliases to 2712
+
+In order to keep the DRM names consistent as DSI-1 and DSI-2, add
+aliases to the Pi5 DT.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -785,6 +785,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               gpio4 = &pinctrl_aon;
+               usb0 = &rp1_usb0;
+               usb1 = &rp1_usb1;
++              drm_dsi1 = &dsi0;
++              drm_dsi2 = &dsi1;
+       };
+       __overrides__ {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch b/target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch
new file mode 100644 (file)
index 0000000..a4c8aea
--- /dev/null
@@ -0,0 +1,64 @@
+From e6e0631fdeb0cd7d4c50e629b4b298e0b76e885b Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 4 Oct 2023 16:02:39 +0100
+Subject: [PATCH] vc4/drm: Remove the clear of SCALER_DISPBKGND_FILL
+
+Since "drm/vc4: hvs: Support BCM2712 HVS" booting Pi4
+with dual 4kp30 displays connected fails with:
+vc4-drm gpu: [drm] *ERROR* [CRTC:107:pixelvalve-4] flip_done timed out
+
+It has been tracked down to the referenced commit adding a
+path to clear the SCALER_DISPBKGND_FILL when not required.
+
+Dual 4kp30 works with a core clock of 297MHz when background fill
+is enabled, but requires a higher value with it disabled.
+320MHz still fails, while 330MHz seems okay.
+
+Lets always enable background fill for Pi0-4.
+
+Fixes: e84da235223d ("drm/vc4: hvs: Support BCM2712 HVS")
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 20 +++++++++-----------
+ 1 file changed, 9 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1349,27 +1349,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
+       WARN_ON(!vc4_state->mm);
+       WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm->mm_node.size);
+-      if (enable_bg_fill) {
++      if (vc4->gen >= VC4_GEN_6) {
+               /* This sets a black background color fill, as is the case
+                * with other DRM drivers.
+                */
+-              if (vc4->gen >= VC4_GEN_6)
++              if (enable_bg_fill)
+                       HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+                                 HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
+                                 SCALER6_DISPX_CTRL1_BGENB);
+               else
+-                      HVS_WRITE(SCALER_DISPBKGNDX(channel),
+-                                HVS_READ(SCALER_DISPBKGNDX(channel)) |
+-                                SCALER_DISPBKGND_FILL);
+-      } else {
+-              if (vc4->gen >= VC4_GEN_6)
+                       HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+                                 HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
+                                 ~SCALER6_DISPX_CTRL1_BGENB);
+-              else
+-                      HVS_WRITE(SCALER_DISPBKGNDX(channel),
+-                                HVS_READ(SCALER_DISPBKGNDX(channel)) &
+-                                ~SCALER_DISPBKGND_FILL);
++      } else {
++              /* we can actually run with a lower core clock when background
++               * fill is enabled on VC4_GEN_5 so leave it enabled always.
++               */
++              HVS_WRITE(SCALER_DISPBKGNDX(channel),
++                        HVS_READ(SCALER_DISPBKGNDX(channel)) |
++                        SCALER_DISPBKGND_FILL);
+       }
+       /* Only update DISPLIST if the CRTC was already running and is not
diff --git a/target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch b/target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch
new file mode 100644 (file)
index 0000000..9bf9e6d
--- /dev/null
@@ -0,0 +1,117 @@
+From 450d617786926b27c25e930241efbd2e37d66bb8 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 6 Oct 2023 16:30:35 +0100
+Subject: [PATCH] gpio: brcmstb: Use dynamic GPIO base numbers
+
+Forcing a gpiochip to have a fixed base number now leads to a warning
+message. Remove the need to do so by calculating hwirq numbers based
+on bank numbers.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs")
+---
+ drivers/gpio/gpio-brcmstb.c | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpio/gpio-brcmstb.c
++++ b/drivers/gpio/gpio-brcmstb.c
+@@ -50,7 +50,6 @@ struct brcmstb_gpio_priv {
+       struct irq_domain *irq_domain;
+       struct irq_chip irq_chip;
+       int parent_irq;
+-      int gpio_base;
+       int num_gpios;
+       int parent_wake_irq;
+ };
+@@ -92,7 +91,7 @@ brcmstb_gpio_get_active_irqs(struct brcm
+ static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq,
+                                       struct brcmstb_gpio_bank *bank)
+ {
+-      return hwirq - (bank->gc.base - bank->parent_priv->gpio_base);
++      return hwirq - bank->id * 32;
+ }
+ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
+@@ -117,8 +116,9 @@ static void brcmstb_gpio_set_imask(struc
+ static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+ {
+       struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
++      struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
+       /* gc_offset is relative to this gpio_chip; want real offset */
+-      int hwirq = offset + (gc->base - priv->gpio_base);
++      int hwirq = offset + bank->id * 32;
+       if (hwirq >= priv->num_gpios)
+               return -ENXIO;
+@@ -263,7 +263,7 @@ static void brcmstb_gpio_irq_bank_handle
+ {
+       struct brcmstb_gpio_priv *priv = bank->parent_priv;
+       struct irq_domain *domain = priv->irq_domain;
+-      int hwbase = bank->gc.base - priv->gpio_base;
++      int hwbase = bank->id * 32;
+       unsigned long status;
+       while ((status = brcmstb_gpio_get_active_irqs(bank))) {
+@@ -414,7 +414,7 @@ static int brcmstb_gpio_of_xlate(struct
+       if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+               return -EINVAL;
+-      offset = gpiospec->args[0] - (gc->base - priv->gpio_base);
++      offset = gpiospec->args[0] - bank->id * 32;
+       if (offset >= gc->ngpio || offset < 0)
+               return -EINVAL;
+@@ -598,8 +598,8 @@ static int brcmstb_gpio_probe(struct pla
+       const __be32 *p;
+       u32 bank_width;
+       int num_banks = 0;
++      int num_gpios = 0;
+       int err;
+-      static int gpio_base;
+       unsigned long flags = 0;
+       bool need_wakeup_event = false;
+@@ -614,7 +614,6 @@ static int brcmstb_gpio_probe(struct pla
+       if (IS_ERR(reg_base))
+               return PTR_ERR(reg_base);
+-      priv->gpio_base = gpio_base;
+       priv->reg_base = reg_base;
+       priv->pdev = pdev;
+@@ -656,7 +655,7 @@ static int brcmstb_gpio_probe(struct pla
+                       dev_dbg(dev, "Width 0 found: Empty bank @ %d\n",
+                               num_banks);
+                       num_banks++;
+-                      gpio_base += MAX_GPIO_PER_BANK;
++                      num_gpios += MAX_GPIO_PER_BANK;
+                       continue;
+               }
+@@ -698,7 +697,7 @@ static int brcmstb_gpio_probe(struct pla
+                       err = -ENOMEM;
+                       goto fail;
+               }
+-              gc->base = gpio_base;
++              gc->base = -1;
+               gc->of_gpio_n_cells = 2;
+               gc->of_xlate = brcmstb_gpio_of_xlate;
+               /* not all ngpio lines are valid, will use bank width later */
+@@ -722,7 +721,7 @@ static int brcmstb_gpio_probe(struct pla
+                                       bank->id);
+                       goto fail;
+               }
+-              gpio_base += gc->ngpio;
++              num_gpios += gc->ngpio;
+               dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id,
+                       gc->base, gc->ngpio, bank->width);
+@@ -733,7 +732,7 @@ static int brcmstb_gpio_probe(struct pla
+               num_banks++;
+       }
+-      priv->num_gpios = gpio_base - priv->gpio_base;
++      priv->num_gpios = num_gpios;
+       if (priv->parent_irq > 0) {
+               err = brcmstb_gpio_irq_setup(pdev, priv);
+               if (err)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch b/target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch
new file mode 100644 (file)
index 0000000..a6d199a
--- /dev/null
@@ -0,0 +1,57 @@
+From 1770efc97981dbf756a18f5eb7615c4bafab665b Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Mon, 2 Oct 2023 15:12:52 +0100
+Subject: [PATCH] media/rpivid: Allow use of iommu in rpivid
+
+In order to use iommu on hevc set dma mask_and_coherent in probe.
+I am assured dma_set_mask_and_coherent is benign on Pi4 (which has
+no iommu) and it seems to be so in practice.
+Also adds a bit of debug to make internal buffer allocation failure
+easier to spot in future
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/staging/media/rpivid/rpivid.c      |  7 +++++++
+ drivers/staging/media/rpivid/rpivid_h265.c | 12 ++++++++++--
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/staging/media/rpivid/rpivid.c
++++ b/drivers/staging/media/rpivid/rpivid.c
+@@ -360,6 +360,13 @@ static int rpivid_probe(struct platform_
+       snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
+       video_set_drvdata(vfd, dev);
++      ret = dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(36));
++      if (ret) {
++              v4l2_err(&dev->v4l2_dev,
++                       "Failed dma_set_mask_and_coherent\n");
++              goto err_v4l2;
++      }
++
+       dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
+       if (IS_ERR(dev->m2m_dev)) {
+               v4l2_err(&dev->v4l2_dev,
+--- a/drivers/staging/media/rpivid/rpivid_h265.c
++++ b/drivers/staging/media/rpivid/rpivid_h265.c
+@@ -2495,11 +2495,19 @@ static int rpivid_h265_start(struct rpiv
+       for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i) {
+               // Don't actually need a kernel mapping here
+               if (gptr_alloc(dev, ctx->pu_bufs + i, pu_alloc,
+-                             DMA_ATTR_NO_KERNEL_MAPPING))
++                             DMA_ATTR_NO_KERNEL_MAPPING)) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Failed to alloc %#zx PU%d buffer\n",
++                               pu_alloc, i);
+                       goto fail;
++              }
+               if (gptr_alloc(dev, ctx->coeff_bufs + i, coeff_alloc,
+-                             DMA_ATTR_NO_KERNEL_MAPPING))
++                             DMA_ATTR_NO_KERNEL_MAPPING)) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Failed to alloc %#zx Coeff%d buffer\n",
++                               pu_alloc, i);
+                       goto fail;
++              }
+       }
+       aux_q_init(ctx);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch b/target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch
new file mode 100644 (file)
index 0000000..9f7c867
--- /dev/null
@@ -0,0 +1,22 @@
+From 10ff8ba24c68f704ecf5a58878617dfe149a14c6 Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Mon, 2 Oct 2023 15:15:13 +0100
+Subject: [PATCH] dts/bcm2712: Add iommu to rpivid
+
+Add iommu to rpivid so it can cope with scatter/gather
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -1158,6 +1158,7 @@
+                       clocks = <&firmware_clocks 11>;
+                       clock-names = "hevc";
+                       status = "disabled";
++                      iommus = <&iommu2>;
+               };
+               sdio1: mmc@fff000 {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch b/target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch
new file mode 100644 (file)
index 0000000..fabc96e
--- /dev/null
@@ -0,0 +1,117 @@
+From b5c3cc7fd9fca73352310e61092fb445b56a362a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 10 Oct 2023 12:41:15 +0100
+Subject: [PATCH] drivers: media: rp1_cfe: Remove PISP specific MBUS formats
+
+Remove the MEDIA_BUS_FMT_PISP* format codcs entirely. For the image
+pad formats, use the 16-bit Bayer format mbus codes instead. For the
+config and stats pad formats, use MEDIA_BUS_FMT_FIXED.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe_fmts.h  | 10 ++++++----
+ .../media/platform/raspberrypi/rp1_cfe/pisp_fe.c   | 11 ++++-------
+ include/uapi/linux/media-bus-format.h              | 14 --------------
+ 3 files changed, 10 insertions(+), 25 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -215,25 +215,25 @@ static const struct cfe_fmt formats[] =
+       /* PiSP Compressed Mode 1 */
+       {
+               .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
+-              .code = MEDIA_BUS_FMT_PISP_COMP1_RGGB,
++              .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+               .depth = 8,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
+-              .code = MEDIA_BUS_FMT_PISP_COMP1_BGGR,
++              .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+               .depth = 8,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
+-              .code = MEDIA_BUS_FMT_PISP_COMP1_GBRG,
++              .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+               .depth = 8,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
+-              .code = MEDIA_BUS_FMT_PISP_COMP1_GRBG,
++              .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+               .depth = 8,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
+       },
+@@ -283,10 +283,12 @@ static const struct cfe_fmt formats[] =
+       /* Frontend formats */
+       {
+               .fourcc = V4L2_META_FMT_RPI_FE_CFG,
++              .code = MEDIA_BUS_FMT_FIXED,
+               .flags = CFE_FORMAT_FLAG_META_OUT,
+       },
+       {
+               .fourcc = V4L2_META_FMT_RPI_FE_STATS,
++              .code = MEDIA_BUS_FMT_FIXED,
+               .flags = CFE_FORMAT_FLAG_META_CAP,
+       },
+ };
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -404,7 +404,7 @@ static int pisp_fe_init_cfg(struct v4l2_
+       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->code = MEDIA_BUS_FMT_FIXED;
+       fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
+       *fmt = cfe_default_format;
+@@ -416,7 +416,7 @@ static int pisp_fe_init_cfg(struct v4l2_
+       fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD);
+       *fmt = cfe_default_meta_format;
+-      fmt->code = MEDIA_BUS_FMT_PISP_FE_STATS;
++      fmt->code = MEDIA_BUS_FMT_FIXED;
+       return 0;
+ }
+@@ -443,12 +443,9 @@ static int pisp_fe_pad_set_fmt(struct v4
+               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;
++      case FE_CONFIG_PAD:
++              format->format.code = MEDIA_BUS_FMT_FIXED;
+               break;
+       }
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -175,18 +175,4 @@
+ /* 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 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch b/target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch
new file mode 100644 (file)
index 0000000..313ca37
--- /dev/null
@@ -0,0 +1,53 @@
+From 9f4002165439d02a63760e68948246e3af764318 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Oct 2023 15:05:38 +0100
+Subject: [PATCH] vc04_services: bcm2835-codec: Correct alignment requirements
+ for YUYV
+
+The firmware wants the YUYV format stride alignment to be to a multiple
+of 32pixels / 64 bytes. The kernel driver was configuring it to a multiple
+of 16 pixels / 32 bytes, which then failed when it tried starting to
+stream.
+
+Correct the alignment requirements.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c      | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -206,28 +206,28 @@ static const struct bcm2835_codec_fmt su
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_YUYV,
+               .depth                  = 16,
+-              .bytesperline_align     = { 32, 32, 32, 32, 32 },
++              .bytesperline_align     = { 64, 64, 64, 64, 64 },
+               .flags                  = 0,
+               .mmal_fmt               = MMAL_ENCODING_YUYV,
+               .size_multiplier_x2     = 2,
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_UYVY,
+               .depth                  = 16,
+-              .bytesperline_align     = { 32, 32, 32, 32, 32 },
++              .bytesperline_align     = { 64, 64, 64, 64, 64 },
+               .flags                  = 0,
+               .mmal_fmt               = MMAL_ENCODING_UYVY,
+               .size_multiplier_x2     = 2,
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_YVYU,
+               .depth                  = 16,
+-              .bytesperline_align     = { 32, 32, 32, 32, 32 },
++              .bytesperline_align     = { 64, 64, 64, 64, 64 },
+               .flags                  = 0,
+               .mmal_fmt               = MMAL_ENCODING_YVYU,
+               .size_multiplier_x2     = 2,
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_VYUY,
+               .depth                  = 16,
+-              .bytesperline_align     = { 32, 32, 32, 32, 32 },
++              .bytesperline_align     = { 64, 64, 64, 64, 64 },
+               .flags                  = 0,
+               .mmal_fmt               = MMAL_ENCODING_VYUY,
+               .size_multiplier_x2     = 2,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch b/target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch
new file mode 100644 (file)
index 0000000..fe956c2
--- /dev/null
@@ -0,0 +1,96 @@
+From dd802fc03b1c71b4297e87068077bfcf9810300a Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 11 Oct 2023 15:14:59 +0100
+Subject: [PATCH] input: touchscreen: edt-ft5x06: Suppress bogus data on
+ startup
+
+When polled without the use of IRQ, FT5x06 registers may return
+undefined initial data, causing unwanted touches or event spamming.
+A simple way to filter this out is to suppress touches until the
+TD_STATUS register changes for the first time.
+
+Increase the delay before first polling to 300ms, to avoid
+transient I2C read flakiness that seems to occur after reset.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 31 +++++++++++++++++++++++---
+ 1 file changed, 28 insertions(+), 3 deletions(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -75,6 +75,8 @@
+ #define EDT_DEFAULT_NUM_X             1024
+ #define EDT_DEFAULT_NUM_Y             1024
++#define RESET_DELAY_MS                        300     /* reset deassert to I2C */
++#define FIRST_POLL_DELAY_MS           300     /* in addition to the above */
+ #define POLL_INTERVAL_MS              17      /* 17ms = 60fps */
+ enum edt_pmode {
+@@ -134,6 +136,7 @@ struct edt_ft5x06_ts_data {
+       char name[EDT_NAME_LEN];
+       char fw_version[EDT_NAME_LEN];
++      int init_td_status;
+       struct edt_reg_addr reg_addr;
+       enum edt_ver version;
+@@ -268,12 +271,33 @@ static irqreturn_t edt_ft5x06_ts_isr(int
+                * points.
+                */
+               num_points = min(rdbuf[2] & 0xf, tsdata->max_support_points);
++
++              /* When polling FT5x06 without IRQ: initial register contents
++               * could be stale or undefined; discard all readings until
++               * TD_STATUS changes for the first time (or num_points is 0).
++               */
++              if (tsdata->init_td_status) {
++                      if (tsdata->init_td_status < 0)
++                              tsdata->init_td_status = rdbuf[2];
++
++                      if (num_points && rdbuf[2] == tsdata->init_td_status)
++                              goto out;
++
++                      tsdata->init_td_status = 0;
++              }
++
+               if (num_points) {
+                       datalen = tplen * num_points + crclen;
+                       cmd = offset;
+                       error = edt_ft5x06_ts_readwrite(tsdata->client,
+                                                       sizeof(cmd), &cmd,
+                                                       datalen, &rdbuf[offset]);
++                      if (error) {
++                              dev_err_ratelimited(dev,
++                                                  "Unable to fetch data, error: %d\n",
++                                                  error);
++                              goto out;
++                      }
+               }
+       }
+@@ -1294,7 +1318,7 @@ static int edt_ft5x06_ts_probe(struct i2
+       if (tsdata->reset_gpio) {
+               usleep_range(5000, 6000);
+               gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+-              msleep(300);
++              msleep(RESET_DELAY_MS);
+       }
+       input = devm_input_allocate_device(&client->dev);
+@@ -1384,11 +1408,12 @@ static int edt_ft5x06_ts_probe(struct i2
+                       return error;
+               }
+       } else {
++              tsdata->init_td_status = -1; /* filter bogus initial data */
+               INIT_WORK(&tsdata->work_i2c_poll,
+                         edt_ft5x06_ts_work_i2c_poll);
+               timer_setup(&tsdata->timer, edt_ft5x06_ts_irq_poll_timer, 0);
+-              tsdata->timer.expires = jiffies +
+-                                      msecs_to_jiffies(POLL_INTERVAL_MS);
++              tsdata->timer.expires =
++                      jiffies + msecs_to_jiffies(FIRST_POLL_DELAY_MS);
+               add_timer(&tsdata->timer);
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch b/target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch
new file mode 100644 (file)
index 0000000..8cb35e1
--- /dev/null
@@ -0,0 +1,97 @@
+From 27f0e0e195568c06f21ce380f0736bdd219baf3c Mon Sep 17 00:00:00 2001
+From: Janis Streib <janis+github@dogcraft.de>
+Date: Sun, 15 Oct 2023 21:08:40 +0200
+Subject: [PATCH] overlays: mcp23017: allow specification of the i2c bus
+
+Analogous to i2c-rtc-overlay.dts
+
+See: https://github.com/raspberrypi/linux/pull/5650
+---
+ arch/arm/boot/dts/overlays/README             | 10 ++++++
+ .../boot/dts/overlays/mcp23017-overlay.dts    | 36 +++++++++++++++++--
+ 2 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2810,6 +2810,16 @@ Params: gpiopin                 Gpio pin
+         mcp23008                Configure an MCP23008 instead.
+         noints                  Disable the interrupt GPIO line.
++        i2c0                    Choose the I2C0 bus on GPIOs 0&1
++        i2c_csi_dsi             Choose the I2C0 bus on GPIOs 44&45
++        i2c3                    Choose the I2C3 bus (configure with the i2c3
++                                overlay - BCM2711 only)
++        i2c4                    Choose the I2C4 bus (configure with the i2c4
++                                overlay - BCM2711 only)
++        i2c5                    Choose the I2C5 bus (configure with the i2c5
++                                overlay - BCM2711 only)
++        i2c6                    Choose the I2C6 bus (configure with the i2c6
++                                overlay - BCM2711 only)
+ Name:   mcp23s17
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -7,7 +7,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target = <&i2c1>;
++              target = <&i2cbus>;
+               __overlay__ {
+                       status = "okay";
+               };
+@@ -24,7 +24,7 @@
+       };
+       fragment@2 {
+-              target = <&i2c1>;
++              target = <&i2cbus>;
+               __overlay__ {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+@@ -58,12 +58,44 @@
+               };
+       };
++      frag100: fragment@100 {
++              target = <&i2c1>;
++              i2cbus: __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      fragment@101 { 
++              target = <&i2c0if>; 
++              __dormant__ { 
++                      status = "okay"; 
++              }; 
++      };
++
++      fragment@102 { 
++              target = <&i2c0mux>; 
++              __dormant__ { 
++                      status = "okay"; 
++              }; 
++      };
++
+       __overrides__ {
+               gpiopin = <&mcp23017_pins>,"brcm,pins:0",
+                               <&mcp23017_irq>,"interrupts:0";
+               addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0";
+               mcp23008 = <0>,"=3";
+               noints = <0>,"!1!4";
++              i2c0 = <&frag100>, "target:0=",<&i2c0>;
++              i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
++                            <0>,"+101+102";
++              i2c3 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c3";
++              i2c4 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c4";
++              i2c5 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c5";
++              i2c6 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c6";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch b/target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch
new file mode 100644 (file)
index 0000000..e9422f1
--- /dev/null
@@ -0,0 +1,46 @@
+From 0339c8c61ca6b54c529f699e7bafd65cda9f3733 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 16 Oct 2023 09:06:25 +0100
+Subject: [PATCH] dts: bcm2712: Set default I2C baudrates to 100kHz
+
+The RP1 I2C interfaces were being left with their default clock rates,
+apparently 400kHz.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/bcm2712-rpi.dtsi    | 2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -250,11 +250,13 @@ i2c0mux: &rp1_gpio {};
+ i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
+       pinctrl-0 = <&rp1_i2c6_38_39>;
+       pinctrl-names = "default";
++      clock-frequency = <100000>;
+ };
+ i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only
+       pinctrl-0 = <&rp1_i2c4_40_41>;
+       pinctrl-names = "default";
++      clock-frequency = <100000>;
+ };
+ i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
+--- a/arch/arm/boot/dts/bcm2712-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2712-rpi.dtsi
+@@ -204,11 +204,13 @@ 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";
++      clock-frequency = <100000>;
+ };
+ i2c_arm: &i2c1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&rp1_i2c1_2_3>;
++      clock-frequency = <100000>;
+ };
+ &i2c2 {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch b/target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch
new file mode 100644 (file)
index 0000000..23d4287
--- /dev/null
@@ -0,0 +1,345 @@
+From 2a47ccf97c6a91bc56f8cfb387d47f59cc347dd5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Sat, 14 Oct 2023 14:57:49 +0100
+Subject: [PATCH] vc_mem: Add the DMA memcpy support from bcm2708_fb
+
+bcm2708_fb is disabled by the vc4-kms-v3d overlay, which means that the
+DMA memcpy support it provides is not available to allow vclog to read
+the VC logs from the top 16MB on Pi 2 and Pi 3. Add the code to the
+vc_mem driver, which will still be enabled.
+
+It ought to be possible to do a proper DMA_MEM_TO_MEM copy via the
+generic DMA customer API, but that can be a later step.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/broadcom/vc_mem.c | 259 +++++++++++++++++++++++++++++++++
+ 1 file changed, 259 insertions(+)
+
+--- a/drivers/char/broadcom/vc_mem.c
++++ b/drivers/char/broadcom/vc_mem.c
+@@ -23,9 +23,21 @@
+ #include <linux/uaccess.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/broadcom/vc_mem.h>
++#include <linux/compat.h>
++#include <linux/platform_data/dma-bcm2708.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
+ #define DRIVER_NAME  "vc-mem"
++/* N.B. These use a different magic value for compatibility with bmc7208_fb */
++#define VC_MEM_IOC_DMACOPY   _IOW('z', 0x22, struct vc_mem_dmacopy)
++#define VC_MEM_IOC_DMACOPY32 _IOW('z', 0x22, struct vc_mem_dmacopy32)
++
++/* address with no aliases */
++#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
++/* cache coherent but non-allocating in L1 and L2 */
++#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
++
+ /* Device (/dev) related variables */
+ static dev_t vc_mem_devnum;
+ static struct class *vc_mem_class;
+@@ -36,6 +48,20 @@ static int vc_mem_inited;
+ static struct dentry *vc_mem_debugfs_entry;
+ #endif
++struct vc_mem_dmacopy {
++      void *dst;
++      __u32 src;
++      __u32 length;
++};
++
++#ifdef CONFIG_COMPAT
++struct vc_mem_dmacopy32 {
++      compat_uptr_t dst;
++      __u32 src;
++      __u32 length;
++};
++#endif
++
+ /*
+  * Videocore memory addresses and size
+  *
+@@ -62,6 +88,20 @@ static uint phys_addr;
+ static uint mem_size;
+ static uint mem_base;
++struct vc_mem_dma {
++      struct device *dev;
++      int dma_chan;
++      int dma_irq;
++      void __iomem *dma_chan_base;
++      wait_queue_head_t dma_waitq;
++      void *cb_base;  /* DMA control blocks */
++      dma_addr_t cb_handle;
++};
++
++struct { u32 base, length; } gpu_mem;
++static struct mutex dma_mutex;
++static struct vc_mem_dma vc_mem_dma;
++
+ static int
+ vc_mem_open(struct inode *inode, struct file *file)
+ {
+@@ -99,6 +139,189 @@ vc_mem_get_current_size(void)
+ }
+ EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
++static int
++vc_mem_dma_init(void)
++{
++      struct vc_mem_dma *vcdma = &vc_mem_dma;
++      struct platform_device *pdev;
++      struct device_node *fwnode;
++      struct rpi_firmware *fw;
++      struct device *dev;
++      u32 revision;
++      int rc;
++
++      if (vcdma->dev)
++              return 0;
++
++      fwnode = of_find_node_by_path("/system");
++      rc = of_property_read_u32(fwnode, "linux,revision", &revision);
++      revision = (revision >> 12) & 0xf;
++      if (revision != 1 && revision != 2) {
++              /* Only BCM2709 and BCM2710 may have logs where the ARMs
++               * can't see them.
++               */
++              return -ENXIO;
++      }
++
++      fwnode = rpi_firmware_find_node();
++      if (!fwnode)
++              return -ENXIO;
++
++      pdev = of_find_device_by_node(fwnode);
++      dev = &pdev->dev;
++
++      rc = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
++      if (rc)
++              return rc;
++
++      fw = rpi_firmware_get(fwnode);
++      if (!fw)
++              return -ENXIO;
++      rc = rpi_firmware_property(fw, RPI_FIRMWARE_GET_VC_MEMORY,
++                                 &gpu_mem, sizeof(gpu_mem));
++      if (rc)
++              return rc;
++
++      gpu_mem.base = INTALIAS_NORMAL(gpu_mem.base);
++
++      if (!gpu_mem.base || !gpu_mem.length) {
++              dev_err(dev, "%s: unable to determine gpu memory (%x,%x)\n",
++                      __func__, gpu_mem.base, gpu_mem.length);
++              return -EFAULT;
++      }
++
++      vcdma->cb_base = dma_alloc_wc(dev, SZ_4K, &vcdma->cb_handle, GFP_KERNEL);
++      if (!vcdma->cb_base) {
++              dev_err(dev, "failed to allocate DMA CBs\n");
++              return -ENOMEM;
++      }
++
++      rc = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
++                              &vcdma->dma_chan_base,
++                              &vcdma->dma_irq);
++      if (rc < 0) {
++              dev_err(dev, "failed to allocate a DMA channel\n");
++              goto free_cb;
++      }
++
++      vcdma->dma_chan = rc;
++
++      init_waitqueue_head(&vcdma->dma_waitq);
++
++      vcdma->dev = dev;
++
++      return 0;
++
++free_cb:
++      dma_free_wc(dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
++
++      return rc;
++}
++
++static void
++vc_mem_dma_uninit(void)
++{
++      struct vc_mem_dma *vcdma = &vc_mem_dma;
++
++      if (vcdma->dev) {
++              bcm_dma_chan_free(vcdma->dma_chan);
++              dma_free_wc(vcdma->dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
++              vcdma->dev = NULL;
++      }
++}
++
++static int dma_memcpy(struct vc_mem_dma *vcdma, dma_addr_t dst, dma_addr_t src,
++                    int size)
++{
++      struct bcm2708_dma_cb *cb = vcdma->cb_base;
++      int burst_size = (vcdma->dma_chan == 0) ? 8 : 2;
++
++      cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
++                 BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
++                 BCM2708_DMA_D_INC;
++      cb->dst = dst;
++      cb->src = src;
++      cb->length = size;
++      cb->stride = 0;
++      cb->pad[0] = 0;
++      cb->pad[1] = 0;
++      cb->next = 0;
++
++      bcm_dma_start(vcdma->dma_chan_base, vcdma->cb_handle);
++      bcm_dma_wait_idle(vcdma->dma_chan_base);
++
++      return 0;
++}
++
++static long vc_mem_copy(struct vc_mem_dmacopy *ioparam)
++{
++      struct vc_mem_dma *vcdma = &vc_mem_dma;
++      size_t size = PAGE_SIZE;
++      const u32 dma_xfer_chunk = 256;
++      u32 *buf = NULL;
++      dma_addr_t bus_addr;
++      long rc = 0;
++      size_t offset;
++
++      /* restrict this to root user */
++      if (!uid_eq(current_euid(), GLOBAL_ROOT_UID))
++              return -EFAULT;
++
++      if (mutex_lock_interruptible(&dma_mutex))
++              return -EINTR;
++
++      rc = vc_mem_dma_init();
++      if (rc)
++              goto out;
++
++      vcdma = &vc_mem_dma;
++
++      if (INTALIAS_NORMAL(ioparam->src) < gpu_mem.base ||
++          INTALIAS_NORMAL(ioparam->src) >= gpu_mem.base + gpu_mem.length) {
++              pr_err("%s: invalid memory access %x (%x-%x)", __func__,
++                     INTALIAS_NORMAL(ioparam->src), gpu_mem.base,
++                     gpu_mem.base + gpu_mem.length);
++              rc = -EFAULT;
++              goto out;
++      }
++
++      buf = dma_alloc_coherent(vcdma->dev, PAGE_ALIGN(size), &bus_addr,
++                               GFP_ATOMIC);
++      if (!buf) {
++              rc = -ENOMEM;
++              goto out;
++      }
++
++      for (offset = 0; offset < ioparam->length; offset += size) {
++              size_t remaining = ioparam->length - offset;
++              size_t s = min(size, remaining);
++              u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
++              u8 *q = (u8 *)ioparam->dst + offset;
++
++              rc = dma_memcpy(vcdma, bus_addr,
++                              INTALIAS_L1L2_NONALLOCATING((u32)(uintptr_t)p),
++                              (s + dma_xfer_chunk - 1) & ~(dma_xfer_chunk - 1));
++              if (rc) {
++                      dev_err(vcdma->dev, "dma_memcpy failed\n");
++                      break;
++              }
++              if (copy_to_user(q, buf, s) != 0) {
++                      pr_err("%s: copy_to_user failed\n", __func__);
++                      rc = -EFAULT;
++                      break;
++              }
++      }
++
++out:
++      if (buf)
++              dma_free_coherent(vcdma->dev, PAGE_ALIGN(size), buf,
++                                bus_addr);
++
++      mutex_unlock(&dma_mutex);
++
++      return rc;
++}
++
+ static long
+ vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ {
+@@ -163,6 +386,21 @@ vc_mem_ioctl(struct file *file, unsigned
+                       }
+                       break;
+               }
++      case VC_MEM_IOC_DMACOPY:
++              {
++                      struct vc_mem_dmacopy ioparam;
++                      /* Get the parameter data.
++                       */
++                      if (copy_from_user
++                          (&ioparam, (void *)arg, sizeof(ioparam))) {
++                              pr_err("%s: copy_from_user failed\n", __func__);
++                              rc = -EFAULT;
++                              break;
++                      }
++
++                      rc = vc_mem_copy(&ioparam);
++                      break;
++              }
+       default:
+               {
+                       return -ENOTTY;
+@@ -193,6 +431,24 @@ vc_mem_compat_ioctl(struct file *file, u
+               break;
++      case VC_MEM_IOC_DMACOPY32:
++      {
++              struct vc_mem_dmacopy32 param32;
++              struct vc_mem_dmacopy param;
++              /* Get the parameter data.
++               */
++              if (copy_from_user(&param32, (void *)arg, sizeof(param32))) {
++                      pr_err("%s: copy_from_user failed\n", __func__);
++                      rc = -EFAULT;
++                      break;
++              }
++              param.dst = compat_ptr(param32.dst);
++              param.src = param32.src;
++              param.length = param32.length;
++              rc = vc_mem_copy(&param);
++              break;
++      }
++
+       default:
+               rc = vc_mem_ioctl(file, cmd, arg);
+               break;
+@@ -330,6 +586,7 @@ vc_mem_init(void)
+       vc_mem_debugfs_init(dev);
+ #endif
++      mutex_init(&dma_mutex);
+       vc_mem_inited = 1;
+       return 0;
+@@ -352,6 +609,7 @@ vc_mem_exit(void)
+ {
+       pr_debug("%s: called\n", __func__);
++      vc_mem_dma_uninit();
+       if (vc_mem_inited) {
+ #ifdef CONFIG_DEBUG_FS
+               vc_mem_debugfs_deinit();
+@@ -360,6 +618,7 @@ vc_mem_exit(void)
+               class_destroy(vc_mem_class);
+               cdev_del(&vc_mem_cdev);
+               unregister_chrdev_region(vc_mem_devnum, 1);
++              vc_mem_inited = 0;
+       }
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch b/target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch
new file mode 100644 (file)
index 0000000..32112f9
--- /dev/null
@@ -0,0 +1,48 @@
+From 6ab30a5dd3fb2ccbd918f147e91eb9bfe9849970 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 16 Oct 2023 12:13:38 +0100
+Subject: [PATCH] drm/vc4: Correct address offset for planes with src_[xy]
+ offsets
+
+11cf37e741b4 switched to using drm_fb_dma_get_gem_addr instead of
+drm_fb_dma_get_gem_obj and adding fb->offset[].
+
+However the tiled formats need to compute the offset in a more
+involved manner than drm_fb_dma_get_gem_addr applies, and we
+were ending up with the offset for src_[xy] being applied twice.
+
+Switch back to using drm_fb_dma_get_gem_obj and fully computing
+the offsets ourselves.
+
+Fixes: 11cf37e741b4 ("drm/vc4: Move the buffer offset out of the vc4_plane_state")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1447,9 +1447,9 @@ static int vc4_plane_mode_set(struct drm
+       vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+       for (i = 0; i < num_planes; i++) {
+-              dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++              struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, i);
+-              vc4_dlist_write(vc4_state, paddr + offsets[i]);
++              vc4_dlist_write(vc4_state, bo->dma_addr + fb->offsets[i] + offsets[i]);
+       }
+       /* Pointer Context Word 0/1/2: Written by the HVS */
+@@ -1842,9 +1842,8 @@ static int vc6_plane_mode_set(struct drm
+        * TODO: This only covers Raster Scan Order planes
+        */
+       for (i = 0; i < num_planes; i++) {
+-              dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
+-
+-              paddr += offsets[i];
++              struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, i);
++              dma_addr_t paddr = bo->dma_addr + fb->offsets[i] + offsets[i];
+               /* Pointer Word 0 */
+               vc4_state->ptr0_offset[i] = vc4_state->dlist_count;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch b/target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch
new file mode 100644 (file)
index 0000000..3e1d504
--- /dev/null
@@ -0,0 +1,69 @@
+From c14550658832026e82111f2a89b3f7bf567afc1c Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 17 Oct 2023 09:35:44 +0100
+Subject: [PATCH] drivers: media: rp1_cfe: Fix link validate test for pixel
+ format
+
+Now that we have removed unique PISP media bus codes, the cfe format
+table has multiple entries with the same media bus code for 16-bit
+formats. The test in cfe_video_link_validate() did not account for this.
+Fix it by testing the media bus code and the V4L2 pixelformat 4cc
+together.
+
+As a drive-by, ensure we have a valid CSI2 datatype id when programming
+the hardware block.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -786,6 +786,9 @@ static void cfe_start_channel(struct cfe
+               width = source_fmt->width;
+               height = source_fmt->height;
++              /* Must have a valid CSI2 datatype. */
++              WARN_ON(!fmt->csi_dt);
++
+               /*
+                * Start the associated CSI2 Channel as well.
+                *
+@@ -809,6 +812,9 @@ static void cfe_start_channel(struct cfe
+                       node_desc[node->id].link_pad - CSI2_NUM_CHANNELS);
+               fmt = find_format_by_code(source_fmt->code);
++              /* Must have a valid CSI2 datatype. */
++              WARN_ON(!fmt->csi_dt);
++
+               if (is_image_output_node(node)) {
+                       width = source_fmt->width;
+                       height = source_fmt->height;
+@@ -1504,7 +1510,8 @@ static int cfe_video_link_validate(struc
+       if (is_image_output_node(node)) {
+               struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
+-              const struct cfe_fmt *fmt;
++              const struct cfe_fmt *fmt = NULL;
++              unsigned int i;
+               if (source_fmt->width != pix_fmt->width ||
+                   source_fmt->height != pix_fmt->height) {
+@@ -1516,8 +1523,14 @@ static int cfe_video_link_validate(struc
+                       goto out;
+               }
+-              fmt = find_format_by_code(source_fmt->code);
+-              if (!fmt || fmt->fourcc != pix_fmt->pixelformat) {
++              for (i = 0; i < ARRAY_SIZE(formats); i++) {
++                      if (formats[i].code == source_fmt->code &&
++                          formats[i].fourcc == pix_fmt->pixelformat) {
++                              fmt = &formats[i];
++                              break;
++                      }
++              }
++              if (!fmt) {
+                       cfe_err("Format mismatch!\n");
+                       ret = -EINVAL;
+                       goto out;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch b/target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch
new file mode 100644 (file)
index 0000000..f304568
--- /dev/null
@@ -0,0 +1,23 @@
+From b6bfece0d9ddf21e1526fead81340ef02f98f6ad Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 17 Oct 2023 17:28:11 +0100
+Subject: [PATCH] dts: bcm2712: Use the new model name
+
+"Model B" is no more - "Raspberry Pi 5" is the official name.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -52,7 +52,7 @@
+ / {
+       compatible = "raspberrypi,5-model-b", "brcm,bcm2712";
+-      model = "Raspberry Pi 5 Model B";
++      model = "Raspberry Pi 5";
+       /* Will be filled by the bootloader */
+       memory@0 {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch b/target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch
new file mode 100644 (file)
index 0000000..df87a1b
--- /dev/null
@@ -0,0 +1,93 @@
+From 0c7fb448e0e0e47c2b3be64e438208682c6a5e61 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Oct 2023 16:32:45 +0100
+Subject: [PATCH] fbdev: Allow client to request a particular /dev/fbN node
+
+Add a flag custom_fb_num to denote that the client has
+requested a specific fbdev node number via node.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/video/fbdev/core/fbmem.c | 24 +++++++++++++++++-------
+ include/linux/fb.h               |  2 ++
+ 2 files changed, 19 insertions(+), 7 deletions(-)
+
+--- a/drivers/video/fbdev/core/fbmem.c
++++ b/drivers/video/fbdev/core/fbmem.c
+@@ -52,6 +52,7 @@ static DEFINE_MUTEX(registration_lock);
+ struct fb_info *registered_fb[FB_MAX] __read_mostly;
+ int num_registered_fb __read_mostly;
++int min_dynamic_fb __read_mostly;
+ #define for_each_registered_fb(i)             \
+       for (i = 0; i < FB_MAX; i++)            \
+               if (!registered_fb[i]) {} else
+@@ -1579,19 +1580,22 @@ static int do_register_framebuffer(struc
+               return -ENXIO;
+       num_registered_fb++;
+-      for (i = 0 ; i < FB_MAX; i++)
+-              if (!registered_fb[i])
+-                      break;
+-      fb_info->node = i;
++      if (!fb_info->custom_fb_num || fb_info->node >= FB_MAX || registered_fb[fb_info->node]) {
++              for (i = min_dynamic_fb ; i < FB_MAX; i++)
++                      if (!registered_fb[i])
++                              break;
++              fb_info->node = i;
++      }
+       refcount_set(&fb_info->count, 1);
+       mutex_init(&fb_info->lock);
+       mutex_init(&fb_info->mm_lock);
+       fb_info->dev = device_create(fb_class, fb_info->device,
+-                                   MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
++                                   MKDEV(FB_MAJOR, fb_info->node), NULL, "fb%d", fb_info->node);
+       if (IS_ERR(fb_info->dev)) {
+               /* Not fatal */
+-              printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
++              printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n",
++                     fb_info->node, PTR_ERR(fb_info->dev));
+               fb_info->dev = NULL;
+       } else
+               fb_init_device(fb_info);
+@@ -1624,7 +1628,7 @@ static int do_register_framebuffer(struc
+       fb_var_to_videomode(&mode, &fb_info->var);
+       fb_add_videomode(&mode, &fb_info->modelist);
+-      registered_fb[i] = fb_info;
++      registered_fb[fb_info->node] = fb_info;
+ #ifdef CONFIG_GUMSTIX_AM200EPD
+       {
+@@ -1719,6 +1723,12 @@ static int fb_aperture_acquire_for_platf
+       return ret;
+ }
++void fb_set_lowest_dynamic_fb(int min_fb_dev)
++{
++      min_dynamic_fb = min_fb_dev;
++}
++EXPORT_SYMBOL(fb_set_lowest_dynamic_fb);
++
+ /**
+  *    register_framebuffer - registers a frame buffer device
+  *    @fb_info: frame buffer info structure
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -512,6 +512,7 @@ struct fb_info {
+       } *apertures;
+       bool skip_vt_switch; /* no VT switch on suspend/resume required */
++      bool custom_fb_num; /* Use value in node as the preferred node number */
+ };
+ static inline struct apertures_struct *alloc_apertures(unsigned int max_num) {
+@@ -614,6 +615,7 @@ extern ssize_t fb_sys_write(struct fb_in
+                           size_t count, loff_t *ppos);
+ /* drivers/video/fbmem.c */
++extern void fb_set_lowest_dynamic_fb(int min_fb_dev);
+ extern int register_framebuffer(struct fb_info *fb_info);
+ extern void unregister_framebuffer(struct fb_info *fb_info);
+ extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch b/target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch
new file mode 100644 (file)
index 0000000..3e360be
--- /dev/null
@@ -0,0 +1,41 @@
+From 1216ea56c2e30aee4975b4dcce79ebd199afaf8f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Oct 2023 16:34:36 +0100
+Subject: [PATCH] drm/fb-helper: Look up preferred fbdev node number from DT
+
+For situations where there are multiple DRM cards in a system,
+add a query of DT for "drm_fb" designations for cards to set
+their preferred /dev/fbN designation.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1932,7 +1932,7 @@ __drm_fb_helper_initial_config_and_unloc
+       struct drm_device *dev = fb_helper->dev;
+       struct fb_info *info;
+       unsigned int width, height;
+-      int ret;
++      int ret, id;
+       width = dev->mode_config.max_width;
+       height = dev->mode_config.max_height;
+@@ -1967,6 +1967,15 @@ __drm_fb_helper_initial_config_and_unloc
+        * register the fbdev emulation instance in kernel_fb_helper_list. */
+       mutex_unlock(&fb_helper->lock);
++      id = of_alias_get_highest_id("drm_fb");
++      if (id >= 0)
++              fb_set_lowest_dynamic_fb(id + 1);
++
++      id = of_alias_get_id(dev->dev->of_node, "drm_fb");
++      if (id >= 0) {
++              info->node = id;
++              info->custom_fb_num = true;
++      }
+       ret = register_framebuffer(info);
+       if (ret < 0)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch b/target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch
new file mode 100644 (file)
index 0000000..924c1b9
--- /dev/null
@@ -0,0 +1,72 @@
+From 61b138adaeaddefe749f421a3b69c67d4a49a8e3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Oct 2023 11:03:51 +0100
+Subject: [PATCH] dt: Add overrides for drm framebuffer allocations on Pi5
+
+Adds dtparam overrides to the base Pi5 DT such that vc4,
+DSI0, DSI1, or DPI can be requested to be /dev/fb[012].
+No override is specified by default, so the order will be
+based on probe order (aka semi-random). Any device that
+doesn't have an override specified will be placed above
+all specified overrides. Having an fb1 or fb2 override but
+no fb0 one will result in no console via fbcon.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 12 ++++++++++++
+ arch/arm/boot/dts/overlays/README     | 24 ++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -824,5 +824,17 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               act_led_trigger = <&act_led>, "linux,default-trigger";
+               pwr_led_activelow = <&pwr_led>, "gpios:8";
+               pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++              drm_fb0_rp1_dsi0 = <&aliases>, "drm_fb0=",&dsi0;
++              drm_fb0_rp1_dsi1 = <&aliases>, "drm_fb0=",&dsi1;
++              drm_fb0_rp1_dpi = <&aliases>, "drm_fb0=",&dpi;
++              drm_fb0_vc4 = <&aliases>, "drm_fb0=",&vc4;
++              drm_fb1_rp1_dsi0 = <&aliases>, "drm_fb1=",&dsi0;
++              drm_fb1_rp1_dsi1 = <&aliases>, "drm_fb1=",&dsi1;
++              drm_fb1_rp1_dpi = <&aliases>, "drm_fb1=",&dpi;
++              drm_fb1_vc4 = <&aliases>, "drm_fb1=",&vc4;
++              drm_fb2_rp1_dsi0 = <&aliases>, "drm_fb2=",&dsi0;
++              drm_fb2_rp1_dsi1 = <&aliases>, "drm_fb2=",&dsi1;
++              drm_fb2_rp1_dpi = <&aliases>, "drm_fb2=",&dpi;
++              drm_fb2_vc4 = <&aliases>, "drm_fb2=",&vc4;
+       };
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -173,6 +173,30 @@ Params:
+         cooling_fan             Enables the Pi 5 cooling fan (enabled
+                                 automatically by the firmware)
++        drm_fb0_rp1_dpi         Assign /dev/fb0 to the RP1 DPI output
++
++        drm_fb0_rp1_dsi0        Assign /dev/fb0 to the RP1 DSI0 output
++
++        drm_fb0_rp1_dsi1        Assign /dev/fb0 to the RP1 DSI1 output
++
++        drm_fb0_vc4             Assign /dev/fb0 to the vc4 outputs
++
++        drm_fb1_rp1_dpi         Assign /dev/fb1 to the RP1 DPI output
++
++        drm_fb1_rp1_dsi0        Assign /dev/fb1 to the RP1 DSI0 output
++
++        drm_fb1_rp1_dsi1        Assign /dev/fb1 to the RP1 DSI1 output
++
++        drm_fb1_vc4             Assign /dev/fb1 to the vc4 outputs
++
++        drm_fb2_rp1_dpi         Assign /dev/fb2 to the RP1 DPI output
++
++        drm_fb2_rp1_dsi0        Assign /dev/fb2 to the RP1 DSI0 output
++
++        drm_fb2_rp1_dsi1        Assign /dev/fb2 to the RP1 DSI1 output
++
++        drm_fb2_vc4             Assign /dev/fb2 to the vc4 outputs
++
+         eee                     Enable Energy Efficient Ethernet support for
+                                 compatible devices (default "on"). See also
+                                 "tx_lpi_timer". Pi3B+ only.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch b/target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch
new file mode 100644 (file)
index 0000000..57782d5
--- /dev/null
@@ -0,0 +1,26 @@
+From f429fc1a072d4bb35e622a1012a5a52914eba4e3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:34:58 +0100
+Subject: [PATCH] drm/connector: Change DRM card alias from underscore to
+ hyphen
+
+Apparently aliases are only allowed lower case and hyphens,
+so swap the use of underscore to hyphen.
+
+Fixes: 3aa1f2477545 ("drm: Look for an alias for the displays to use as the DRM device name")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -112,7 +112,7 @@ static struct drm_conn_prop_enum_list dr
+ };
+ #define MAX_DT_NODE_NAME_LEN  20
+-#define DT_DRM_NODE_PREFIX    "drm_"
++#define DT_DRM_NODE_PREFIX    "drm-"
+ static void drm_connector_get_of_name(int type, char *node_name, int length)
+ {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch b/target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch
new file mode 100644 (file)
index 0000000..0536924
--- /dev/null
@@ -0,0 +1,24 @@
+From e33170e21494279801e9f17eeb910ec45ebd740c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:28:43 +0100
+Subject: [PATCH] dt: Alter alias names from _ to - for drm_dsiN
+
+Fixes: 7ec42740a45b ("dt: Add DSI1 and DSI2 aliases to 2712")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -787,8 +787,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               gpio4 = &pinctrl_aon;
+               usb0 = &rp1_usb0;
+               usb1 = &rp1_usb1;
+-              drm_dsi1 = &dsi0;
+-              drm_dsi2 = &dsi1;
++              drm-dsi1 = &dsi0;
++              drm-dsi2 = &dsi1;
+       };
+       __overrides__ {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch b/target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch
new file mode 100644 (file)
index 0000000..78e5b89
--- /dev/null
@@ -0,0 +1,28 @@
+From 0e9e925112fabdbd448e17947796317a6dbca96e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:32:04 +0100
+Subject: [PATCH] drm/fb_helper: Change query for FB designation from drm_fb to
+ drm-fb
+
+Fixes: 1216ea56c2e3 ("drm/fb-helper: Look up preferred fbdev node number from DT")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1967,11 +1967,11 @@ __drm_fb_helper_initial_config_and_unloc
+        * register the fbdev emulation instance in kernel_fb_helper_list. */
+       mutex_unlock(&fb_helper->lock);
+-      id = of_alias_get_highest_id("drm_fb");
++      id = of_alias_get_highest_id("drm-fb");
+       if (id >= 0)
+               fb_set_lowest_dynamic_fb(id + 1);
+-      id = of_alias_get_id(dev->dev->of_node, "drm_fb");
++      id = of_alias_get_id(dev->dev->of_node, "drm-fb");
+       if (id >= 0) {
+               info->node = id;
+               info->custom_fb_num = true;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch b/target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch
new file mode 100644 (file)
index 0000000..42865dc
--- /dev/null
@@ -0,0 +1,43 @@
+From 87be94059193d0bc64d52a9535df4fb1891c72fe Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:29:20 +0100
+Subject: [PATCH] dt: Alter alias names from _ to - for drm_fbN_* overrides
+
+Fixes: 61b138adaead ("dt: Add overrides for drm framebuffer allocations on Pi5")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -824,17 +824,17 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               act_led_trigger = <&act_led>, "linux,default-trigger";
+               pwr_led_activelow = <&pwr_led>, "gpios:8";
+               pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
+-              drm_fb0_rp1_dsi0 = <&aliases>, "drm_fb0=",&dsi0;
+-              drm_fb0_rp1_dsi1 = <&aliases>, "drm_fb0=",&dsi1;
+-              drm_fb0_rp1_dpi = <&aliases>, "drm_fb0=",&dpi;
+-              drm_fb0_vc4 = <&aliases>, "drm_fb0=",&vc4;
+-              drm_fb1_rp1_dsi0 = <&aliases>, "drm_fb1=",&dsi0;
+-              drm_fb1_rp1_dsi1 = <&aliases>, "drm_fb1=",&dsi1;
+-              drm_fb1_rp1_dpi = <&aliases>, "drm_fb1=",&dpi;
+-              drm_fb1_vc4 = <&aliases>, "drm_fb1=",&vc4;
+-              drm_fb2_rp1_dsi0 = <&aliases>, "drm_fb2=",&dsi0;
+-              drm_fb2_rp1_dsi1 = <&aliases>, "drm_fb2=",&dsi1;
+-              drm_fb2_rp1_dpi = <&aliases>, "drm_fb2=",&dpi;
+-              drm_fb2_vc4 = <&aliases>, "drm_fb2=",&vc4;
++              drm_fb0_rp1_dsi0 = <&aliases>, "drm-fb0=",&dsi0;
++              drm_fb0_rp1_dsi1 = <&aliases>, "drm-fb0=",&dsi1;
++              drm_fb0_rp1_dpi = <&aliases>, "drm-fb0=",&dpi;
++              drm_fb0_vc4 = <&aliases>, "drm-fb0=",&vc4;
++              drm_fb1_rp1_dsi0 = <&aliases>, "drm-fb1=",&dsi0;
++              drm_fb1_rp1_dsi1 = <&aliases>, "drm-fb1=",&dsi1;
++              drm_fb1_rp1_dpi = <&aliases>, "drm-fb1=",&dpi;
++              drm_fb1_vc4 = <&aliases>, "drm-fb1=",&vc4;
++              drm_fb2_rp1_dsi0 = <&aliases>, "drm-fb2=",&dsi0;
++              drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1;
++              drm_fb2_rp1_dpi = <&aliases>, "drm-fb2=",&dpi;
++              drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch b/target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch
new file mode 100644 (file)
index 0000000..19720e8
--- /dev/null
@@ -0,0 +1,21 @@
+From cb8a4adb586c5e926e415ac0dae3ffb4af30b0a9 Mon Sep 17 00:00:00 2001
+From: Andrew Scheller <andrew.scheller@raspberrypi.com>
+Date: Thu, 19 Oct 2023 14:13:36 +0100
+Subject: [PATCH] Typo in overlays README
+
+touchscreen-size-y for rpi-ft5406 defaults to 480, not 600
+---
+ arch/arm/boot/dts/overlays/README | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3738,7 +3738,7 @@ Name:   rpi-ft5406
+ Info:   Official Raspberry Pi display touchscreen
+ Load:   dtoverlay=rpi-ft5406,<param>=<val>
+ Params: touchscreen-size-x      Touchscreen X resolution (default 800)
+-        touchscreen-size-y      Touchscreen Y resolution (default 600);
++        touchscreen-size-y      Touchscreen Y resolution (default 480);
+         touchscreen-inverted-x  Invert touchscreen X coordinates (default 0);
+         touchscreen-inverted-y  Invert touchscreen Y coordinates (default 0);
+         touchscreen-swapped-x-y Swap X and Y cordinates (default 0);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch
new file mode 100644 (file)
index 0000000..95940dd
--- /dev/null
@@ -0,0 +1,23 @@
+From 4cb97982f88d8fb623ace5a9511198e442e993ba Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:15:25 +0100
+Subject: [PATCH] dts: bcm2712: Add the krnbt parameter
+
+Add a Pi 5 implementation of the krnbt parameter, for symmetry and
+for tinkering purposes.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -809,6 +809,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               i2c_baudrate = <&i2c_arm>, "clock-frequency:0";
+               i2c_arm_baudrate = <&i2c_arm>, "clock-frequency:0";
+               i2c_vc_baudrate = <&i2c_vc>, "clock-frequency:0";
++              krnbt = <&bluetooth>, "status";
+               nvme = <&pciex1>, "status";
+               pciex1 = <&pciex1>, "status";
+               pciex1_gen = <&pciex1> , "max-link-speed:0";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch b/target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch
new file mode 100644 (file)
index 0000000..99b8426
--- /dev/null
@@ -0,0 +1,109 @@
+From 4137b49989ce710305e476d0bd1086d7d906ff50 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:09:54 +0100
+Subject: [PATCH] drm/vc4_fkms: Fix up interrupt handler for both 2835/2711 and
+ 2712
+
+2712 has switched from using the SMI peripheral to another interrupt
+source for the vsync interrupt, so handle both sources cleanly.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 48 ++++++++++++++++++++------
+ 1 file changed, 38 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -47,9 +47,15 @@ struct get_display_cfg {
+       u32  max_pixel_clock[2];  //Max pixel clock for each display
+ };
++enum vc4_fkms_revision {
++      BCM2835_6_7,
++      BCM2711,
++      BCM2712,
++};
++
+ struct vc4_fkms {
+       struct get_display_cfg cfg;
+-      bool bcm2711;
++      enum vc4_fkms_revision revision;
+ };
+ #define PLANES_PER_CRTC               8
+@@ -1149,7 +1155,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
+       /* Pi4 can't generate odd horizontal timings on HDMI, so reject modes
+        * that would set them.
+        */
+-      if (fkms->bcm2711 &&
++      if (fkms->revision >= BCM2711 &&
+           (vc4_crtc->display_number == 2 || vc4_crtc->display_number == 7) &&
+           !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
+           ((mode->hdisplay |                          /* active */
+@@ -1267,6 +1273,20 @@ static irqreturn_t vc4_crtc_irq_handler(
+       return ret;
+ }
++static irqreturn_t vc4_crtc2712_irq_handler(int irq, void *data)
++{
++      struct vc4_crtc **crtc_list = data;
++      int i;
++
++      for (i = 0; crtc_list[i]; i++) {
++              if (crtc_list[i]->vblank_enabled)
++                      drm_crtc_handle_vblank(&crtc_list[i]->base);
++              vc4_crtc_handle_page_flip(crtc_list[i]);
++      }
++
++      return IRQ_HANDLED;
++}
++
+ static int vc4_fkms_page_flip(struct drm_crtc *crtc,
+                             struct drm_framebuffer *fb,
+                             struct drm_pending_vblank_event *event,
+@@ -1352,9 +1372,12 @@ static const struct drm_crtc_helper_func
+ };
+ static const struct of_device_id vc4_firmware_kms_dt_match[] = {
+-      { .compatible = "raspberrypi,rpi-firmware-kms" },
++      { .compatible = "raspberrypi,rpi-firmware-kms",
++        .data = (void *)BCM2835_6_7 },
+       { .compatible = "raspberrypi,rpi-firmware-kms-2711",
+-        .data = (void *)1 },
++        .data = (void *)BCM2711 },
++      { .compatible = "raspberrypi,rpi-firmware-kms-2712",
++        .data = (void *)BCM2712 },
+       {}
+ };
+@@ -1924,8 +1947,7 @@ static int vc4_fkms_bind(struct device *
+       match = of_match_device(vc4_firmware_kms_dt_match, dev);
+       if (!match)
+               return -ENODEV;
+-      if (match->data)
+-              fkms->bcm2711 = true;
++      fkms->revision = (enum vc4_fkms_revision)match->data;
+       firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
+       vc4->firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node);
+@@ -1992,10 +2014,16 @@ static int vc4_fkms_bind(struct device *
+               if (IS_ERR(crtc_list[0]->regs))
+                       DRM_ERROR("Oh dear, failed to map registers\n");
+-              writel(0, crtc_list[0]->regs + SMICS);
+-              ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+-                                     vc4_crtc_irq_handler, 0,
+-                                     "vc4 firmware kms", crtc_list);
++              if (fkms->revision >= BCM2712) {
++                      ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++                                             vc4_crtc2712_irq_handler, 0,
++                                             "vc4 firmware kms", crtc_list);
++              } else {
++                      writel(0, crtc_list[0]->regs + SMICS);
++                      ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++                                             vc4_crtc_irq_handler, 0,
++                                             "vc4 firmware kms", crtc_list);
++              }
+               if (ret)
+                       DRM_ERROR("Oh dear, failed to register IRQ\n");
+       } else {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch b/target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch
new file mode 100644 (file)
index 0000000..9dea2d0
--- /dev/null
@@ -0,0 +1,25 @@
+From 5a52cae54a05499a8487f392cf5dfc3d8a837e6f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:12:09 +0100
+Subject: [PATCH] dt: Switch bcm2712 firmware-kms node to using the 2712
+ compatible
+
+With the new compatible to handle the interrupts correctly, switch
+the base dt to use it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -103,7 +103,7 @@
+               };
+               firmwarekms: firmwarekms@7d503000 {
+-                      compatible = "raspberrypi,rpi-firmware-kms";
++                      compatible = "raspberrypi,rpi-firmware-kms-2712";
+                       /* SUN_L2 interrupt reg */
+                       reg = <0x7d503000 0x18>;
+                       interrupt-parent = <&cpu_l2_irq>;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch b/target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch
new file mode 100644 (file)
index 0000000..841fa42
--- /dev/null
@@ -0,0 +1,35 @@
+From f075893e9b0e241879998c0b12cf8af0ba7737da Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 23 Oct 2023 10:03:03 +0100
+Subject: [PATCH] drivers: media: imx477: Disable the scaler
+
+The horizontal scaler was enabled for the 2028x1520 and 2028x1080 modes,
+with a scale factor of 1. It caused a single column of bad pixels on the
+right edge of the image. Since scaling is not needed for these modes,
+disable it entirely.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -632,7 +632,7 @@ static const struct imx477_reg mode_2028
+       {0x9e9f, 0x00},
+       {0xa2a9, 0x60},
+       {0xa2b7, 0x00},
+-      {0x0401, 0x01},
++      {0x0401, 0x00},
+       {0x0404, 0x00},
+       {0x0405, 0x20},
+       {0x0408, 0x00},
+@@ -733,7 +733,7 @@ static const struct imx477_reg mode_2028
+       {0x9e9f, 0x00},
+       {0xa2a9, 0x60},
+       {0xa2b7, 0x00},
+-      {0x0401, 0x01},
++      {0x0401, 0x00},
+       {0x0404, 0x00},
+       {0x0405, 0x20},
+       {0x0408, 0x00},
diff --git a/target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch b/target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch
new file mode 100644 (file)
index 0000000..77f30a3
--- /dev/null
@@ -0,0 +1,37 @@
+From eccaa8588fca9c9ec950664f1d5894bd826b57b0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Oct 2023 14:10:15 +0100
+Subject: [PATCH] dt: Add drm_fbN_vc4 overrides for Pi0-4
+
+Follows up '61b138adaead ("dt: Add overrides for drm framebuffer
+allocations on Pi5")' with an equivalent for Pi0-4.
+
+These will have no effect on most normal systems, but drm_fb0_vc4
+will stop SPI displays jumping in and claiming /dev/fb0.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm270x-rpi.dtsi | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -1,7 +1,7 @@
+ /* Downstream modifications to bcm2835-rpi.dtsi */
+ / {
+-      aliases {
++      aliases: aliases {
+               aux = &aux;
+               sound = &sound;
+               soc = &soc;
+@@ -98,6 +98,9 @@
+               sdio_overclock = <&mmc>,"brcm,overclock-50:0",
+                                <&mmcnr>,"brcm,overclock-50:0";
+               axiperf      = <&axiperf>,"status";
++              drm_fb0_vc4 = <&aliases>, "drm-fb0=",&vc4;
++              drm_fb1_vc4 = <&aliases>, "drm-fb1=",&vc4;
++              drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch b/target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch
new file mode 100644 (file)
index 0000000..5016e78
--- /dev/null
@@ -0,0 +1,95 @@
+From 3ed6d34d53e94ecbebc64c8fa3d1b6d3c41db8fb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 24 Oct 2023 09:58:52 +0100
+Subject: [PATCH] fixup! overlays: mcp23017: allow specification of the i2c bus
+
+The incorrect fragment order (*) caused broke the interrupt usage, and
+while it was being fixed the lack of a reference to the pinctrl
+declaration was noticed.
+
+See: https://github.com/raspberrypi/linux/issues/5677
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+(*) Ideally all fragments would appear in the file in the order in which
+they should be merged, but that is easy to forget and can be awkward, so
+the firmware merges all "intra" fragments (those that target other
+fragments in the overlay) before "inter" fragments (those that target
+the base DTB). However, intra fragments that target other intra
+fragments is a level of nesting too far for this logic to cope, so they
+must appear before the fragments they target.
+---
+ .../boot/dts/overlays/mcp23017-overlay.dts    | 42 ++++++++++---------
+ 1 file changed, 22 insertions(+), 20 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -24,30 +24,13 @@
+       };
+       fragment@2 {
+-              target = <&i2cbus>;
+-              __overlay__ {
+-                      #address-cells = <1>;
+-                      #size-cells = <0>;
+-
+-                      mcp23017: mcp@20 {
+-                              compatible = "microchip,mcp23017";
+-                              reg = <0x20>;
+-                              gpio-controller;
+-                              #gpio-cells = <2>;
+-
+-                              status = "okay";
+-                      };
+-              };
+-      };
+-
+-      fragment@3 {
+               target = <&mcp23017>;
+               __dormant__ {
+                       compatible = "microchip,mcp23008";
+               };
+       };
+-      fragment@4 {
++      fragment@3 {
+               target = <&mcp23017>;
+               mcp23017_irq: __overlay__ {
+                       #interrupt-cells=<2>;
+@@ -58,6 +41,25 @@
+               };
+       };
++      fragment@4 {
++              target = <&i2cbus>;
++              __overlay__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++
++                      mcp23017: mcp@20 {
++                              compatible = "microchip,mcp23017";
++                              pinctrl-name = "default";
++                              pinctrl-0 = <&mcp23017_pins>;
++                              reg = <0x20>;
++                              gpio-controller;
++                              #gpio-cells = <2>;
++
++                              status = "okay";
++                      };
++              };
++      };
++
+       frag100: fragment@100 {
+               target = <&i2c1>;
+               i2cbus: __overlay__ {
+@@ -83,8 +85,8 @@
+               gpiopin = <&mcp23017_pins>,"brcm,pins:0",
+                               <&mcp23017_irq>,"interrupts:0";
+               addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0";
+-              mcp23008 = <0>,"=3";
+-              noints = <0>,"!1!4";
++              mcp23008 = <0>,"=2";
++              noints = <0>,"!1!3";
+               i2c0 = <&frag100>, "target:0=",<&i2c0>;
+               i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+                             <0>,"+101+102";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch b/target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch
new file mode 100644 (file)
index 0000000..7fe0c50
--- /dev/null
@@ -0,0 +1,54 @@
+From 8d53cc5b4b2a6f9baed7a0aa801a39ad9dce9bf8 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 26 Oct 2023 08:55:24 +0100
+Subject: [PATCH] drivers: media: pisp_be: Add back V4L2_PIX_FMT_RPI_BE format
+
+Add the opaque V4L2_PIX_FMT_RPI_BE format back to the format list as it
+is needed for the verification test suite. Also set the default format
+to YUV420 non-multiplanar.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/pisp_be/pisp_be.c     | 9 ++++++---
+ .../media/platform/raspberrypi/pisp_be/pisp_be_formats.h | 5 +++++
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+@@ -1230,8 +1230,11 @@ static int try_format(struct v4l2_format
+               return verify_be_pix_format(f, node);
+       fmt = find_format(pixfmt);
+-      if (!fmt)
+-              fmt = find_format(V4L2_PIX_FMT_YUV420M);
++      if (!fmt) {
++              dev_dbg(pispbe->dev, "%s: [%s] Format not found, defaulting to YUV420\n",
++                      __func__, NODE_NAME(node));
++              fmt = find_format(V4L2_PIX_FMT_YUV420);
++      }
+       f->fmt.pix_mp.pixelformat = fmt->fourcc;
+       f->fmt.pix_mp.num_planes = fmt->num_planes;
+@@ -1576,7 +1579,7 @@ static void node_set_default_format(stru
+       } else {
+               struct v4l2_format f = {0};
+-              f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
++              f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420;
+               f.fmt.pix_mp.width = 1920;
+               f.fmt.pix_mp.height = 1080;
+               f.type = node->buf_type;
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -457,6 +457,11 @@ static const struct pisp_be_format suppo
+               .colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+               .colorspace_default = V4L2_COLORSPACE_RAW,
+       },
++      /* Opaque BE format for HW verification. */
++      {
++              .fourcc             = V4L2_PIX_FMT_RPI_BE,
++              .align              = 32,
++      },
+ };
+ static const struct pisp_be_format meta_out_supported_formats[] = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch b/target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch
new file mode 100644 (file)
index 0000000..3ef42e7
--- /dev/null
@@ -0,0 +1,32 @@
+From f1154884295a4bd00d6ebcaf01fa30141145903d Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 10:04:15 +0100
+Subject: [PATCH] dt-bindings: PCI: brcmstb: add optional property -
+ "brcm,tperst-clk-ms"
+
+This property can be used to delay deassertion of external fundamental
+reset, which may be useful for endpoints that require an extended time for
+internal setup to complete.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
++++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
+@@ -77,6 +77,14 @@ properties:
+       minItems: 1
+       maxItems: 3
++  brcm,tperst-clk-ms:
++    category: optional
++    type: int
++    description: u32 giving the number of milliseconds to extend
++      the time between internal release of fundamental reset and
++      the deassertion of the external PERST# pin. This has the
++      effect of increasing the Tperst_clk phase of link init.
++
+ required:
+   - compatible
+   - reg
diff --git a/target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch b/target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch
new file mode 100644 (file)
index 0000000..021e210
--- /dev/null
@@ -0,0 +1,71 @@
+From 4b0c6453808a662869a43c504913f3b7ed64486a Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 13:01:11 +0100
+Subject: [PATCH] drivers: pci: brcmstb: optionally extend Tperst_clk time
+ during link-up
+
+The RC has a feature that allows for manual control over the deassertion
+of the PERST# output pin, which allows the time between refclk active
+and reset deassert at the EP to be increased.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 24 +++++++++++++++++++++++-
+ 1 file changed, 23 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -138,6 +138,7 @@
+ #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_PERST_ASSERT_MASK             0x8
+ #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
+@@ -352,6 +353,7 @@ struct brcm_pcie {
+       bool                    (*rc_mode)(struct brcm_pcie *pcie);
+       struct subdev_regulators *sr;
+       bool                    ep_wakeup_capable;
++      u32                     tperst_clk_ms;
+ };
+ static inline bool is_bmips(const struct brcm_pcie *pcie)
+@@ -1388,9 +1390,28 @@ static int brcm_pcie_start_link(struct b
+       u16 nlw, cls, lnksta;
+       bool ssc_good = false;
+       int ret, i;
++      u32 tmp;
+       /* Unassert the fundamental reset */
+-      pcie->perst_set(pcie, 0);
++      if (pcie->tperst_clk_ms) {
++              /*
++               * Increase Tperst_clk time by forcing PERST# output low while
++               * the internal reset is released, so the PLL generates stable
++               * refclk output further in advance of PERST# deassertion.
++               */
++              tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++              u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
++              writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++              pcie->perst_set(pcie, 0);
++              msleep(pcie->tperst_clk_ms);
++
++              tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++              u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
++              writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++      } else {
++              pcie->perst_set(pcie, 0);
++      }
+       /*
+        * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
+@@ -1923,6 +1944,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");
++      of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms);
+       ret = clk_prepare_enable(pcie->clk);
+       if (ret) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch b/target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch
new file mode 100644 (file)
index 0000000..a81d038
--- /dev/null
@@ -0,0 +1,59 @@
+From db90a5e5fc2fbd843b29eb8110ed5e03604a2887 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 13:04:54 +0100
+Subject: [PATCH] arm: dt: add dtparams for PCIe reset timing override
+
+The Pi 5 variant gets two parameters so that the CM4-compatible
+name will also work on Pi 5.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-cm4.dts | 2 ++
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/overlays/README     | 7 +++++++
+ 3 files changed, 11 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
+@@ -446,5 +446,7 @@ i2c_csi_dsi0: &i2c0 {
+               cam1_reg = <&cam1_reg>,"status";
+               cam1_reg_gpio = <&cam1_reg>,"gpio:4",
+                                 <&cam1_reg>,"gpio:0=", <&gpio>;
++
++              pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0";
+       };
+ };
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -814,6 +814,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               pciex1 = <&pciex1>, "status";
+               pciex1_gen = <&pciex1> , "max-link-speed:0";
+               pciex1_no_l0s = <&pciex1>, "aspm-no-l0s?";
++              pciex1_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
++              pcie_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+               random = <&random>, "status";
+               rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
+               spi = <&spi0>, "status";
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -280,6 +280,10 @@ Params:
+                                 (2711 only, but not applicable on CM4S)
+                                 N.B. USB-A ports on 4B are subsequently disabled
++        pcie_tperst_clk_ms      Add N milliseconds between PCIe reference clock
++                                activation and PERST# deassertion
++                                (CM4 and 2712, default "0")
++
+         pciex1                  Set to "on" to enable the external PCIe link
+                                 (2712 only, default "off")
+@@ -290,6 +294,9 @@ Params:
+                                 PCIe link for devices that have broken
+                                 implementations (2712 only, default "off")
++        pciex1_tperst_clk_ms    Alias for pcie_tperst_clk_ms
++                                (2712 only, default "0")
++
+         spi                     Set to "on" to enable the spi interfaces
+                                 (default "off")
diff --git a/target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch b/target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch
new file mode 100644 (file)
index 0000000..b08a572
--- /dev/null
@@ -0,0 +1,35 @@
+From cb013b6602de32c647ed08faf899596664a18635 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Thu, 26 Oct 2023 13:47:54 +0100
+Subject: [PATCH] arm: dt: bcm2712: don't unconditionally enable MPS read
+ completions
+
+RP1 supports it, but it's not a given that an arbitrary EP device
+on PCIE2 will. Migrate the property to the rp1_target fragment.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ arch/arm/boot/dts/bcm2712.dtsi        | 1 -
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -160,6 +160,7 @@
+ };
+ rp1_target: &pcie2 {
++      brcm,enable-mps-rcb;
+       brcm,vdm-qos-map = <0xbbaa9888>;
+       aspm-no-l0s;
+       status = "okay";
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -1086,7 +1086,6 @@
+                                     0x00 0x00000000
+                                     0x10 0x00000000>;
+-                      brcm,enable-mps-rcb;
+                       brcm,enable-l1ss;
+                       status = "disabled";
+               };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch b/target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch
new file mode 100644 (file)
index 0000000..e6122e1
--- /dev/null
@@ -0,0 +1,39 @@
+From 8dcc16f0adc50f5cb8a11a6dde238131d0ca45a0 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Fri, 27 Oct 2023 12:14:22 +0100
+Subject: [PATCH] drivers: media: imx477: Set horizontal binning when disabling
+ the scaler
+
+The horizontal scaler has been disabled but actually the sensor is not
+binning horizontally, resulting in images that are stretched 2x
+horizontally (missing the right half of the field of view completely).
+
+Therefore we must additionally set the horizontal binning mode. There
+is only marginal change in output quality and noise levels.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Fixes: f075893e9b0e ("drivers: media: imx477: Disable the scaler")
+---
+ drivers/media/i2c/imx477.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -607,7 +607,7 @@ static const struct imx477_reg mode_2028
+       {0x0385, 0x01},
+       {0x0387, 0x01},
+       {0x0900, 0x01},
+-      {0x0901, 0x12},
++      {0x0901, 0x22},
+       {0x0902, 0x02},
+       {0x3140, 0x02},
+       {0x3c00, 0x00},
+@@ -708,7 +708,7 @@ static const struct imx477_reg mode_2028
+       {0x0385, 0x01},
+       {0x0387, 0x01},
+       {0x0900, 0x01},
+-      {0x0901, 0x12},
++      {0x0901, 0x22},
+       {0x0902, 0x02},
+       {0x3140, 0x02},
+       {0x3c00, 0x00},
diff --git a/target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch b/target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch
new file mode 100644 (file)
index 0000000..adedaf6
--- /dev/null
@@ -0,0 +1,25 @@
+From e641fd7a50987ad6b7ce1ab36189bc8817295e42 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 31 Oct 2023 16:34:56 +0000
+Subject: [PATCH] fixup! arch/arm64: Add Revision, Serial, Model to cpuinfo
+
+Delete the Hardware string, which is pointless and misleading.
+
+See: https://github.com/raspberrypi/bookworm-feedback/issues/129
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/kernel/cpuinfo.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/arch/arm64/kernel/cpuinfo.c
++++ b/arch/arm64/kernel/cpuinfo.c
+@@ -224,8 +224,6 @@ static int c_show(struct seq_file *m, vo
+               seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
+       }
+-      seq_printf(m, "Hardware\t: BCM2835\n");
+-
+       np = of_find_node_by_path("/system");
+       if (np) {
+               if (!of_property_read_u32(np, "linux,revision", &revision))
diff --git a/target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch b/target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch
new file mode 100644 (file)
index 0000000..18c12cd
--- /dev/null
@@ -0,0 +1,35 @@
+From e86c43b86179fba90a1d9dd5acb554767af6740f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 12 Jun 2023 15:23:55 +0100
+Subject: [PATCH] dts: bcm2710-rpi-zero-2-w: Remove WLAN firmwares
+
+With careful use of qualified firmware names there is no need for the
+ability to override the device names based on Device Tree properties.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
+@@ -161,19 +161,6 @@
+       brcmf: wifi@1 {
+               reg = <1>;
+               compatible = "brcm,bcm4329-fmac";
+-
+-              firmwares {
+-                      fw_43436p {
+-                              chipid = <43430>;
+-                              revmask = <4>;
+-                              fw_base = "brcm/brcmfmac43436-sdio";
+-                      };
+-                      fw_43436s {
+-                              chipid = <43430>;
+-                              revmask = <2>;
+-                              fw_base = "brcm/brcmfmac43436s-sdio";
+-                      };
+-              };
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch b/target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch
new file mode 100644 (file)
index 0000000..d5aeae5
--- /dev/null
@@ -0,0 +1,111 @@
+From a11312709f46b71bf320a9dcc8cf4e09056552cd Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 1 Nov 2023 13:25:54 +0000
+Subject: [PATCH] drivers: media: cfe: Set the CSI-2 link frequency correctly
+
+Use the sensor provided link frequency to set the DPHY timing parameters
+on stream_on. This replaces the hard-coded 999 MHz value currently being
+used. As a fallback, revert to the original 999 Mhz link frequency.
+
+As a drive-by, fix a 80-character line formatting error.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 63 +++++++++++++++++--
+ 1 file changed, 58 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -780,7 +780,8 @@ static void cfe_start_channel(struct cfe
+                       __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);
++              source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
++                                                      cfe->fe_csi2_channel);
+               fmt = find_format_by_code(source_fmt->code);
+               width = source_fmt->width;
+@@ -982,6 +983,59 @@ static void cfe_buffer_queue(struct vb2_
+       spin_unlock_irqrestore(&cfe->state_lock, flags);
+ }
++static u64 sensor_link_frequency(struct cfe_device *cfe)
++{
++      struct v4l2_mbus_framefmt *source_fmt;
++      struct v4l2_subdev_state *state;
++      struct media_entity *entity;
++      struct v4l2_subdev *subdev;
++      const struct cfe_fmt *fmt;
++      struct media_pad *pad;
++      s64 link_freq;
++
++      state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
++      source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, 0);
++      fmt = find_format_by_code(source_fmt->code);
++      v4l2_subdev_unlock_state(state);
++
++      /*
++       * Walk up the media graph to find either the sensor entity, or another
++       * entity that advertises the V4L2_CID_LINK_FREQ or V4L2_CID_PIXEL_RATE
++       * control through the subdev.
++       */
++      entity = &cfe->csi2.sd.entity;
++      while (1) {
++              pad = &entity->pads[0];
++              if (!(pad->flags & MEDIA_PAD_FL_SINK))
++                      goto err;
++
++              pad = media_pad_remote_pad_first(pad);
++              if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++                      goto err;
++
++              entity = pad->entity;
++              subdev = media_entity_to_v4l2_subdev(entity);
++              if (entity->function == MEDIA_ENT_F_CAM_SENSOR ||
++                  v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ) ||
++                  v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE))
++                      break;
++      }
++
++      link_freq = v4l2_get_link_freq(subdev->ctrl_handler, fmt->depth,
++                                     cfe->csi2.active_data_lanes * 2);
++      if (link_freq < 0)
++              goto err;
++
++      /* x2 for DDR. */
++      link_freq *= 2;
++      cfe_info("Using a link frequency of %lld Hz\n", link_freq);
++      return link_freq;
++
++err:
++      cfe_err("Unable to determine sensor link frequency, using 999 MHz\n");
++      return 999 * 1000000UL;
++}
++
+ static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+ {
+       struct v4l2_mbus_config mbus_config = { 0 };
+@@ -1049,10 +1103,11 @@ static int cfe_start_streaming(struct vb
+               goto err_disable_cfe;
+       }
+-      cfe_dbg("Starting sensor streaming\n");
+-
++      cfe_dbg("Configuring CSI-2 block\n");
++      cfe->csi2.dphy.dphy_freq = sensor_link_frequency(cfe) / 1000000UL;
+       csi2_open_rx(&cfe->csi2);
++      cfe_dbg("Starting sensor streaming\n");
+       cfe->sequence = 0;
+       ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
+       if (ret < 0) {
+@@ -1945,8 +2000,6 @@ static int of_cfe_connect_subdevs(struct
+               }
+       }
+-      /* 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;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch b/target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch
new file mode 100644 (file)
index 0000000..722714d
--- /dev/null
@@ -0,0 +1,42 @@
+From fb78b2617e70e58e03e0d50e674758fdd2a80d45 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 10:39:11 +0000
+Subject: [PATCH] dts: bcm2712-rpi-5-b: Create some dummy nodes
+
+The kernel now treats multiple fragments targeting the same node as an
+error. For this reason, it is important that labels created just for
+compatibility with other systems (e.g. i2c0if and i2c0mux) are
+attached to unique nodes, not just tacked onto existing nodes.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -157,6 +157,12 @@
+       dummy: dummy {
+               // A target for unwanted overlay fragments
+       };
++
++
++      // A few extra labels to keep overlays happy
++
++      i2c0if: i2c0if {};
++      i2c0mux: i2c0mux {};
+ };
+ rp1_target: &pcie2 {
+@@ -243,11 +249,6 @@ aux: &dummy {};
+ #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";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch b/target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch
new file mode 100644 (file)
index 0000000..41ab025
--- /dev/null
@@ -0,0 +1,49 @@
+From cd66a0832351762b496bdce6f2f94a871d11484e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 13:12:55 +0000
+Subject: [PATCH] dts: rp1: Add spi6, fix spi1 #address-cells
+
+spi6 won't be useful on Pi 5 because it can't be enabled on the 40-pin
+header, but include it for completeness.
+
+Also fix the #address-cells value for spi1, otherwise the kernel will
+reject attempts to apply the, say, spi1-2cs overlay at runtime.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/rp1.dtsi | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -187,7 +187,7 @@
+                       interrupts = <RP1_INT_SPI1 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
+                       clock-names = "ssi_clk";
+-                      #address-cells = <0>;
++                      #address-cells = <1>;
+                       #size-cells = <0>;
+                       num-cs = <2>;
+                       dmas = <&rp1_dma RP1_DMA_SPI1_TX>,
+@@ -262,6 +262,21 @@
+                       dma-names = "tx", "rx";
+                       status = "disabled";
+               };
++
++              rp1_spi6: spi@68000 {
++                      reg = <0xc0 0x40068000  0x0 0x130>;
++                      compatible = "snps,dw-apb-ssi";
++                      interrupts = <RP1_INT_SPI6 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_SPI6_TX>,
++                             <&rp1_dma RP1_DMA_SPI6_RX>;
++                      dma-names = "tx", "rx";
++                      status = "disabled";
++              };
+               // SPI7 is a target/slave interface
+               rp1_spi7: spi@6c000 {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch b/target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch
new file mode 100644 (file)
index 0000000..b6e0c32
--- /dev/null
@@ -0,0 +1,67 @@
+From 17f135b742c4edb340afb365873c3a574f7e16cb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 17:05:46 +0000
+Subject: [PATCH] overlays: uart<n>-pi5: Add the pinctrl-0 property
+
+Without the pinctrl-0 property in the overlays, the UARTs may not be
+mapped correctly.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts | 1 +
+ 5 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+               target = <&uart0>;
+               frag0: __overlay__ {
+                       status = "okay";
++                      pinctrl-0 = <&uart0_pins>;
+               };
+       };
+--- a/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+               target = <&uart1>;
+               frag0: __overlay__ {
+                       status = "okay";
++                      pinctrl-0 = <&uart1_pins>;
+               };
+       };
+--- a/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+               target = <&uart2>;
+               frag0: __overlay__ {
+                       status = "okay";
++                      pinctrl-0 = <&uart2_pins>;
+               };
+       };
+--- a/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+               target = <&uart3>;
+               frag0: __overlay__ {
+                       status = "okay";
++                      pinctrl-0 = <&uart3_pins>;
+               };
+       };
+--- a/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+               target = <&uart4>;
+               frag0: __overlay__ {
+                       status = "okay";
++                      pinctrl-0 = <&uart4_pins>;
+               };
+       };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch b/target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch
new file mode 100644 (file)
index 0000000..f0a652b
--- /dev/null
@@ -0,0 +1,51 @@
+From c9a785d57c302d5f1d4de4e67fa57522e66c7882 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 6 Nov 2023 09:40:50 +0000
+Subject: [PATCH] drivers: media: imx477: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 450 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -164,6 +164,10 @@ struct imx477_mode {
+       struct imx477_reg_list reg_list;
+ };
++static const s64 imx477_link_freq_menu[] = {
++      IMX477_DEFAULT_LINK_FREQ,
++};
++
+ static const struct imx477_reg mode_common_regs[] = {
+       {0x0136, 0x18},
+       {0x0137, 0x00},
+@@ -1110,6 +1114,7 @@ struct imx477 {
+       struct v4l2_ctrl_handler ctrl_handler;
+       /* V4L2 Controls */
+       struct v4l2_ctrl *pixel_rate;
++      struct v4l2_ctrl *link_freq;
+       struct v4l2_ctrl *exposure;
+       struct v4l2_ctrl *vflip;
+       struct v4l2_ctrl *hflip;
+@@ -1997,6 +2002,15 @@ static int imx477_init_controls(struct i
+                                              IMX477_PIXEL_RATE, 1,
+                                              IMX477_PIXEL_RATE);
++      /* LINK_FREQ is also read only */
++      imx477->link_freq =
++              v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops,
++                                     V4L2_CID_LINK_FREQ,
++                                     ARRAY_SIZE(imx477_link_freq_menu) - 1, 0,
++                                     imx477_link_freq_menu);
++      if (imx477->link_freq)
++              imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+       /*
+        * Create the controls here, but mode specific limits are setup
+        * in the imx477_set_framing_limits() call below.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch b/target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch
new file mode 100644 (file)
index 0000000..f62fbbe
--- /dev/null
@@ -0,0 +1,24 @@
+From 46913ee0590ee0e3f607ab189be19a4d0ce785f2 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 6 Nov 2023 09:42:37 +0000
+Subject: [PATCH] drivers: media: imx477: Correctly set IMX477_PIXEL_RATE as a
+ r/o control
+
+This control is meant to be read-only, mark it as such.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2001,6 +2001,8 @@ static int imx477_init_controls(struct i
+                                              IMX477_PIXEL_RATE,
+                                              IMX477_PIXEL_RATE, 1,
+                                              IMX477_PIXEL_RATE);
++      if (imx477->pixel_rate)
++              imx477->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+       /* LINK_FREQ is also read only */
+       imx477->link_freq =
diff --git a/target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch b/target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch
new file mode 100644 (file)
index 0000000..948d79b
--- /dev/null
@@ -0,0 +1,52 @@
+From 6e9f68bba01b9c36a77b68c4b3167c317da986da Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 17:46:13 +0100
+Subject: [PATCH] drm/vc4: Correct logic on stopping an HVS channel
+
+When factoring out __vc4_hvs_stop_channel, the logic got inverted from
+       if (condition)
+         // stop channel
+to
+       if (condition)
+         goto out
+       //stop channel
+       out:
+and also changed the exact register writes used to stop the channel.
+
+Correct the logic so that the channel is actually stopped, and revert
+to the original register writes.
+
+Fixes: 6d01a106b4c8 ("drm/vc4: crtc: Move HVS init and close to a function")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -992,13 +992,11 @@ static void __vc4_hvs_stop_channel(struc
+       if (!drm_dev_enter(drm, &idx))
+               return;
+-      if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
++      if (!(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE))
+               goto out;
+-      HVS_WRITE(SCALER_DISPCTRLX(chan),
+-                HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
+-      HVS_WRITE(SCALER_DISPCTRLX(chan),
+-                HVS_READ(SCALER_DISPCTRLX(chan)) & ~SCALER_DISPCTRLX_ENABLE);
++      HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
++      HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
+       /* Once we leave, the scaler should be disabled and its fifo empty. */
+       WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
+@@ -1026,7 +1024,7 @@ static void __vc6_hvs_stop_channel(struc
+       if (!drm_dev_enter(drm, &idx))
+               return;
+-      if (HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB)
++      if (!(HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB))
+               goto out;
+       HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
diff --git a/target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch b/target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch
new file mode 100644 (file)
index 0000000..627f22c
--- /dev/null
@@ -0,0 +1,30 @@
+From 31c4c359aa2dbb1a7c095f0a6ef4e13cd46cfd14 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 18:05:09 +0100
+Subject: [PATCH] drm/vc4: Drop WARN for HVS FIFOs not being empty
+
+The reset condition for the EMPTY flag in DISPSTATx is 0,
+so seeing as we've just reset the pipeline there is no
+guarantee that the flag will denote empty if it hasn't been
+enabled.
+
+Drop the WARN.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1005,10 +1005,6 @@ static void __vc4_hvs_stop_channel(struc
+                                  SCALER_DISPSTATX_MODE) !=
+                    SCALER_DISPSTATX_MODE_DISABLED);
+-      WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
+-                    (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+-                   SCALER_DISPSTATX_EMPTY);
+-
+ out:
+       drm_dev_exit(idx);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch b/target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch
new file mode 100644 (file)
index 0000000..ce03224
--- /dev/null
@@ -0,0 +1,85 @@
+From 8b7078d1bbd8bb548cc97d5214adb828e9f0037c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 18:23:31 +0100
+Subject: [PATCH] drm/vc4: Free all stale dlists if channel is disabled
+
+The code handling freeing stale dlists had 2 issues:
+- it disabled the interrupt as soon as the first EOF interrupt
+  occurred, even if it didn't clear all stale allocations, thus
+  leading to stale entries
+- It didn't free stale entries from disabled channels, so eg
+  "kmstest -c 0" could leave a stale alloc on channel 1 floating
+  around.
+
+Keep the interrupt enabled whilst there are any outstanding
+allocs, and discard those on disabled channels. This second
+channel does require us to call vc4_hvs_stop_channel from
+vc4_crtc_atomic_disable so that the channel actually gets stopped.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c |  2 ++
+ drivers/gpu/drm/vc4/vc4_hvs.c  | 27 +++++++++++++++++++++++++--
+ 2 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -660,6 +660,8 @@ static void vc4_crtc_atomic_disable(stru
+       vc4_crtc_disable(crtc, encoder, state, old_vc4_state->assigned_channel);
++      vc4_hvs_atomic_disable(crtc, state);
++
+       /*
+        * Make sure we issue a vblank event after disabling the CRTC if
+        * someone was waiting it.
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -697,7 +697,8 @@ static void vc4_hvs_schedule_dlist_sweep
+       if (!list_empty(&hvs->stale_dlist_entries))
+               queue_work(system_unbound_wq, &hvs->free_dlist_work);
+-      vc4_hvs_irq_clear_eof(hvs, channel);
++      if (list_empty(&hvs->stale_dlist_entries))
++              vc4_hvs_irq_clear_eof(hvs, channel);
+       spin_unlock_irqrestore(&hvs->mm_lock, flags);
+ }
+@@ -712,6 +713,27 @@ static bool vc4_hvs_frcnt_lte(u8 cnt1, u
+       return (s8)((cnt1 << 2) - (cnt2 << 2)) <= 0;
+ }
++bool vc4_hvs_check_channel_active(struct vc4_hvs *hvs, unsigned int fifo)
++{
++      struct vc4_dev *vc4 = hvs->vc4;
++      struct drm_device *drm = &vc4->base;
++      bool enabled = false;
++      int idx;
++
++      WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
++      if (!drm_dev_enter(drm, &idx))
++              return 0;
++
++      if (vc4->gen >= VC4_GEN_6)
++              enabled = HVS_READ(SCALER6_DISPX_CTRL0(fifo)) & SCALER6_DISPX_CTRL0_ENB;
++      else
++              enabled = HVS_READ(SCALER_DISPCTRLX(fifo)) & SCALER_DISPCTRLX_ENABLE;
++
++      drm_dev_exit(idx);
++      return enabled;
++}
++
+ /*
+  * Some atomic commits (legacy cursor updates, mostly) will not wait for
+  * the next vblank and will just return once the commit has been pushed
+@@ -746,7 +768,8 @@ static void vc4_hvs_dlist_free_work(stru
+               u8 frcnt;
+               frcnt = vc4_hvs_get_fifo_frame_count(hvs, cur->channel);
+-              if (!vc4_hvs_frcnt_lte(cur->target_frame_count, frcnt))
++              if (vc4_hvs_check_channel_active(hvs, cur->channel) &&
++                  !vc4_hvs_frcnt_lte(cur->target_frame_count, frcnt))
+                       continue;
+               vc4_hvs_free_dlist_entry_locked(hvs, cur);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch b/target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch
new file mode 100644 (file)
index 0000000..b16489a
--- /dev/null
@@ -0,0 +1,64 @@
+From 665e9810340abc37769b317445907bac1843dd64 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 27 Oct 2023 16:46:04 +0100
+Subject: [PATCH] drm/vc4: Add hvs_dlist_allocs debugfs function.
+
+Users are reporting running out of DLIST memory. Add a
+debugfs file to dump out all the allocations.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -347,6 +347,36 @@ static int vc5_hvs_debugfs_gamma(struct
+       return 0;
+ }
++static int vc4_hvs_debugfs_dlist_allocs(struct seq_file *m, void *data)
++{
++      struct drm_info_node *node = m->private;
++      struct drm_device *dev = node->minor->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++      struct vc4_hvs *hvs = vc4->hvs;
++      struct drm_printer p = drm_seq_file_printer(m);
++      struct vc4_hvs_dlist_allocation *cur, *next;
++      struct drm_mm_node *mm_node;
++      unsigned long flags;
++
++      spin_lock_irqsave(&hvs->mm_lock, flags);
++
++      drm_printf(&p, "Allocated nodes:\n");
++      list_for_each_entry(mm_node, drm_mm_nodes(&hvs->dlist_mm), node_list) {
++              drm_printf(&p, "node [%08llx + %08llx]\n", mm_node->start, mm_node->size);
++      }
++
++      drm_printf(&p, "Stale nodes:\n");
++      list_for_each_entry_safe(cur, next, &hvs->stale_dlist_entries, node) {
++              drm_printf(&p, "node [%08llx + %08llx] channel %u frcnt %u\n",
++                         cur->mm_node.start, cur->mm_node.size, cur->channel,
++                         cur->target_frame_count);
++      }
++
++      spin_unlock_irqrestore(&hvs->mm_lock, flags);
++
++      return 0;
++}
++
+ /* The filter kernel is composed of dwords each containing 3 9-bit
+  * signed integers packed next to each other.
+  */
+@@ -1602,6 +1632,11 @@ int vc4_hvs_debugfs_init(struct drm_mino
+       if (ret)
+               return ret;
++      ret = vc4_debugfs_add_file(minor, "hvs_dlist_allocs",
++                                 vc4_hvs_debugfs_dlist_allocs, NULL);
++      if (ret)
++              return ret;
++
+       ret = vc4_debugfs_add_regset32(minor, "hvs_regs",
+                                      &hvs->regset);
+       if (ret)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch b/target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch
new file mode 100644 (file)
index 0000000..aa38b18
--- /dev/null
@@ -0,0 +1,23 @@
+From 23ea21ef5f6efb3082c184843b35a2f8f2e4374c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Oct 2023 11:15:38 +0000
+Subject: [PATCH] drm/vc4: Log the size of the dlist allocation that was
+ attempted
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -659,7 +659,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+                                dlist_count);
+       spin_unlock_irqrestore(&hvs->mm_lock, flags);
+       if (ret) {
+-              drm_err(dev, "Failed to allocate DLIST entry: %d\n", ret);
++              drm_err(dev, "Failed to allocate DLIST entry. Requested size=%zu. ret=%d\n",
++                      dlist_count, ret);
+               return ERR_PTR(ret);
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch b/target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch
new file mode 100644 (file)
index 0000000..a72e3b8
--- /dev/null
@@ -0,0 +1,104 @@
+From f9f480b04f1dc280bd4411477f5ee7336361367b Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 24 Oct 2023 16:20:42 +0100
+Subject: [PATCH] drm/vc4: crtc: Support odd horizontal timings on BCM2712
+
+BCM2711 runs pixelvalve at two pixels per clock cycle which results
+in an unfortunate limitation that odd horizontal timings are not
+possible. This is apparent on the standard DMT mode of 1366x768@60
+which cannot be driven with correct timing.
+
+BCM2712 defaults to the same behaviour, but has a mode to support
+odd timings. While internally it still runs at two pixels per clock,
+setting the PV_VCONTROL_ODD_TIMING bit makes it appear externally
+to behave as it is one pixel per clock.
+
+Switching to this mode fixes 1366x768@60 mode, and other custom
+resultions with odd horizontal timings.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 12 ++++--------
+ drivers/gpu/drm/vc4/vc4_hdmi.c |  4 ++--
+ drivers/gpu/drm/vc4/vc4_regs.h |  1 +
+ 3 files changed, 7 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -398,12 +398,6 @@ static void vc4_crtc_config_pv(struct dr
+       vc4_crtc_pixelvalve_reset(crtc);
+-      /*
+-       * NOTE: The BCM2712 has a H_OTE (Horizontal Odd Timing Enable)
+-       * bit that, when set, will allow to specify the timings in
+-       * pixels instead of cycles, thus allowing to specify odd
+-       * timings.
+-       */
+       CRTC_WRITE(PV_HORZA,
+                  VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+                                PV_HORZA_HBP) |
+@@ -448,6 +442,7 @@ static void vc4_crtc_config_pv(struct dr
+                */
+               CRTC_WRITE(PV_V_CONTROL,
+                          PV_VCONTROL_CONTINUOUS |
++                         (vc4->gen >= VC4_GEN_6 ? PV_VCONTROL_ODD_TIMING : 0) |
+                          (is_dsi ? PV_VCONTROL_DSI : 0) |
+                          PV_VCONTROL_INTERLACE |
+                          (odd_field_first
+@@ -459,6 +454,7 @@ static void vc4_crtc_config_pv(struct dr
+       } else {
+               CRTC_WRITE(PV_V_CONTROL,
+                          PV_VCONTROL_CONTINUOUS |
++                         (vc4->gen >= VC4_GEN_6 ? PV_VCONTROL_ODD_TIMING : 0) |
+                          (is_dsi ? PV_VCONTROL_DSI : 0));
+               CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+       }
+@@ -1334,7 +1330,7 @@ const struct vc4_pv_data bcm2712_pv0_dat
+               .hvs_output = 0,
+       },
+       .fifo_depth = 64,
+-      .pixels_per_clock = 2,
++      .pixels_per_clock = 1,
+       .encoder_types = {
+               [0] = VC4_ENCODER_TYPE_HDMI0,
+       },
+@@ -1347,7 +1343,7 @@ const struct vc4_pv_data bcm2712_pv1_dat
+               .hvs_output = 1,
+       },
+       .fifo_depth = 64,
+-      .pixels_per_clock = 2,
++      .pixels_per_clock = 1,
+       .encoder_types = {
+               [0] = VC4_ENCODER_TYPE_HDMI1,
+       },
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -3958,7 +3958,7 @@ static const struct vc4_hdmi_variant bcm
+               PHY_LANE_2,
+               PHY_LANE_CK,
+       },
+-      .unsupported_odd_h_timings      = true,
++      .unsupported_odd_h_timings      = false,
+       .external_irq_controller        = true,
+       .init_resources         = vc5_hdmi_init_resources,
+@@ -3985,7 +3985,7 @@ static const struct vc4_hdmi_variant bcm
+               PHY_LANE_2,
+               PHY_LANE_CK,
+       },
+-      .unsupported_odd_h_timings      = true,
++      .unsupported_odd_h_timings      = false,
+       .external_irq_controller        = true,
+       .init_resources         = vc5_hdmi_init_resources,
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -155,6 +155,7 @@
+ # define PV_CONTROL_EN                                BIT(0)
+ #define PV_V_CONTROL                          0x04
++# define PV_VCONTROL_ODD_TIMING                       BIT(29)
+ # define PV_VCONTROL_ODD_DELAY_MASK           VC4_MASK(22, 6)
+ # define PV_VCONTROL_ODD_DELAY_SHIFT          6
+ # define PV_VCONTROL_ODD_FIRST                        BIT(5)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch b/target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch
new file mode 100644 (file)
index 0000000..caf8b1a
--- /dev/null
@@ -0,0 +1,41 @@
+From 686fe776309fba5cad642c40177d39bf1fb320b2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 7 Nov 2023 14:49:47 +0000
+Subject: [PATCH] spi: dw-dma: Get the last DMA scoop out of the FIFO
+
+With a DMA FIFO threshold greater than 1 (encoded as 0), it is possible
+for data in the FIFO to be inaccessible, causing the transfer to fail
+after a timeout. If the transfer includes a transmission, reduce the
+RX threshold when the TX completes, otherwise use 1 for the whole
+transfer (inefficient, but not catastrophic at SPI data rates).
+
+See: https://github.com/raspberrypi/linux/issues/5696
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-dw-dma.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-dw-dma.c
++++ b/drivers/spi/spi-dw-dma.c
+@@ -275,8 +275,10 @@ static void dw_spi_dma_tx_done(void *arg
+       struct dw_spi *dws = arg;
+       clear_bit(DW_SPI_TX_BUSY, &dws->dma_chan_busy);
+-      if (test_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy))
++      if (test_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy)) {
++              dw_writel(dws, DW_SPI_DMARDLR, 0);
+               return;
++      }
+       complete(&dws->dma_completion);
+ }
+@@ -602,6 +604,8 @@ static int dw_spi_dma_transfer(struct dw
+       nents = max(xfer->tx_sg.nents, xfer->rx_sg.nents);
++      dw_writel(dws, DW_SPI_DMARDLR, xfer->tx_buf ? (dws->rxburst - 1) : 0);
++
+       /*
+        * Execute normal DMA-based transfer (which submits the Rx and Tx SG
+        * lists directly to the DMA engine at once) if either full hardware
diff --git a/target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch b/target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch
new file mode 100644 (file)
index 0000000..2ab224d
--- /dev/null
@@ -0,0 +1,62 @@
+From 4d2261fe86ce08bbee3c000718000e9f86593d88 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 11:52:16 +0000
+Subject: [PATCH] drivers: mmc: sdhci: add SPURIOUS_INT_RESP quirk
+
+Certain controllers (dwc-mshc) generate timeout conditions separately to
+command-completion conditions, where the end result is interrupts are
+separated in time depending on the current SDCLK frequency.
+
+This causes spurious interrupts if SDCLK is slow compared to the CPU's
+ability to process and return from interrupt. This occurs during card
+probe with an empty slot where all commands that would generate a
+response time out.
+
+Add a quirk to squelch command response interrupts when a command
+timeout interrupt is received.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci.c | 11 +++++++++++
+ drivers/mmc/host/sdhci.h |  3 +++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -1728,6 +1728,12 @@ static bool sdhci_send_command(struct sd
+       if (host->use_external_dma)
+               sdhci_external_dma_pre_transfer(host, cmd);
++      if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_INT_RESP) {
++              host->ier |= SDHCI_INT_RESPONSE;
++              sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++              sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
++      }
++
+       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+       return true;
+@@ -3330,6 +3336,11 @@ static void sdhci_cmd_irq(struct sdhci_h
+               if (intmask & SDHCI_INT_TIMEOUT) {
+                       host->cmd->error = -ETIMEDOUT;
+                       sdhci_err_stats_inc(host, CMD_TIMEOUT);
++                      if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_INT_RESP) {
++                              host->ier &= ~SDHCI_INT_RESPONSE;
++                              sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++                              sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
++                      }
+               } else {
+                       host->cmd->error = -EILSEQ;
+                       if (!mmc_op_tuning(host->cmd->opcode))
+--- a/drivers/mmc/host/sdhci.h
++++ b/drivers/mmc/host/sdhci.h
+@@ -486,6 +486,9 @@ struct sdhci_host {
+ #define SDHCI_QUIRK2_NO_SDR50  (1<<20)
+ #define SDHCI_QUIRK2_NO_SDR104        (1<<21)
++/* Command timeouts may generate a trailing INT_RESPONSE later */
++#define SDHCI_QUIRK2_SPURIOUS_INT_RESP                        (1<<31)
++
+       int irq;                /* Device IRQ */
+       void __iomem *ioaddr;   /* Mapped address */
+       phys_addr_t mapbase;    /* physical address base */
diff --git a/target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch b/target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch
new file mode 100644 (file)
index 0000000..ffdc1b1
--- /dev/null
@@ -0,0 +1,42 @@
+From ebe13d0d4314255d226ba740e37a14172a8b9091 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:10:13 +0000
+Subject: [PATCH] dt-bindings: mmc: sdhci-of-dwcmhsc: Add Raspberry Pi RP1
+ support
+
+The DWC MSHC controller on RP1 needs differentiating from the generic
+version.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ .../devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml          | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
++++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
+@@ -16,6 +16,7 @@ allOf:
+ properties:
+   compatible:
+     enum:
++      - raspberrypi,rp1-dwcmshc
+       - rockchip,rk3568-dwcmshc
+       - rockchip,rk3588-dwcmshc
+       - snps,dwcmshc-sdhci
+@@ -34,6 +35,8 @@ properties:
+       - description: axi clock for rockchip specified
+       - description: block clock for rockchip specified
+       - description: timer clock for rockchip specified
++      - description: timeout clock for rp1 specified
++      - description: sdio clock generator for rp1 specified
+   clock-names:
+@@ -44,6 +47,8 @@ properties:
+       - const: axi
+       - const: block
+       - const: timer
++      - const: timeout
++      - const: sdio
+   rockchip,txclk-tapnum:
+     description: Specify the number of delay for tx sampling.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch b/target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch
new file mode 100644 (file)
index 0000000..54b719c
--- /dev/null
@@ -0,0 +1,41 @@
+From 80dd8795ca631ac692fd3079487aea6d934a829c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:12:59 +0000
+Subject: [PATCH] drivers: mmc: sdhci-of-dwcmshc: add RP1 dt ID and quirks
+
+Differentiate the RP1 variant of the Designware MSHC controller(s).
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -373,6 +373,15 @@ static const struct sdhci_pltfm_data sdh
+ };
+ #endif
++static const struct sdhci_pltfm_data sdhci_dwcmshc_rp1_pdata = {
++      .ops = &sdhci_dwcmshc_ops,
++      .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
++                SDHCI_QUIRK_BROKEN_CARD_DETECTION,
++      .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
++                 SDHCI_QUIRK2_BROKEN_HS200 |
++                 SDHCI_QUIRK2_SPURIOUS_INT_RESP,
++};
++
+ static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
+       .ops = &sdhci_dwcmshc_rk35xx_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+@@ -441,6 +450,10 @@ static void dwcmshc_rk35xx_postinit(stru
+ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
+       {
++              .compatible = "raspberrypi,rp1-dwcmshc",
++              .data = &sdhci_dwcmshc_rp1_pdata,
++      },
++      {
+               .compatible = "rockchip,rk3588-dwcmshc",
+               .data = &sdhci_dwcmshc_rk35xx_pdata,
+       },
diff --git a/target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch b/target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch
new file mode 100644 (file)
index 0000000..c708bf1
--- /dev/null
@@ -0,0 +1,104 @@
+From 51cdff455e3c3df29764f71bc0c9dd0e099945d6 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:14:25 +0000
+Subject: [PATCH] arm: dts: change RP1 SDHCI controller compatible string
+
+Also add a sdio-pi5 overlay which enables mmc0 on GPIOs 22-27, as was
+possible with earlier models of Pi.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  7 ++++++
+ arch/arm/boot/dts/overlays/overlay_map.dts    |  4 ++++
+ .../boot/dts/overlays/sdio-pi5-overlay.dts    | 24 +++++++++++++++++++
+ arch/arm/boot/dts/rp1.dtsi                    |  4 ++--
+ 5 files changed, 38 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/sdio-pi5-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -225,6 +225,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+       sc16is752-spi1.dtbo \
+       sdhost.dtbo \
+       sdio.dtbo \
++      sdio-pi5.dtbo \
+       seeed-can-fd-hat-v1.dtbo \
+       seeed-can-fd-hat-v2.dtbo \
+       sh1106-spi.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3932,6 +3932,13 @@ Info:   This overlay is now deprecated.
+ Load:   <Deprecated>
++Name:   sdio-pi5
++Info:   Selects the rp1_mmc0 interface and enables it on GPIOs 22-27.
++        Pi 5 only.
++Load:   dtoverlay=sdio-pi5
++Params: <None>
++
++
+ Name:   sdtweak
+ Info:   This overlay is now deprecated. Use the sd_* dtparams in the
+         base DTB, e.g. "dtoverlay=sdtweak,poll_once" becomes
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -250,6 +250,10 @@
+               deprecated = "use sdio,bus_width=1,gpios_22_25";
+       };
++      sdio-pi5 {
++              bcm2712;
++      };
++
+       sdtweak {
+               deprecated = "use 'dtparam=sd_poll_once' etc.";
+       };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/sdio-pi5-overlay.dts
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++
++/* SDIO/SD/MMC on RP1 bank 0 */
++
++/{
++      compatible = "brcm,bcm2712";
++
++      fragment@0 {
++              target = <&rp1_mmc0>;
++              frag0: __overlay__ {
++                      status = "okay";
++                      pinctrl-0 = <&rp1_sdio0_22_27>;
++                      pinctrl-names = "default";
++              };
++      };
++
++      fragment@1 {
++              target = <&rp1_sdio_clk0>;
++              frag1: __overlay__ {
++                      status = "okay";
++              };
++      };
++};
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -962,7 +962,7 @@
+               rp1_mmc0: mmc@180000 {
+                       reg = <0xc0 0x40180000  0x0 0x100>;
+-                      compatible = "snps,dwcmshc-sdhci";
++                      compatible = "raspberrypi,rp1-dwcmshc";
+                       interrupts = <RP1_INT_SDIO0 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
+                                 &rp1_clocks RP1_CLK_SDIO_TIMER
+@@ -978,7 +978,7 @@
+               rp1_mmc1: mmc@184000 {
+                       reg = <0xc0 0x40184000  0x0 0x100>;
+-                      compatible = "snps,dwcmshc-sdhci";
++                      compatible = "raspberrypi,rp1-dwcmshc";
+                       interrupts = <RP1_INT_SDIO1 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
+                                 &rp1_clocks RP1_CLK_SDIO_TIMER
diff --git a/target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch b/target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch
new file mode 100644 (file)
index 0000000..aa02512
--- /dev/null
@@ -0,0 +1,22 @@
+From 020ee5029ab0b11e47696f538418105ccfdb44de Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Nov 2023 19:17:33 +0000
+Subject: [PATCH] ASoC: bcm: audioinjector_octo: Add soundcard "owner"
+
+See: https://github.com/raspberrypi/linux/issues/5697
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/audioinjector-octo-soundcard.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/sound/soc/bcm/audioinjector-octo-soundcard.c
++++ b/sound/soc/bcm/audioinjector-octo-soundcard.c
+@@ -252,6 +252,7 @@ static const struct snd_soc_dapm_route a
+ static struct snd_soc_card snd_soc_audioinjector_octo = {
+       .name = "audioinjector-octo-soundcard",
++      .owner = THIS_MODULE,
+       .dai_link = audioinjector_octo_dai,
+       .num_links = ARRAY_SIZE(audioinjector_octo_dai),
diff --git a/target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch b/target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch
new file mode 100644 (file)
index 0000000..60f489c
--- /dev/null
@@ -0,0 +1,139 @@
+From f364e0eb8f973e1aa24a3c451d18e84247a8efcd Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 8 Nov 2023 10:57:45 +0000
+Subject: [PATCH] drivers: media: imx708: Adjust broken line correction
+ parameter
+
+In full-resolution mode, the LPF_INTENSITY_EN and LPF_INTENSITY
+registers control Quad Bayer Re-mosaic broken line correction.
+Expose this as a module parameter "qbc_adjust": zero disables
+the correction and values in the range 2 to 5 set its strength.
+
+There is a trade-off between coloured and monochrome patterns.
+The previous fixed value 4 could produce ladder/spots artefacts
+in coloured textures. The new default value 2 may suit a wider
+range of scenes.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/media/i2c/imx708.c | 50 ++++++++++++++++++++++++++++++++------
+ 1 file changed, 42 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/i2c/imx708.c
++++ b/drivers/media/i2c/imx708.c
+@@ -20,6 +20,14 @@
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-mediabus.h>
++/*
++ * Parameter to adjust Quad Bayer re-mosaic broken line correction
++ * strength, used in full-resolution mode only. Set zero to disable.
++ */
++static int qbc_adjust = 2;
++module_param(qbc_adjust, int, 0644);
++MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5]");
++
+ #define IMX708_REG_VALUE_08BIT                1
+ #define IMX708_REG_VALUE_16BIT                2
+@@ -99,11 +107,17 @@
+ /* HDR exposure ratio (long:med == med:short) */
+ #define IMX708_HDR_EXPOSURE_RATIO       4
+-#define IMX708_REG_MID_EXPOSURE       0x3116
+-#define IMX708_REG_SHT_EXPOSURE       0x0224
++#define IMX708_REG_MID_EXPOSURE               0x3116
++#define IMX708_REG_SHT_EXPOSURE               0x0224
+ #define IMX708_REG_MID_ANALOG_GAIN    0x3118
+ #define IMX708_REG_SHT_ANALOG_GAIN    0x0216
++/* QBC Re-mosaic broken line correction registers */
++#define IMX708_LPF_INTENSITY_EN               0xC428
++#define IMX708_LPF_INTENSITY_ENABLED  0x00
++#define IMX708_LPF_INTENSITY_DISABLED 0x01
++#define IMX708_LPF_INTENSITY          0xC429
++
+ /*
+  * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12).
+  * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels)
+@@ -171,6 +185,9 @@ struct imx708_mode {
+       /* HDR flag, used for checking if the current mode is HDR */
+       bool hdr;
++
++      /* Quad Bayer Re-mosaic flag */
++      bool remosaic;
+ };
+ /* Default PDAF pixel correction gains */
+@@ -363,8 +380,6 @@ static const struct imx708_reg mode_4608
+       {0x341f, 0x20},
+       {0x3420, 0x00},
+       {0x3421, 0xd8},
+-      {0xC428, 0x00},
+-      {0xC429, 0x04},
+       {0x3366, 0x00},
+       {0x3367, 0x00},
+       {0x3368, 0x00},
+@@ -677,7 +692,8 @@ static const struct imx708_mode supporte
+               .pixel_rate = 595200000,
+               .exposure_lines_min = 8,
+               .exposure_lines_step = 1,
+-              .hdr = false
++              .hdr = false,
++              .remosaic = true
+       },
+       {
+               /* regular 2x2 binned. */
+@@ -699,7 +715,8 @@ static const struct imx708_mode supporte
+               .pixel_rate = 585600000,
+               .exposure_lines_min = 4,
+               .exposure_lines_step = 2,
+-              .hdr = false
++              .hdr = false,
++              .remosaic = false
+       },
+       {
+               /* 2x2 binned and cropped for 720p. */
+@@ -721,7 +738,8 @@ static const struct imx708_mode supporte
+               .pixel_rate = 566400000,
+               .exposure_lines_min = 4,
+               .exposure_lines_step = 2,
+-              .hdr = false
++              .hdr = false,
++              .remosaic = false
+       },
+ };
+@@ -746,7 +764,8 @@ static const struct imx708_mode supporte
+               .pixel_rate = 777600000,
+               .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO,
+               .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO,
+-              .hdr = true
++              .hdr = true,
++              .remosaic = false
+       }
+ };
+@@ -1515,6 +1534,21 @@ static int imx708_start_streaming(struct
+               return ret;
+       }
++      /* Quad Bayer re-mosaic adjustments (for full-resolution mode only) */
++      if (imx708->mode->remosaic && qbc_adjust > 0) {
++              imx708_write_reg(imx708, IMX708_LPF_INTENSITY,
++                               IMX708_REG_VALUE_08BIT, qbc_adjust);
++              imx708_write_reg(imx708,
++                               IMX708_LPF_INTENSITY_EN,
++                               IMX708_REG_VALUE_08BIT,
++                               IMX708_LPF_INTENSITY_ENABLED);
++      } else {
++              imx708_write_reg(imx708,
++                               IMX708_LPF_INTENSITY_EN,
++                               IMX708_REG_VALUE_08BIT,
++                               IMX708_LPF_INTENSITY_DISABLED);
++      }
++
+       /* Apply customized values from user */
+       ret =  __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler);
+       if (ret)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch b/target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch
new file mode 100644 (file)
index 0000000..ea0ea39
--- /dev/null
@@ -0,0 +1,96 @@
+From 65407c54fb4119e528b70b329448269657e0941e Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 8 Nov 2023 10:05:05 +0000
+Subject: [PATCH] drivers: media: cfe: Don't confuse MHz and Mbps
+
+The driver was interchaning these units when talking about link rate.
+Fix this to avoid confusion. Apart from the logging message change,
+there is no function change in this commit.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c  |  8 ++++----
+ drivers/media/platform/raspberrypi/rp1_cfe/dphy.c | 10 +++++-----
+ drivers/media/platform/raspberrypi/rp1_cfe/dphy.h |  2 +-
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -983,7 +983,7 @@ static void cfe_buffer_queue(struct vb2_
+       spin_unlock_irqrestore(&cfe->state_lock, flags);
+ }
+-static u64 sensor_link_frequency(struct cfe_device *cfe)
++static u64 sensor_link_rate(struct cfe_device *cfe)
+ {
+       struct v4l2_mbus_framefmt *source_fmt;
+       struct v4l2_subdev_state *state;
+@@ -1028,11 +1028,11 @@ static u64 sensor_link_frequency(struct
+       /* x2 for DDR. */
+       link_freq *= 2;
+-      cfe_info("Using a link frequency of %lld Hz\n", link_freq);
++      cfe_info("Using a link rate of %lld Mbps\n", link_freq / (1000 * 1000));
+       return link_freq;
+ err:
+-      cfe_err("Unable to determine sensor link frequency, using 999 MHz\n");
++      cfe_err("Unable to determine sensor link rate, using 999 Mbps\n");
+       return 999 * 1000000UL;
+ }
+@@ -1104,7 +1104,7 @@ static int cfe_start_streaming(struct vb
+       }
+       cfe_dbg("Configuring CSI-2 block\n");
+-      cfe->csi2.dphy.dphy_freq = sensor_link_frequency(cfe) / 1000000UL;
++      cfe->csi2.dphy.dphy_rate = sensor_link_rate(cfe) / 1000000UL;
+       csi2_open_rx(&cfe->csi2);
+       cfe_dbg("Starting sensor streaming\n");
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+@@ -96,7 +96,7 @@ static uint8_t dphy_transaction(struct d
+       return get_tstdout(dphy);
+ }
+-static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t freq_mhz)
++static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps)
+ {
+       /* See Table 5-1 on page 65 of dphy databook */
+       static const u16 hsfreqrange_table[][2] = {
+@@ -116,11 +116,11 @@ static void dphy_set_hsfreqrange(struct
+       };
+       unsigned int i;
+-      if (freq_mhz < 80 || freq_mhz > 1500)
+-              dphy_err("DPHY: Frequency %u MHz out of range\n", freq_mhz);
++      if (mbps < 80 || mbps > 1500)
++              dphy_err("DPHY: Datarate %u Mbps out of range\n", mbps);
+       for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
+-              if (freq_mhz <= hsfreqrange_table[i][0])
++              if (mbps <= hsfreqrange_table[i][0])
+                       break;
+       }
+@@ -139,7 +139,7 @@ static void dphy_init(struct dphy_data *
+       set_tstclr(dphy, 0);
+       usleep_range(15, 20);
+-      dphy_set_hsfreqrange(dphy, dphy->dphy_freq);
++      dphy_set_hsfreqrange(dphy, dphy->dphy_rate);
+       usleep_range(5, 10);
+       dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1);
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+@@ -15,7 +15,7 @@ struct dphy_data {
+       void __iomem *base;
+-      u32 dphy_freq;
++      u32 dphy_rate;
+       u32 num_lanes;
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch b/target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch
new file mode 100644 (file)
index 0000000..1b6d204
--- /dev/null
@@ -0,0 +1,25 @@
+From 6137fb168c08bd8c41c8421bf26f09ed29479f08 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 15 Nov 2023 08:25:11 +0000
+Subject: [PATCH] overlays: imx296: Fix cam port override for regulators
+
+The override was missing/incorrect for the regulator labels.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx296-overlay.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -97,8 +97,9 @@
+               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>,
+                      <&imx296>, "clocks:0=",<&cam0_clk>,
+-                     <&imx296>, "VANA-supply:0=",<&cam0_reg>;
++                     <&imx296>, "avdd-supply:0=",<&cam0_reg>;
+               clock-frequency = <&clk_over>, "clock-frequency:0";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch b/target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch
new file mode 100644 (file)
index 0000000..2801798
--- /dev/null
@@ -0,0 +1,32 @@
+From 7443b602cb503b42dd0ae8e957e26decb420d632 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 10:15:15 +0000
+Subject: [PATCH] overlays: ov5647: Regularise vcm node label name
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -15,7 +15,7 @@
+                       #include "ov5647.dtsi"
+-                      vcm: ad5398@c {
++                      vcm_node: ad5398@c {
+                               compatible = "adi,ad5398";
+                               reg = <0x0c>;
+                               status = "disabled";
+@@ -78,8 +78,8 @@
+                      <&clk_frag>, "target:0=",<&cam0_clk>,
+                      <&cam_node>, "clocks:0=",<&cam0_clk>,
+                      <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
+-              vcm = <&vcm>, "status=okay",
+-                    <&cam_node>,"lens-focus:0=", <&vcm>;
++              vcm = <&vcm_node>, "status=okay",
++                     <&cam_node>,"lens-focus:0=", <&vcm_node>;
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch b/target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch
new file mode 100644 (file)
index 0000000..f079756
--- /dev/null
@@ -0,0 +1,27 @@
+From d484bef133af9c87d64899fc1e1d0be2a7c7785b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 10:16:10 +0000
+Subject: [PATCH] overlays: ov5647: cam0 mode should use cam0_reg
+
+When the cam0 parameter is used, the vcm should be updated to refer to
+the cam0 regulator.
+
+See: https://github.com/raspberrypi/linux/issues/5722
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -77,7 +77,8 @@
+                      <&reg_frag>, "target:0=",<&cam0_reg>,
+                      <&clk_frag>, "target:0=",<&cam0_clk>,
+                      <&cam_node>, "clocks:0=",<&cam0_clk>,
+-                     <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
++                     <&cam_node>, "avdd-supply:0=",<&cam0_reg>,
++                     <&vcm_node>, "VANA-supply:0=",<&cam0_reg>;
+               vcm = <&vcm_node>, "status=okay",
+                      <&cam_node>,"lens-focus:0=", <&vcm_node>;
+       };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch b/target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch
new file mode 100644 (file)
index 0000000..457a0e9
--- /dev/null
@@ -0,0 +1,25 @@
+From 0924b74687bd195b98f223814ff88b4227654e85 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 14:46:14 +0000
+Subject: [PATCH] w1: Disable kernel log spam
+
+See: https://forums.raspberrypi.com/viewtopic.php?p=2159344
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/w1/w1.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/w1/w1.c
++++ b/drivers/w1/w1.c
+@@ -742,8 +742,10 @@ int w1_attach_slave_device(struct w1_mas
+       atomic_set(&sl->refcnt, 1);
+       atomic_inc(&sl->master->refcnt);
+       dev->slave_count++;
++#if 0
+       dev_info(&dev->dev, "Attaching one wire slave %02x.%012llx crc %02x\n",
+                 rn->family, (unsigned long long)rn->id, rn->crc);
++#endif
+       /* slave modules need to be loaded in a context with unlocked mutex */
+       mutex_unlock(&dev->mutex);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch b/target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch
new file mode 100644 (file)
index 0000000..b8933bc
--- /dev/null
@@ -0,0 +1,21 @@
+From be8ca764e0530ec8ac18ca03c49e3cda13562d3a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:21:05 +0000
+Subject: [PATCH] include: uapi: mbus: Add a media bus format enum for 16-bit
+ mono output
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ include/uapi/linux/media-bus-format.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -95,6 +95,7 @@
+ #define MEDIA_BUS_FMT_YUYV12_2X12             0x201e
+ #define MEDIA_BUS_FMT_YVYU12_2X12             0x201f
+ #define MEDIA_BUS_FMT_Y14_1X14                        0x202d
++#define MEDIA_BUS_FMT_Y16_1X16                        0x202e
+ #define MEDIA_BUS_FMT_UYVY8_1X16              0x200f
+ #define MEDIA_BUS_FMT_VYUY8_1X16              0x2010
+ #define MEDIA_BUS_FMT_YUYV8_1X16              0x2011
diff --git a/target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch b/target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch
new file mode 100644 (file)
index 0000000..5685518
--- /dev/null
@@ -0,0 +1,73 @@
+From 9abab2e8e5cfbeae6e1b33cc3a5ed773e4e31774 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:25:39 +0000
+Subject: [PATCH] include: uapi: v4l2: Add additional pixel formats for use
+ with PiSP
+
+Add the following formats:
+
+- V4L2_PIX_FMT_RGB48/V4L2_PIX_FMT_BGR48
+  48-bit RGB where each colour sample is 16-bits.
+
+- V4L2_PIX_FMT_PISP_COMP1_MONO/V4L2_PIX_FMT_PISP_COMP2_MONO
+  16-bit to 8-bit pisp compressed monochrome pixel format.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 8 ++++++--
+ include/uapi/linux/videodev2.h       | 6 ++++++
+ 2 files changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1304,6 +1304,8 @@ static void v4l_fill_fmtdesc(struct v4l2
+       case V4L2_PIX_FMT_BGRX32:       descr = "32-bit XBGR 8-8-8-8"; break;
+       case V4L2_PIX_FMT_RGBA32:       descr = "32-bit RGBA 8-8-8-8"; break;
+       case V4L2_PIX_FMT_RGBX32:       descr = "32-bit RGBX 8-8-8-8"; break;
++      case V4L2_PIX_FMT_BGR48:        descr = "48-bit BGR 16-16-16"; break;
++      case V4L2_PIX_FMT_RGB48:        descr = "48-bit RGB 16-16-16"; break;
+       case V4L2_PIX_FMT_GREY:         descr = "8-bit Greyscale"; break;
+       case V4L2_PIX_FMT_Y4:           descr = "4-bit Greyscale"; break;
+       case V4L2_PIX_FMT_Y6:           descr = "6-bit Greyscale"; break;
+@@ -1510,11 +1512,13 @@ static void v4l_fill_fmtdesc(struct v4l2
+               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_COMP1_BGGR:
++              case V4L2_PIX_FMT_PISP_COMP1_MONO: 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;
++              case V4L2_PIX_FMT_PISP_COMP2_BGGR:
++              case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP Bayer Comp 2"; break;
+               default:
+                       if (fmt->description[0])
+                               return;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -582,6 +582,10 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_ARGB32  v4l2_fourcc('B', 'A', '2', '4') /* 32  ARGB-8-8-8-8  */
+ #define V4L2_PIX_FMT_XRGB32  v4l2_fourcc('B', 'X', '2', '4') /* 32  XRGB-8-8-8-8  */
++/* RGB formats (6 bytes per pixel) */
++#define V4L2_PIX_FMT_BGR48 v4l2_fourcc('B', 'G', 'R', '6') /* 16  BGR-16-16-16 */
++#define V4L2_PIX_FMT_RGB48 v4l2_fourcc('R', 'G', 'B', '6') /* 16  RGB-16-16-16 */
++
+ /* Grey formats */
+ #define V4L2_PIX_FMT_GREY    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
+ #define V4L2_PIX_FMT_Y4      v4l2_fourcc('Y', '0', '4', ' ') /*  4  Greyscale     */
+@@ -799,10 +803,12 @@ struct v4l2_pix_format {
+ #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_COMP1_MONO  v4l2_fourcc('P', 'C', '1', 'M')
+ #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')
++#define V4L2_PIX_FMT_PISP_COMP2_MONO  v4l2_fourcc('P', 'C', '2', 'M')
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
diff --git a/target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch b/target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch
new file mode 100644 (file)
index 0000000..607f23e
--- /dev/null
@@ -0,0 +1,52 @@
+From 88d06a674009ad5b77234537527a800e6e0e88a3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:28:55 +0000
+Subject: [PATCH] drivers: media: cfe: Add 16-bit and compressed mono format
+ support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe_fmts.h  | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -249,28 +249,34 @@ static const struct cfe_fmt formats[] =
+               .code = MEDIA_BUS_FMT_Y10_1X10,
+               .depth = 10,
+               .csi_dt = 0x2b,
+-              .remap = { V4L2_PIX_FMT_Y16 },
++              .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_Y12P,
+               .code = MEDIA_BUS_FMT_Y12_1X12,
+               .depth = 12,
+               .csi_dt = 0x2c,
+-              .remap = { V4L2_PIX_FMT_Y16 },
++              .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_Y14P,
+               .code = MEDIA_BUS_FMT_Y14_1X14,
+               .depth = 14,
+               .csi_dt = 0x2d,
+-              .remap = { V4L2_PIX_FMT_Y16 },
++              .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_Y16,
++              .code = MEDIA_BUS_FMT_Y16_1X16,
+               .depth = 16,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
+       },
+-
++      {
++              .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
++              .code = MEDIA_BUS_FMT_Y16_1X16,
++              .depth = 8,
++              .flags = CFE_FORMAT_FLAG_FE_OUT,
++      },
+       /* Embedded data format */
+       {
+               .fourcc = V4L2_META_FMT_SENSOR_DATA,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch b/target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch
new file mode 100644 (file)
index 0000000..2ea4abd
--- /dev/null
@@ -0,0 +1,72 @@
+From 2affda8d2b172aa0fd22778983d983fc9522e621 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:29:47 +0000
+Subject: [PATCH] drivers: media: pisp_be: Add mono and 48-bit RGB pixel format
+ support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../raspberrypi/pisp_be/pisp_be_formats.h     | 45 +++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -234,6 +234,24 @@ static const struct pisp_be_format suppo
+               .colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
+               .colorspace_default = V4L2_COLORSPACE_SRGB,
+       },
++      {
++              .fourcc             = V4L2_PIX_FMT_RGB48,
++              .align              = 64,
++              .bit_depth          = 48,
++              .plane_factor       = { P3(1.0) },
++              .num_planes         = 1,
++              .colorspace_mask    = V4L2_COLORSPACE_MASK_ALL_SRGB,
++              .colorspace_default = V4L2_COLORSPACE_SRGB,
++      },
++      {
++              .fourcc             = V4L2_PIX_FMT_BGR48,
++              .align              = 64,
++              .bit_depth          = 48,
++              .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,
+@@ -457,6 +475,33 @@ static const struct pisp_be_format suppo
+               .colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
+               .colorspace_default = V4L2_COLORSPACE_RAW,
+       },
++      /* Greyscale Formats */
++      {
++              .fourcc         = V4L2_PIX_FMT_GREY,
++              .bit_depth      = 8,
++              .align          = 32,
++              .num_planes     = 1,
++              .colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++              .colorspace_default = V4L2_COLORSPACE_RAW,
++      },
++      {
++              .fourcc         = V4L2_PIX_FMT_Y16,
++              .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_PISP_COMP1_MONO,
++              .bit_depth      = 8,
++              .align          = 32,
++              .plane_factor   = { P3(1.0) },
++              .num_planes     = 1,
++              .colorspace_mask    = V4L2_COLORSPACE_MASK_RAW,
++              .colorspace_default = V4L2_COLORSPACE_RAW,
++      },
+       /* Opaque BE format for HW verification. */
+       {
+               .fourcc             = V4L2_PIX_FMT_RPI_BE,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch b/target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch
new file mode 100644 (file)
index 0000000..88bb61a
--- /dev/null
@@ -0,0 +1,32 @@
+From 52545628c07be2fd1c9df598a17130d92f12da23 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 15:17:34 +0000
+Subject: [PATCH] ASoC: dwc: Remove check in set_bclk_ratio handling
+
+A check added to dw_i2s_set_bclk_ratio that the data format is
+consistent with the ratio seems reasonable but breaks when the
+ratio is changed before the format. Remove the check - it is
+unnecessary.
+
+See: https://github.com/raspberrypi/linux/issues/5724
+Fixes: 9c6694c24f26 ("ASOC: dwc: Fix 16-bit audio handling")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -427,11 +427,8 @@ static int dw_i2s_set_bclk_ratio(struct
+                                unsigned int ratio)
+ {
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+-      struct i2s_clk_config_data *config = &dev->config;
+       dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
+-      if (ratio < config->data_width * 2)
+-              return -EINVAL;
+       switch (ratio) {
+       case 32:
diff --git a/target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch b/target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch
new file mode 100644 (file)
index 0000000..30cc82b
--- /dev/null
@@ -0,0 +1,39 @@
+From 5a0aa24b8ff58ceaf98c62670156bef7f48ed32b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 21 Nov 2023 15:08:38 +0000
+Subject: [PATCH] overlays: README: Fix cut-and-paste errors
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1673,7 +1673,7 @@ Params: 24db_digital_gain       Allow ga
+                                 responsibility of the user to ensure that
+                                 the Digital volume control is set to a value
+                                 that does not result in clipping/distortion!)
+-        slave                   Force DAC+ Pro into slave mode, using Pi as
++        slave                   Force AMP100 into slave mode, using Pi as
+                                 master for bit clock and frame clock.
+         leds_off                If set to 'true' the onboard indicator LEDs
+                                 are switched off at all times.
+@@ -1713,7 +1713,7 @@ Params: 24db_digital_gain       Allow ga
+                                 responsibility of the user to ensure that
+                                 the Digital volume control is set to a value
+                                 that does not result in clipping/distortion!)
+-        slave                   Force DAC+ Pro into slave mode, using Pi as
++        slave                   Force DAC+ into slave mode, using Pi as
+                                 master for bit clock and frame clock.
+         leds_off                If set to 'true' the onboard indicator LEDs
+                                 are switched off at all times.
+@@ -1736,7 +1736,7 @@ Params: 24db_digital_gain       Allow ga
+                                 responsibility of the user to ensure that
+                                 the Digital volume control is set to a value
+                                 that does not result in clipping/distortion!)
+-        slave                   Force DAC+ Pro into slave mode, using Pi as
++        slave                   Force DAC+ADC into slave mode, using Pi as
+                                 master for bit clock and frame clock.
+         leds_off                If set to 'true' the onboard indicator LEDs
+                                 are switched off at all times.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch b/target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch
new file mode 100644 (file)
index 0000000..36565bf
--- /dev/null
@@ -0,0 +1,38 @@
+From e60fbc34aa98b3ba2c9338ad628fc8d8137e9065 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 14 Nov 2023 18:36:19 +0000
+Subject: [PATCH] media/i2c: ov7251: Switch from V4L2_CID_GAIN to
+ V4L2_CID_ANALOGUE_GAIN
+
+The mainline driver has implemented analogue gain using the control
+V4L2_CID_GAIN instead of V4L2_CID_ANALOGUE_GAIN.
+
+libcamera requires V4L2_CID_ANALOGUE_GAIN, and therefore fails.
+
+Update the driver to use V4L2_CID_ANALOGUE_GAIN.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov7251.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/ov7251.c
++++ b/drivers/media/i2c/ov7251.c
+@@ -1063,7 +1063,7 @@ static int ov7251_s_ctrl(struct v4l2_ctr
+       case V4L2_CID_EXPOSURE:
+               ret = ov7251_set_exposure(ov7251, ctrl->val);
+               break;
+-      case V4L2_CID_GAIN:
++      case V4L2_CID_ANALOGUE_GAIN:
+               ret = ov7251_set_gain(ov7251, ctrl->val);
+               break;
+       case V4L2_CID_TEST_PATTERN:
+@@ -1588,7 +1588,7 @@ static int ov7251_init_ctrls(struct ov72
+       ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+                                            V4L2_CID_EXPOSURE, 1, 32, 1, 32);
+       ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+-                                       V4L2_CID_GAIN, 16, 1023, 1, 16);
++                                       V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16);
+       v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops,
+                                    V4L2_CID_TEST_PATTERN,
+                                    ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch b/target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch
new file mode 100644 (file)
index 0000000..8cdbb51
--- /dev/null
@@ -0,0 +1,63 @@
+From 444884f7b62bfe5ef313dd1d47f81a40e695ab0b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 17 Nov 2023 14:43:44 +0000
+Subject: [PATCH] drm/vc4: Drop planes that are completely off-screen
+
+It is permitted for a plane to be configured such that none
+of it is on-screen via either negative dest rectangle X,Y
+offset, or just an offset that is greater than the crtc
+dimensions.
+
+These planes were resized via drm_atomic_helper_check_plane_state
+such that the source rectangle had a zero width or height, but
+they still created a dlist entry even though they contributed
+no pixels. In the case of vc6_plane_mode_set, that it could result
+in negative values being written into registers, which caused
+incorrect behaviour.
+
+Drop planes that result in a source width or height of 0 pixels
+to avoid the incorrect rendering.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1108,6 +1108,12 @@ static int vc4_plane_mode_set(struct drm
+       width = vc4_state->src_w[0] >> 16;
+       height = vc4_state->src_h[0] >> 16;
++      if (!width || !height) {
++              /* 0 source size probably means the plane is offscreen */
++              vc4_state->dlist_initialized = 1;
++              return 0;
++      }
++
+       /* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
+        * and 4:4:4, scl1 should be set to scl0 so both channels of
+        * the scaler do the same thing.  For YUV, the Y plane needs
+@@ -1623,6 +1629,12 @@ static int vc6_plane_mode_set(struct drm
+       width = vc4_state->src_w[0] >> 16;
+       height = vc4_state->src_h[0] >> 16;
++      if (!width || !height) {
++              /* 0 source size probably means the plane is offscreen */
++              vc4_state->dlist_initialized = 1;
++              return 0;
++      }
++
+       /* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
+        * and 4:4:4, scl1 should be set to scl0 so both channels of
+        * the scaler do the same thing.  For YUV, the Y plane needs
+@@ -1994,6 +2006,9 @@ int vc4_plane_atomic_check(struct drm_pl
+       if (ret)
+               return ret;
++      if (!vc4_state->src_w[0] || !vc4_state->src_h[0])
++              return 0;
++
+       ret = vc4_plane_allocate_lbm(new_plane_state);
+       if (ret)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch b/target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch
new file mode 100644 (file)
index 0000000..270975e
--- /dev/null
@@ -0,0 +1,29 @@
+From 91ad217f93232fb3a0b52487fec67860fb29e93a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 17 Nov 2023 14:50:11 +0000
+Subject: [PATCH] drm/bridge: display-connector: Select DRM_KMS_HELPER
+
+Commit 7cd70656d128 ("drm/bridge: display-connector: implement
+bus fmts callbacks") added use of drm_atomic_helper_bridge_*
+functions, but didn't select the dependency of DRM_KMS_HELPER.
+If nothing else selected that dependency it resulted in a
+build failure.
+
+Select the missing dependency.
+
+Fixes: 7cd70656d128 ("drm/bridge: display-connector: implement bus fmts callbacks")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/bridge/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/bridge/Kconfig
++++ b/drivers/gpu/drm/bridge/Kconfig
+@@ -67,6 +67,7 @@ config DRM_CROS_EC_ANX7688
+ config DRM_DISPLAY_CONNECTOR
+       tristate "Display connector support"
+       depends on OF
++      select DRM_KMS_HELPER
+       help
+         Driver for display connectors with support for DDC and hot-plug
+         detection. Most display controllers handle display connectors
diff --git a/target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch b/target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch
new file mode 100644 (file)
index 0000000..4935936
--- /dev/null
@@ -0,0 +1,56 @@
+From 51712a6493bf8824419f51ca7950e7d88f48b699 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 22 Nov 2023 18:36:54 +0000
+Subject: [PATCH] drm: vc4: Free the dlist alloc immediately if it never hit
+ the hw
+
+atomic_check creates a state, and allocates the dlist memory for
+it such that atomic_flush can not fail.
+
+On destroy that dlist allocation was being put in the stale list,
+even though it had never been programmed into the hardware,
+therefore doing lots of atomic_checks could consume all the dlist
+memory and fail.
+
+If the dlist has never been programmed into the hardware, then
+free it immediately.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 6 +++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -696,6 +696,7 @@ struct vc4_hvs_dlist_allocation {
+       struct drm_mm_node mm_node;
+       unsigned int channel;
+       u8 target_frame_count;
++      bool dlist_programmed;
+ };
+ struct vc4_crtc_state {
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -697,8 +697,11 @@ void vc4_hvs_mark_dlist_entry_stale(stru
+        * Kunit tests run with a mock device and we consider any hardware
+        * access a test failure. Let's free the dlist allocation right away if
+        * we're running under kunit, we won't risk a dlist corruption anyway.
++       *
++       * Likewise if the allocation was only checked and never programmed, we
++       * can destroy the allocation immediately.
+        */
+-      if (kunit_get_current_test()) {
++      if (kunit_get_current_test() || !alloc->dlist_programmed) {
+               spin_lock_irqsave(&hvs->mm_lock, flags);
+               vc4_hvs_free_dlist_entry_locked(hvs, alloc);
+               spin_unlock_irqrestore(&hvs->mm_lock, flags);
+@@ -1201,6 +1204,7 @@ static void vc4_hvs_install_dlist(struct
+               return;
+       WARN_ON(!vc4_state->mm);
++      vc4_state->mm->dlist_programmed = true;
+       if (vc4->gen >= VC4_GEN_6)
+               HVS_WRITE(SCALER6_DISPX_LPTRS(vc4_state->assigned_channel),
diff --git a/target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch b/target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch
new file mode 100644 (file)
index 0000000..131e502
--- /dev/null
@@ -0,0 +1,48 @@
+From 2a6c3115f4142e23ca10c984d7f65ac8fb901cc2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 8 Nov 2023 15:50:39 +0000
+Subject: [PATCH] input: edt-ft5x06: Include I2C details in names for the
+ devices
+
+libinput uses the input device name alone. If you have two
+identical input devices, then there is no way to differentiate
+between them, and in the case of touchscreens that means no
+way to associate them with the appropriate display device.
+
+Add the I2C bus and address to the start of the input device
+name so that the name is always unique within the system.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -67,6 +67,7 @@
+ #define TOUCH_EVENT_RESERVED          0x03
+ #define EDT_NAME_LEN                  23
++#define EDT_NAME_PREFIX_LEN           8
+ #define EDT_SWITCH_MODE_RETRIES               10
+ #define EDT_SWITCH_MODE_DELAY         5 /* msec */
+ #define EDT_RAW_DATA_RETRIES          100
+@@ -134,7 +135,7 @@ struct edt_ft5x06_ts_data {
+       int max_support_points;
+       unsigned int known_ids;
+-      char name[EDT_NAME_LEN];
++      char name[EDT_NAME_PREFIX_LEN + EDT_NAME_LEN];
+       char fw_version[EDT_NAME_LEN];
+       int init_td_status;
+@@ -965,6 +966,9 @@ static int edt_ft5x06_ts_identify(struct
+       char *model_name = tsdata->name;
+       char *fw_version = tsdata->fw_version;
++      snprintf(model_name, EDT_NAME_PREFIX_LEN, "%s ", dev_name(&client->dev));
++      model_name += strlen(model_name);
++
+       /* see what we find if we assume it is a M06 *
+        * if we get less than EDT_NAME_LEN, we don't want
+        * to have garbage in there
diff --git a/target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch b/target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch
new file mode 100644 (file)
index 0000000..4b529e6
--- /dev/null
@@ -0,0 +1,52 @@
+From a420bbde05f8a6691b0c3e0830092e443365aaa7 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:20:27 +0000
+Subject: [PATCH] input: goodix: Include I2C details in names for the devices
+
+libinput uses the input device name alone. If you have two
+identical input devices, then there is no way to differentiate
+between them, and in the case of touchscreens that means no
+way to associate them with the appropriate display device.
+
+Add the I2C bus and address to the start of the input device
+name so that the name is always unique within the system.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/goodix.c | 5 ++++-
+ drivers/input/touchscreen/goodix.h | 3 +++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/goodix.c
++++ b/drivers/input/touchscreen/goodix.c
+@@ -1211,7 +1211,10 @@ static int goodix_configure_dev(struct g
+               return -ENOMEM;
+       }
+-      ts->input_dev->name = "Goodix Capacitive TouchScreen";
++      snprintf(ts->name, GOODIX_NAME_MAX_LEN, "%s Goodix Capacitive TouchScreen",
++               dev_name(&ts->client->dev));
++
++      ts->input_dev->name = ts->name;
+       ts->input_dev->phys = "input/ts";
+       ts->input_dev->id.bustype = BUS_I2C;
+       ts->input_dev->id.vendor = 0x0416;
+--- a/drivers/input/touchscreen/goodix.h
++++ b/drivers/input/touchscreen/goodix.h
+@@ -57,6 +57,8 @@
+ #define GOODIX_CONFIG_MAX_LENGTH              240
+ #define GOODIX_MAX_KEYS                               7
++#define GOODIX_NAME_MAX_LEN                   38
++
+ enum goodix_irq_pin_access_method {
+       IRQ_PIN_ACCESS_NONE,
+       IRQ_PIN_ACCESS_GPIO,
+@@ -91,6 +93,7 @@ struct goodix_ts_data {
+       enum gpiod_flags gpiod_rst_flags;
+       char id[GOODIX_ID_MAX_LEN + 1];
+       char cfg_name[64];
++      char name[GOODIX_NAME_MAX_LEN];
+       u16 version;
+       bool reset_controller_at_probe;
+       bool load_cfg_from_disk;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch b/target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch
new file mode 100644 (file)
index 0000000..c40b77c
--- /dev/null
@@ -0,0 +1,84 @@
+From a984fda6b2c24dbf1ca21924f99c8f9418f5765e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 22 Nov 2023 19:17:44 +0000
+Subject: [PATCH] drm: vc4: Block swiotlb bounce buffers being imported as
+ dmabuf
+
+The dmabuf import already checks that the backing buffer is contiguous
+and rejects it if it isn't. vc4 also requires that the buffer is
+in the bottom 1GB of RAM, and this is all correctly defined via
+dma-ranges.
+
+However the kernel silently uses swiotlb to bounce dma buffers
+around if they are in the wrong region. This relies on dma sync
+functions to be called in order to copy the data to/from the
+bounce buffer.
+
+DRM is based on all memory allocations being coherent with the
+GPU so that any updates to a framebuffer will be acted on without
+the need for any additional update. This is fairly fundamentally
+incompatible with needing to call dma_sync_ to handle the bounce
+buffer copies, and therefore we have to detect and reject mappings
+that use bounce buffers.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 26 ++++++++++++++++++++++++--
+ 1 file changed, 24 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -29,6 +29,7 @@
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
++#include <linux/dma-direct.h>
+ #include <drm/drm_aperture.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -175,6 +176,19 @@ static void vc4_close(struct drm_device
+       kfree(vc4file);
+ }
++struct drm_gem_object *
++vc4_prime_import_sg_table(struct drm_device *dev,
++                        struct dma_buf_attachment *attach,
++                        struct sg_table *sgt)
++{
++      phys_addr_t phys = dma_to_phys(dev->dev, sg_dma_address(sgt->sgl));
++
++      if (is_swiotlb_buffer(dev->dev, phys))
++              return ERR_PTR(-EINVAL);
++
++      return drm_gem_dma_prime_import_sg_table(dev, attach, sgt);
++}
++
+ DEFINE_DRM_GEM_FOPS(vc4_drm_fops);
+ static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
+@@ -211,7 +225,11 @@ const struct drm_driver vc4_drm_driver =
+       .gem_create_object = vc4_create_object,
+-      DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc4_bo_dumb_create),
++      .dumb_create            = vc4_bo_dumb_create,
++      .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
++      .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
++      .gem_prime_import_sg_table = vc4_prime_import_sg_table,
++      .gem_prime_mmap         = drm_gem_prime_mmap,
+       .ioctls = vc4_drm_ioctls,
+       .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
+@@ -234,7 +252,11 @@ const struct drm_driver vc5_drm_driver =
+       .debugfs_init = vc4_debugfs_init,
+ #endif
+-      DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc5_dumb_create),
++      .dumb_create            = vc5_dumb_create,
++      .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
++      .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
++      .gem_prime_import_sg_table = vc4_prime_import_sg_table,
++      .gem_prime_mmap         = drm_gem_prime_mmap,
+       .fops = &vc4_drm_fops,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch b/target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch
new file mode 100644 (file)
index 0000000..c7c9dab
--- /dev/null
@@ -0,0 +1,75 @@
+From 77a01f7da77446277139d8e9ce63f078cbe1ecfe Mon Sep 17 00:00:00 2001
+From: Kenny <aSmig+github@romhat.net>
+Date: Wed, 22 Nov 2023 16:22:37 -0800
+Subject: [PATCH] overlays: i2c-sensor: Add adt7410 support
+
+See https://github.com/raspberrypi/linux/pull/5738
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README              | 12 ++++++++----
+ .../boot/dts/overlays/i2c-sensor-common.dtsi   | 18 +++++++++++++++++-
+ 2 files changed, 25 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2144,10 +2144,14 @@ Name:   i2c-sensor
+ Info:   Adds support for a number of I2C barometric pressure, temperature,
+         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, BMP380, CCS811, DS1621, HDC100X, JC42,
+-                                LM75, MCP980x, MPU6050, MPU9250, MS5637, MS5803,
+-                                MS5805, MS5837, MS8607, SHT3x or TMP102
++Params: addr                    Set the address for the ADT7410, BH1750, BME280,
++                                BME680, BMP280, BMP380, CCS811, DS1621, HDC100X,
++                                JC42, LM75, MCP980x, MPU6050, MPU9250, MS5637,
++                                MS5803, MS5805, MS5837, MS8607, SHT3x or TMP102
++
++        adt7410                 Select the Analog Devices ADT7410 and ADT7420
++                                temperature sensors
++                                Valid address 0x48-0x4b, default 0x48
+         aht10                   Select the Aosong AHT10 temperature and humidity
+                                 sensor
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -508,6 +508,21 @@
+               };
+       };
++      fragment@34 {
++              target = <&i2cbus>;
++              __dormant__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "okay";
++
++                      adt7410: adt7410@48 {
++                              compatible = "adi,adt7410", "adi,adt7420";
++                              reg = <0x48>;
++                              status = "okay";
++                      };
++              };
++      };
++
+       __overrides__ {
+               bme280 = <0>,"+0";
+               bmp085 = <0>,"+1";
+@@ -543,6 +558,7 @@
+               mpu9250 = <0>,"+29";
+               bno055 = <0>,"+31";
+               sht4x = <0>,"+32";
++              adt7410 = <0>,"+34";
+               addr =  <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
+                       <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0",
+@@ -552,7 +568,7 @@
+                       <&ms5837>,"reg:0", <&ms8607>,"reg:0",
+                       <&mpu6050>,"reg:0", <&mpu9250>,"reg:0",
+                       <&bno055>,"reg:0", <&sht4x>,"reg:0",
+-                      <&bmp380>,"reg:0";
++                      <&bmp380>,"reg:0", <&adt7410>,"reg:0";
+               int_pin = <&max30102>, "interrupts:0",
+                       <&mpu6050>, "interrupts:0",
+                       <&mpu9250>, "interrupts:0";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch b/target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch
new file mode 100644 (file)
index 0000000..2620a59
--- /dev/null
@@ -0,0 +1,26 @@
+From 7c185b18a1c6a6cd6ab8805fef03a4a8c9931656 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 24 Nov 2023 13:57:09 +0000
+Subject: [PATCH] overlays: hat_map: Add pisound mapping
+
+See: https://github.com/raspberrypi/linux/issues/5741
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/hat_map.dts | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/hat_map.dts
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -6,6 +6,11 @@
+               overlay = "iqaudio-codec";
+       };
++      pisound {
++              uuid = [ a7ee5d28 da03 41f5 bbd7 20438a4bec5d ];
++              overlay = "pisound";
++      };
++
+       recalbox-rgbdual {
+               uuid = [ 1c955808 681f 4bbc a2ef b7ea47cd388e ];
+               overlay = "recalboxrgbdual";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch b/target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch
new file mode 100644 (file)
index 0000000..05da5d3
--- /dev/null
@@ -0,0 +1,47 @@
+From bc76ab2e772242d0d92d38b2a5648485ee1a1b44 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michael=20B=C3=BCchler?= <michael.buechler@posteo.net>
+Date: Sun, 19 Nov 2023 01:22:20 +0100
+Subject: [PATCH] drm/vc4: Set TV margins on the composite connector state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Raspberry Pi bootloader can pass a set of margin values to the
+kernel cmdline as part of the video= option. These should result in
+black borders on the output to compensate for TVs where the visible area
+on the screen is smaller than the full extents of the video image.
+
+With the VC4 driver this currently works on an HDMI connector, but not
+on a composite video connector.
+
+The TV margins from the kernel cmdline are available in the
+drm_cmdline_mode structure of the composite video connector. Apply them
+to the connector state in the connector reset function. This is how it
+is implemented for the VC4 HDMI connector.
+
+Signed-off-by: Michael Büchler <michael.buechler@posteo.net>
+---
+ drivers/gpu/drm/vc4/vc4_vec.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_vec.c
++++ b/drivers/gpu/drm/vc4/vc4_vec.c
+@@ -436,6 +436,9 @@ static void vc4_vec_connector_reset(stru
+       /* preserve TV standard */
+       if (connector->state)
+               connector->state->tv.mode = vc4_vec_get_default_mode(connector);
++
++      /* apply TV margins from the cmdline_mode */
++      drm_atomic_helper_connector_tv_reset(connector);
+ }
+ static int vc4_vec_connector_atomic_check(struct drm_connector *conn,
+@@ -483,6 +486,8 @@ static int vc4_vec_connector_init(struct
+       drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
++      drm_connector_attach_tv_margin_properties(connector);
++
+       drm_object_attach_property(&connector->base,
+                                  dev->mode_config.tv_mode_property,
+                                  vc4_vec_get_default_mode(connector));
diff --git a/target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch b/target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch
new file mode 100644 (file)
index 0000000..62e408b
--- /dev/null
@@ -0,0 +1,26 @@
+From 63d8c0f5185169058384142547655fc038aae0bf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 9 Aug 2023 21:04:40 +0100
+Subject: [PATCH] drm: panel: jdi-lt070me05000: Add prepare_upstream_first flag
+
+The panel driver wants to send DCS commands from the prepare
+hook, therefore the DSI host wants to be pre_enabled first.
+Set the flag to achieve this.
+
+https://forums.raspberrypi.com/viewtopic.php?t=354708
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
++++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
+@@ -437,6 +437,7 @@ static int jdi_panel_add(struct jdi_pane
+               return ret;
+       }
++      jdi->base.prepare_upstream_first = true;
+       drm_panel_init(&jdi->base, &jdi->dsi->dev, &jdi_panel_funcs,
+                      DRM_MODE_CONNECTOR_DSI);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch b/target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch
new file mode 100644 (file)
index 0000000..e1f16d0
--- /dev/null
@@ -0,0 +1,55 @@
+From 76b1bbf3ec3be0afdc768863ab7e9bbd2734b97b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Nov 2023 14:29:57 +0000
+Subject: [PATCH] drivers: media: cfe: Find the source pads on the sensor
+ entity
+
+The driver was assuming that pad 0 on the sensor entity was the
+appropriate source pad, but this isn't necessarily the case.
+With video-mux, it has the sink pads first, and then the source
+pad as the last one.
+
+Iterate through the sensor pads to find the relevant source pads.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1826,7 +1826,7 @@ static void cfe_unregister_nodes(struct
+ static int cfe_link_node_pads(struct cfe_device *cfe)
+ {
+-      unsigned int i;
++      unsigned int i, source_pad = 0;
+       int ret;
+       for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
+@@ -1835,14 +1835,23 @@ static int cfe_link_node_pads(struct cfe
+               if (!check_state(cfe, NODE_REGISTERED, i))
+                       continue;
+-              if (i < cfe->sensor->entity.num_pads) {
++              /* Find next source pad */
++              while (source_pad < cfe->sensor->entity.num_pads &&
++                     !(cfe->sensor->entity.pads[source_pad].flags &
++                                                      MEDIA_PAD_FL_SOURCE))
++                      source_pad++;
++
++              if (source_pad < cfe->sensor->entity.num_pads) {
+                       /* Sensor -> CSI2 */
+-                      ret = media_create_pad_link(&cfe->sensor->entity, i,
++                      ret = media_create_pad_link(&cfe->sensor->entity, source_pad,
+                                                   &cfe->csi2.sd.entity, i,
+                                                   MEDIA_LNK_FL_IMMUTABLE |
+                                                   MEDIA_LNK_FL_ENABLED);
+                       if (ret)
+                               return ret;
++
++                      /* Dealt with that source_pad, look at the next one next time */
++                      source_pad++;
+               }
+               /* CSI2 channel # -> /dev/video# */
diff --git a/target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch b/target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch
new file mode 100644 (file)
index 0000000..09cdcc7
--- /dev/null
@@ -0,0 +1,101 @@
+From 08c5904ad00ffc54d37058ead19814f8a85f6f39 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 27 Nov 2023 14:50:58 +0000
+Subject: [PATCH] dtoverlays: Add option for cam0 to camera-mux-Nport overlays
+
+Seeing as the mux can be connected to either CAM/DISP1 or
+CAM/DISP0 on a Pi5, add a cam0 override to allow configuration
+of which is used. Default (as with all camera overlays) is CAM/DISP1.
+
+The overlay does NOT update the camera regulator used by all the
+sensors as doing so would be a nightmare. The Arducam mulitplexer
+boards these overlays are initially supporting seem to tie the
+regulator GPIO for all the sensors high anyway.
+If it was viewed as necessary, then creating an additional
+regulator that listed cam[01]_reg as the parent should work.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README                       | 4 ++++
+ arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts | 7 +++++--
+ arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts | 7 +++++--
+ 3 files changed, 14 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -823,6 +823,8 @@ Params: cam0-arducam-64mp       Select A
+         cam1-ov9281             Select OV9281 for camera on port 1
+         cam1-imx290-clk-freq    Set clock frequency for an IMX290 on port 1
++        cam0                    Connect the mux to CAM0 port (default is CAM1)
++
+ Name:   camera-mux-4port
+ Info:   Configures a 4 port camera multiplexer
+@@ -878,6 +880,8 @@ Params: cam0-arducam-64mp       Select A
+         cam3-ov9281             Select OV9281 for camera on port 3
+         cam3-imx290-clk-freq    Set clock frequency for an IMX290 on port 3
++        cam0                    Connect the mux to CAM0 port (default is CAM1)
++
+ Name:   cap1106
+ Info:   Enables the ability to use the cap1106 touch sensor as a keyboard
+--- a/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
+@@ -77,7 +77,7 @@
+       };
+       /* Mux define */
+-      fragment@200 {
++      i2c_frag: fragment@200 {
+               target = <&i2c_csi_dsi>;
+               __overlay__ {
+                       #address-cells = <1>;
+@@ -294,7 +294,7 @@
+               };
+       };
+-      fragment@201 {
++      csi_frag: fragment@201 {
+               target = <&csi1>;
+               __overlay__ {
+                       status = "okay";
+@@ -501,5 +501,8 @@
+                                      <&imx290_0>,"clock-frequency:0";
+               cam1-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+                                      <&imx290_1>,"clock-frequency:0";
++
++              cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++                     <&csi_frag>, "target:0=",<&csi0>;
+       };
+ };
+--- a/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
+@@ -135,7 +135,7 @@
+       };
+       /* Mux define */
+-      fragment@200 {
++      i2c_frag: fragment@200 {
+               target = <&i2c_csi_dsi>;
+               __overlay__ {
+                       #address-cells = <1>;
+@@ -552,7 +552,7 @@
+               };
+       };
+-      fragment@201 {
++      csi_frag: fragment@201 {
+               target = <&csi1>;
+               __overlay__ {
+                       status = "okay";
+@@ -872,5 +872,8 @@
+                                      <&imx290_2>,"clock-frequency:0";
+               cam3-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+                                      <&imx290_3>,"clock-frequency:0";
++
++              cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++                     <&csi_frag>, "target:0=",<&csi0>;
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch b/target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch
new file mode 100644 (file)
index 0000000..258b9de
--- /dev/null
@@ -0,0 +1,26 @@
+From 6fac5d5ed6d023733ef7304afc88c8d89467a204 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 27 Nov 2023 12:16:04 +0000
+Subject: [PATCH] ASoC: dwc: Permit sample rates up to 384kHz
+
+The BCM2835 I2S block advertises clock rates up to 384kHz, and there's
+no reason why RP1's DWC I2S block shouldn't do the same.
+
+See: https://github.com/raspberrypi/linux/issues/5748
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -667,7 +667,7 @@ static int dw_configure_dai_by_dt(struct
+       if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+               return -EINVAL;
+-      ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
++      ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_384000);
+       if (ret < 0)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch
new file mode 100644 (file)
index 0000000..f4b7098
--- /dev/null
@@ -0,0 +1,63 @@
+From cbc65d9c95088d1437a5a84c7d6628b8d27a86f1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 28 Nov 2023 12:14:03 +0000
+Subject: [PATCH] ASoC: dwc: Fix full-duplex mode
+
+Configuration of the DMA register was carelessly zeroing bits that may
+used by a stream in the other direction. Preserve them instead.
+
+See: https://github.com/raspberrypi/linux/issues/5741
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -189,10 +189,17 @@ static void dw_i2s_config(struct dw_i2s_
+ {
+       struct i2s_clk_config_data *config = &dev->config;
+       u32 ch_reg;
+-      u32 dmacr = 0;
++      u32 dmacr;
+       i2s_disable_channels(dev, stream);
++      dmacr = i2s_read_reg(dev->i2s_base, DMACR);
++
++      if (stream == SNDRV_PCM_STREAM_PLAYBACK)
++              dmacr &= ~(DMACR_DMAEN_TXCH0 * 0xf);
++      else
++              dmacr &= ~(DMACR_DMAEN_RXCH0 * 0xf);
++
+       for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+@@ -210,10 +217,6 @@ static void dw_i2s_config(struct dw_i2s_
+                       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);
+ }
+@@ -319,10 +322,13 @@ static int dw_i2s_startup(struct snd_pcm
+       dw_i2s_config(dev, substream->stream);
+       dmacr = i2s_read_reg(dev->i2s_base, DMACR);
+-      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dma_data = &dev->play_dma_data;
+-      else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++              dmacr |= DMACR_DMAEN_TX;
++      } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               dma_data = &dev->capture_dma_data;
++              dmacr |= DMACR_DMAEN_RX;
++      }
+       snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+       i2s_write_reg(dev->i2s_base, DMACR, dmacr);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch b/target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch
new file mode 100644 (file)
index 0000000..8472c3e
--- /dev/null
@@ -0,0 +1,45 @@
+From a7ac27fbac1aa3fb8316cf1ff6dd2f81109d46d2 Mon Sep 17 00:00:00 2001
+From: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Date: Fri, 29 Sep 2023 17:05:55 +0200
+Subject: [PATCH] ASoC: pcm512x: Adds bindings for TAS575x devices
+
+commit 736b884a7b68c4eeb66dbf75b97c8ec9b9eeff7f upstream.
+
+The TAS5754/6 power amplifiers use the same pcm512x driver with
+only minor restictions described in the bindings document.
+
+Signed-off-by: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20230929150555.405388-1-joerg.hifiberry@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ Documentation/devicetree/bindings/sound/pcm512x.txt | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/Documentation/devicetree/bindings/sound/pcm512x.txt
++++ b/Documentation/devicetree/bindings/sound/pcm512x.txt
+@@ -1,12 +1,12 @@
+-PCM512x audio CODECs
++PCM512x and TAS575x audio CODECs/amplifiers
+ These devices support both I2C and SPI (configured with pin strapping
+-on the board).
++on the board). The TAS575x devices only support I2C.
+ Required properties:
+-  - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141" or
+-                 "ti,pcm5142"
++  - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141",
++                 "ti,pcm5142", "ti,tas5754" or "ti,tas5756"
+   - reg : the I2C address of the device for I2C, the chip select
+           number for SPI.
+@@ -25,6 +25,7 @@ Optional properties:
+     through <6>.  The device will be configured for clock input on the
+     given pll-in pin and PLL output on the given pll-out pin.  An
+     external connection from the pll-out pin to the SCLK pin is assumed.
++    Caution: the TAS-desvices only support gpios 1,2 and 3
+ Examples:
diff --git a/target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch b/target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch
new file mode 100644 (file)
index 0000000..5009b22
--- /dev/null
@@ -0,0 +1,102 @@
+From a535dd07aff73f3a6eb40174ab5dc413d05f36a1 Mon Sep 17 00:00:00 2001
+From: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Date: Fri, 29 Sep 2023 17:07:20 +0200
+Subject: [PATCH] ASoC: Adds support for TAS575x to the pcm512x driver
+
+commit 1f817805262c2c34142291da376d4932d3c493bc upstream.
+
+Enables the existing pcm512x driver to control the almost
+compatible TAS5754 and -76 amplifers. Both amplifiers support
+only an I2C interface and the internal PLL must be always
+on to provide necessary clocks to the amplifier section.
+Tested on TAS5756 with support from Andreas Arbesser-Krasser
+from Texas Instruments <a-krasser@ti.com>
+
+Signed-off-by: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Link: https://lore.kernel.org/r/20230929150722.405415-1-joerg.hifiberry@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/codecs/pcm512x-i2c.c |  4 ++++
+ sound/soc/codecs/pcm512x.c     | 36 +++++++++++++++++++++++++++++++---
+ 2 files changed, 37 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/codecs/pcm512x-i2c.c
++++ b/sound/soc/codecs/pcm512x-i2c.c
+@@ -39,6 +39,8 @@ static const struct i2c_device_id pcm512
+       { "pcm5122", },
+       { "pcm5141", },
+       { "pcm5142", },
++      { "tas5754", },
++      { "tas5756", },
+       { }
+ };
+ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+@@ -49,6 +51,8 @@ static const struct of_device_id pcm512x
+       { .compatible = "ti,pcm5122", },
+       { .compatible = "ti,pcm5141", },
+       { .compatible = "ti,pcm5142", },
++      { .compatible = "ti,tas5754", },
++      { .compatible = "ti,tas5756", },
+       { }
+ };
+ MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+--- a/sound/soc/codecs/pcm512x.c
++++ b/sound/soc/codecs/pcm512x.c
+@@ -48,6 +48,7 @@ struct pcm512x_priv {
+       int mute;
+       struct mutex mutex;
+       unsigned int bclk_ratio;
++      int force_pll_on;
+ };
+ /*
+@@ -1258,10 +1259,34 @@ static int pcm512x_hw_params(struct snd_
+                       return ret;
+               }
+-              ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+-                                       PCM512x_PLLE, 0);
++              if (!pcm512x->force_pll_on) {
++                      ret = regmap_update_bits(pcm512x->regmap,
++                                               PCM512x_PLL_EN, PCM512x_PLLE, 0);
++              } else {
++                      /* provide minimum PLL config for TAS575x clocking
++                       * and leave PLL enabled
++                       */
++                      ret = regmap_write(pcm512x->regmap,
++                                         PCM512x_PLL_COEFF_0, 0x01);
++                      if (ret != 0) {
++                              dev_err(component->dev,
++                                      "Failed to set pll coefficient: %d\n", ret);
++                              return ret;
++                      }
++                      ret = regmap_write(pcm512x->regmap,
++                                         PCM512x_PLL_COEFF_1, 0x04);
++                      if (ret != 0) {
++                              dev_err(component->dev,
++                                      "Failed to set pll coefficient: %d\n", ret);
++                              return ret;
++                      }
++                      ret = regmap_write(pcm512x->regmap,
++                                         PCM512x_PLL_EN, 0x01);
++                      dev_dbg(component->dev, "Enabling PLL for TAS575x\n");
++              }
++
+               if (ret != 0) {
+-                      dev_err(component->dev, "Failed to disable pll: %d\n", ret);
++                      dev_err(component->dev, "Failed to set pll mode: %d\n", ret);
+                       return ret;
+               }
+       }
+@@ -1659,6 +1684,11 @@ int pcm512x_probe(struct device *dev, st
+                       ret = -EINVAL;
+                       goto err_pm;
+               }
++
++              if (!strcmp(np->name, "tas5756") ||
++                  !strcmp(np->name, "tas5754"))
++                      pcm512x->force_pll_on = 1;
++              dev_dbg(dev, "Device ID: %s\n", np->name);
+       }
+ #endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch b/target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch
new file mode 100644 (file)
index 0000000..d914ab6
--- /dev/null
@@ -0,0 +1,176 @@
+From 0a0084b8621682ad96d001e798aa239b11a3cf00 Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:53:12 +0100
+Subject: [PATCH] drm/panel: add panel-dsi
+
+Equivalent to panel-dpi for configuring a simple DSI panel with
+device tree side timings and bus settings.
+Motiviation is the same as for panel-dpi of wanting to support
+new simple panels without needing to patch the kernel.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ drivers/gpu/drm/panel/panel-simple.c | 123 ++++++++++++++++++++++++++-
+ 1 file changed, 122 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-simple.c
++++ b/drivers/gpu/drm/panel/panel-simple.c
+@@ -40,6 +40,7 @@
+ #include <drm/drm_edid.h>
+ #include <drm/drm_mipi_dsi.h>
+ #include <drm/drm_panel.h>
++#include <drm/drm_of.h>
+ /**
+  * struct panel_desc - Describes a simple panel.
+@@ -4660,6 +4661,9 @@ static const struct panel_desc_dsi osd10
+       .lanes = 4,
+ };
++// for panels using generic panel-dsi binding
++static struct panel_desc_dsi panel_dsi;
++
+ static const struct of_device_id dsi_of_match[] = {
+       {
+               .compatible = "auo,b080uan01",
+@@ -4683,14 +4687,118 @@ static const struct of_device_id dsi_of_
+               .compatible = "osddisplays,osd101t2045-53ts",
+               .data = &osd101t2045_53ts
+       }, {
++              /* Must be the last entry */
++              .compatible = "panel-dsi",
++              .data = &panel_dsi,
++      }, {
+               /* sentinel */
+       }
+ };
+ MODULE_DEVICE_TABLE(of, dsi_of_match);
++
++/* Checks for DSI panel definition in device-tree, analog to panel_dpi */
++static int panel_dsi_dt_probe(struct device *dev,
++                        struct panel_desc_dsi *desc_dsi)
++{
++      struct panel_desc *desc;
++      struct display_timing *timing;
++      const struct device_node *np;
++      const char *dsi_color_format;
++      const char *dsi_mode_flags;
++      struct property *prop;
++      int dsi_lanes, ret;
++
++      np = dev->of_node;
++
++      desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
++      if (!desc)
++              return -ENOMEM;
++
++      timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
++      if (!timing)
++              return -ENOMEM;
++
++      ret = of_get_display_timing(np, "panel-timing", timing);
++      if (ret < 0) {
++              dev_err(dev, "%pOF: no panel-timing node found for \"panel-dsi\" binding\n",
++                      np);
++              return ret;
++      }
++
++      desc->timings = timing;
++      desc->num_timings = 1;
++
++      of_property_read_u32(np, "width-mm", &desc->size.width);
++      of_property_read_u32(np, "height-mm", &desc->size.height);
++
++      dsi_lanes = drm_of_get_data_lanes_count_ep(np, 0, 0, 1, 4);
++
++      if (dsi_lanes < 0) {
++              dev_err(dev, "%pOF: no or too many data-lanes defined", np);
++              return dsi_lanes;
++      }
++
++      desc_dsi->lanes = dsi_lanes;
++
++      of_property_read_string(np, "dsi-color-format", &dsi_color_format);
++      if (!strcmp(dsi_color_format, "RGB888")) {
++              desc_dsi->format = MIPI_DSI_FMT_RGB888;
++              desc->bpc = 8;
++      } else if (!strcmp(dsi_color_format, "RGB565")) {
++              desc_dsi->format = MIPI_DSI_FMT_RGB565;
++              desc->bpc = 6;
++      } else if (!strcmp(dsi_color_format, "RGB666")) {
++              desc_dsi->format = MIPI_DSI_FMT_RGB666;
++              desc->bpc = 6;
++      } else if (!strcmp(dsi_color_format, "RGB666_PACKED")) {
++              desc_dsi->format = MIPI_DSI_FMT_RGB666_PACKED;
++              desc->bpc = 6;
++      } else {
++              dev_err(dev, "%pOF: no valid dsi-color-format defined", np);
++              return -EINVAL;
++      }
++
++
++      of_property_for_each_string(np, "mode", prop, dsi_mode_flags) {
++              if (!strcmp(dsi_mode_flags, "MODE_VIDEO"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_BURST"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_BURST;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_SYNC_PULSE"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_AUTO_VERT"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_AUTO_VERT;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_HSE"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_HSE;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HFP"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HFP;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HBP"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HBP;
++              else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HSA"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HSA;
++              else if (!strcmp(dsi_mode_flags, "MODE_VSYNC_FLUSH"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_VSYNC_FLUSH;
++              else if (!strcmp(dsi_mode_flags, "MODE_NO_EOT_PACKET"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_NO_EOT_PACKET;
++              else if (!strcmp(dsi_mode_flags, "CLOCK_NON_CONTINUOUS"))
++                      desc_dsi->flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS;
++              else if (!strcmp(dsi_mode_flags, "MODE_LPM"))
++                      desc_dsi->flags |= MIPI_DSI_MODE_LPM;
++              else if (!strcmp(dsi_mode_flags, "HS_PKT_END_ALIGNED"))
++                      desc_dsi->flags |= MIPI_DSI_HS_PKT_END_ALIGNED;
++      }
++
++      desc->connector_type = DRM_MODE_CONNECTOR_DSI;
++      desc_dsi->desc = *desc;
++
++      return 0;
++}
++
+ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
+ {
+       const struct panel_desc_dsi *desc;
++      struct panel_desc_dsi *dt_desc;
+       const struct of_device_id *id;
+       int err;
+@@ -4698,7 +4806,20 @@ static int panel_simple_dsi_probe(struct
+       if (!id)
+               return -ENODEV;
+-      desc = id->data;
++      if (id->data == &panel_dsi) {
++              /* Handle the generic panel-dsi binding */
++              dt_desc = devm_kzalloc(&dsi->dev, sizeof(*dt_desc), GFP_KERNEL);
++              if (!dt_desc)
++                      return -ENOMEM;
++
++              err = panel_dsi_dt_probe(&dsi->dev, dt_desc);
++              if (err < 0)
++                      return err;
++
++              desc = dt_desc;
++      } else {
++              desc = id->data;
++      }
+       err = panel_simple_probe(&dsi->dev, &desc->desc);
+       if (err < 0)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch
new file mode 100644 (file)
index 0000000..c75ca8e
--- /dev/null
@@ -0,0 +1,136 @@
+From e14ceeadfa5b9d8a41e73edfa416bbe92dd5b20d Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:53:22 +0100
+Subject: [PATCH] dt-bindings: display: panel-dsi bindings
+
+Bindings for the panel-dsi specific additions to panel-simple.
+Allow for DSI specific bus settings and panel timing
+to be define in devicetree. Very similar to panel-dpi.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ .../bindings/display/panel/panel-dsi.yaml     | 118 ++++++++++++++++++
+ 1 file changed, 118 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/panel/panel-dsi.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/panel/panel-dsi.yaml
+@@ -0,0 +1,118 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/panel/panel-dsi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Generic MIPI DSI Panel
++
++maintainers:
++  - Timon Skerutsch <kernel@diodes-delight.com>
++
++allOf:
++  - $ref: panel-common.yaml#
++
++properties:
++  compatible:
++    description:
++      Shall contain a panel specific compatible and "panel-dsi"
++      in that order.
++    items:
++      - {}
++      - const: panel-dsi
++
++  dsi-color-format:
++    description: |
++      The color format used by the panel. Only DSI supported formats are allowed.
++    enum:
++      - RGB888
++      - RGB666
++      - RGB666_PACKED
++      - RGB565
++
++  port:
++    $ref: /schemas/graph.yaml#/$defs/port-base
++    unevaluatedProperties: false
++    description:
++      Panel MIPI DSI input
++
++    properties:
++      endpoint:
++        $ref: /schemas/media/video-interfaces.yaml#
++        unevaluatedProperties: false
++
++        properties:
++          data-lanes: true
++
++        required:
++          - data-lanes
++
++  mode:
++    description: |
++      DSI mode flags. See DSI Specs for details.
++      These are driver independent features of the DSI bus.
++    items:
++      - const: MODE_VIDEO
++      - const: MODE_VIDEO_BURST
++      - const: MODE_VIDEO_SYNC_PULSE
++      - const: MODE_VIDEO_AUTO_VERT
++      - const: MODE_VIDEO_HSE
++      - const: MODE_VIDEO_NO_HFP
++      - const: MODE_VIDEO_NO_HBP
++      - const: MODE_VIDEO_NO_HSA
++      - const: MODE_VSYNC_FLUSH
++      - const: MODE_NO_EOT_PACKET
++      - const: CLOCK_NON_CONTINUOUS
++      - const: MODE_LPM
++      - const: HS_PKT_END_ALIGNED
++
++  reg: true
++  backlight: true
++  enable-gpios: true
++  width-mm: true
++  height-mm: true
++  panel-timing: true
++  power-supply: true
++  reset-gpios: true
++  ddc-i2c-bus: true
++
++required:
++  - panel-timing
++  - reg
++  - power-supply
++  - dsi-color-format
++  - port
++
++additionalProperties: false
++
++examples:
++  - |
++    panel {
++        compatible = "panel-mfgr,generic-dsi-panel","panel-dsi";
++        power-supply = <&vcc_supply>;
++        backlight = <&backlight>;
++        dsi-color-format = "RGB888";
++        reg = <0>;
++        mode = "MODE_VIDEO", "MODE_VIDEO_BURST", "MODE_NO_EOT_PACKET";
++
++        port {
++            panel_dsi_port: endpoint {
++                data-lanes = <1 2>;
++                remote-endpoint = <&dsi_out>;
++            };
++        };
++
++        panel-timing {
++            clock-frequency = <9200000>;
++            hactive = <800>;
++            vactive = <480>;
++            hfront-porch = <8>;
++            hback-porch = <4>;
++            hsync-len = <41>;
++            vback-porch = <2>;
++            vfront-porch = <4>;
++            vsync-len = <10>;
++        };
++    };
++
++...
diff --git a/target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch b/target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch
new file mode 100644 (file)
index 0000000..28a82d5
--- /dev/null
@@ -0,0 +1,176 @@
+From ccf75f2a6f4045484c4539f7d47264f8f6b8453c Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:52:35 +0100
+Subject: [PATCH] overlays: example overlay for using panel-dsi on RPi
+
+Analog to the generic panel-dpi overlay to use panel-dsi with dtparam
+to not require a panel specific overlay for simple use cases that
+do not require setting more niche DSI modes.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ arch/arm/boot/dts/overlays/Makefile           |   1 +
+ arch/arm/boot/dts/overlays/README             |  31 +++++
+ .../overlays/vc4-kms-dsi-generic-overlay.dts  | 106 ++++++++++++++++++
+ 3 files changed, 138 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-dsi-generic-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -293,6 +293,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+       vc4-kms-dpi-hyperpixel4sq.dtbo \
+       vc4-kms-dpi-panel.dtbo \
+       vc4-kms-dsi-7inch.dtbo \
++      vc4-kms-dsi-generic.dtbo \
+       vc4-kms-dsi-lt070me05000.dtbo \
+       vc4-kms-dsi-lt070me05000-v2.dtbo \
+       vc4-kms-dsi-waveshare-panel.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4785,6 +4785,37 @@ Params: sizex                   Touchscr
+                                 the default DSI1 and i2c_csi_dsi).
++Name:   vc4-kms-dsi-generic
++Info:   Enable a generic DSI display under KMS.
++        Default timings are for a 840x480 RGB888 panel.
++        Requires vc4-kms-v3d to be loaded.
++Load:   dtoverlay=vc4-kms-dsi-generic,<param>=<val>
++Params: clock-frequency         Display clock frequency (Hz)
++        hactive                 Horizontal active pixels
++        hfp                     Horizontal front porch
++        hsync                   Horizontal sync pulse width
++        hbp                     Horizontal back porch
++        vactive                 Vertical active lines
++        vfp                     Vertical front porch
++        vsync                   Vertical sync pulse width
++        vbp                     Vertical back porch
++        width-mm                Define the screen width in mm
++        height-mm               Define the screen height in mm
++        rgb565                  Change to RGB565 output
++        rgb666                  Change to RGB666 output
++        rgb666p                 Change to RGB666 output with pixel packing
++        rgb888                  Change to RGB888 output, this is the default
++        one-lane                Use one DSI lane for data transmission
++                                This is the default
++        two-lane                Use two DSI lanes for data transmission
++        three-lane              Use three DSI lanes for data transmission
++                                Only supported on Pi5 and CM
++        four-lane               Use four DSI lanes for data transmission
++                                Only supported on Pi5 and CM
++        dsi0                    Switch DSI port to DSI0
++                                Only supported on Pi5 and CM
++
++
+ Name:   vc4-kms-dsi-lt070me05000
+ Info:   Enable a JDI LT070ME05000 DSI display on DSI1.
+         Note that this is a 4 lane DSI device, so it will only work on a Compute
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-generic-overlay.dts
+@@ -0,0 +1,106 @@
++/dts-v1/;
++/plugin/;
++
++/ {
++      compatible = "brcm,bcm2835";
++
++      dsi_frag: fragment@0 {
++              target = <&dsi1>;
++              __overlay__{
++                      status = "okay";
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      port {
++                              dsi_out:endpoint {
++                                      remote-endpoint = <&panel_dsi_port>;
++                              };
++                      };
++                      panel: panel-dsi-generic@0 {
++                              // See panel-dsi.yaml binding
++                              // Using dummy name for panel model
++                              compatible = "Generic,panel-dsi","panel-dsi";
++                              reg = <0>;
++                              power-supply = <0>;
++                              backlight = <0>;
++                              dsi-color-format = "RGB888";
++                              mode = "MODE_VIDEO";
++                              width-mm = <0>;
++                              height-mm = <0>;
++
++                              port {
++                                      panel_dsi_port: endpoint {
++                                              data-lanes = <1>;
++                                              remote-endpoint = <&dsi_out>;
++                                      };
++                              };
++
++                              timing: panel-timing {
++                                      clock-frequency = <30000000>;
++                                      hactive = <840>;
++                                      vactive = <480>;
++                                      hback-porch = <44>;
++                                      hfront-porch = <46>;
++                                      hsync-len = <2>;
++                                      vback-porch = <18>;
++                                      vfront-porch = <16>;
++                                      vsync-len = <2>;
++                              };
++                      };
++              };
++      };
++
++      fragment@1 {
++              target = <&panel_dsi_port>;
++              __dormant__ {
++                      data-lanes = <1>;
++              };
++      };
++
++      fragment@2 {
++              target = <&panel_dsi_port>;
++              __dormant__ {
++                      data-lanes = <1 2>;
++              };
++      };
++
++      fragment@3 {
++              target = <&panel_dsi_port>;
++              __dormant__ {
++                      data-lanes = <1 2 3>;
++              };
++      };
++
++      fragment@4 {
++              target = <&panel_dsi_port>;
++              __dormant__ {
++                      data-lanes = <1 2 3 4>;
++              };
++      };
++
++      __overrides__ {
++              dsi0 = <&dsi_frag>, "target:0=",<&dsi0>;
++
++              clock-frequency = <&timing>, "clock-frequency:0";
++              hactive = <&timing>, "hactive:0";
++              hfp = <&timing>, "hfront-porch:0";
++              hsync = <&timing>, "hsync-len:0";
++              hbp = <&timing>, "hback-porch:0";
++              vactive = <&timing>, "vactive:0";
++              vfp = <&timing>, "vfront-porch:0";
++              vsync = <&timing>, "vsync-len:0";
++              vbp = <&timing>, "vback-porch:0";
++
++              width-mm = <&panel>, "width-mm:0";
++              height-mm = <&panel>, "height-mm:0";
++
++              rgb565 = <&panel>, "dsi-color-format=RGB565";
++              rgb666p = <&panel>, "dsi-color-format=RGB666_PACKED";
++              rgb666 = <&panel>, "dsi-color-format=RGB666";
++              rgb888 = <&panel>, "dsi-color-format=RGB888";
++              one-lane = <0>,"+1";
++              two-lane = <0>,"+2";
++              three-lane = <0>,"+3";
++              four-lane = <0>,"+4";
++      };
++
++};
diff --git a/target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch b/target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch
new file mode 100644 (file)
index 0000000..ef9328b
--- /dev/null
@@ -0,0 +1,159 @@
+From a477a6351575aa173f9f82857f5797e384fbc704 Mon Sep 17 00:00:00 2001
+From: JinShil <slavo5150@yahoo.com>
+Date: Tue, 28 Nov 2023 17:05:44 +0900
+Subject: [PATCH] overlays: ADS1115: allow specification of the i2c bus
+
+---
+ arch/arm/boot/dts/overlays/README             | 10 +++
+ .../arm/boot/dts/overlays/ads1115-overlay.dts | 80 +++++++++++++------
+ 2 files changed, 66 insertions(+), 24 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -489,6 +489,16 @@ Params: addr                    I2C bus
+         cha_gain                Set the gain of the Programmable Gain
+                                 Amplifier for this channel. (Default 1 sets the
+                                 full scale of the channel to 4.096 Volts)
++        i2c0                    Choose the I2C0 bus on GPIOs 0&1
++        i2c_csi_dsi             Choose the I2C0 bus on GPIOs 44&45
++        i2c3                    Choose the I2C3 bus (configure with the i2c3
++                                overlay - BCM2711 only)
++        i2c4                    Choose the I2C4 bus (configure with the i2c4
++                                overlay - BCM2711 only)
++        i2c5                    Choose the I2C5 bus (configure with the i2c5
++                                overlay - BCM2711 only)
++        i2c6                    Choose the I2C6 bus (configure with the i2c6
++                                overlay - BCM2711 only)
+         Channel parameters can be set for each enabled channel.
+         A maximum of 4 channels can be enabled (letters a thru d).
+--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts
+@@ -9,23 +9,6 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target = <&i2c_arm>;
+-              __overlay__ {
+-                      #address-cells = <1>;
+-                      #size-cells = <0>;
+-                      status = "okay";
+-
+-                      ads1115: ads1115@48 {
+-                              compatible = "ti,ads1115";
+-                              status = "okay";
+-                              #address-cells = <1>;
+-                              #size-cells = <0>;
+-                              reg = <0x48>;
+-                      };
+-              };
+-      };
+-
+-      fragment@1 {
+               target = <&ads1115>;
+               __dormant__ {
+                       #address-cells = <1>;
+@@ -39,7 +22,7 @@
+               };
+       };
+-      fragment@2 {
++      fragment@1 {
+               target = <&ads1115>;
+               __dormant__ {
+                       #address-cells = <1>;
+@@ -53,7 +36,7 @@
+               };
+       };
+-      fragment@3 {
++      fragment@2 {
+               target = <&ads1115>;
+               __dormant__ {
+                       #address-cells = <1>;
+@@ -67,7 +50,7 @@
+               };
+       };
+-      fragment@4 {
++      fragment@3 {
+               target = <&ads1115>;
+               __dormant__ {
+                       #address-cells = <1>;
+@@ -81,23 +64,72 @@
+               };
+       };
++      fragment@4 {
++              target = <&i2cbus>;
++              __overlay__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "okay";
++
++                      ads1115: ads1115@48 {
++                              compatible = "ti,ads1115";
++                              status = "okay";
++                              #address-cells = <1>;
++                              #size-cells = <0>;
++                              reg = <0x48>;
++                      };
++              };
++      };
++
++      frag100: fragment@100 {
++              target = <&i2c1>;
++              i2cbus: __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      fragment@101 {
++              target = <&i2c0if>;
++              __dormant__ {
++                      status = "okay";
++              };
++      };
++
++      fragment@102 {
++              target = <&i2c0mux>;
++              __dormant__ {
++                      status = "okay";
++              };
++      };
++
+       __overrides__ {
+               addr =            <&ads1115>,"reg:0";
+-              cha_enable =      <0>,"=1";
++              cha_enable =      <0>,"=0";
+               cha_cfg =         <&channel_a>,"reg:0";
+               cha_gain =        <&channel_a>,"ti,gain:0";
+               cha_datarate =    <&channel_a>,"ti,datarate:0";
+-              chb_enable =      <0>,"=2";
++              chb_enable =      <0>,"=1";
+               chb_cfg =         <&channel_b>,"reg:0";
+               chb_gain =        <&channel_b>,"ti,gain:0";
+               chb_datarate =    <&channel_b>,"ti,datarate:0";
+-              chc_enable =      <0>,"=3";
++              chc_enable =      <0>,"=2";
+               chc_cfg =         <&channel_c>,"reg:0";
+               chc_gain =        <&channel_c>,"ti,gain:0";
+               chc_datarate =    <&channel_c>,"ti,datarate:0";
+-              chd_enable =      <0>,"=4";
++              chd_enable =      <0>,"=3";
+               chd_cfg =         <&channel_d>,"reg:0";
+               chd_gain =        <&channel_d>,"ti,gain:0";
+               chd_datarate =    <&channel_d>,"ti,datarate:0";
++              i2c0 = <&frag100>, "target:0=",<&i2c0>;
++              i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
++                            <0>,"+101+102";
++              i2c3 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c3";
++              i2c4 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c4";
++              i2c5 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c5";
++              i2c6 = <&frag100>, "target?=0",
++                     <&frag100>, "target-path=i2c6";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch b/target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch
new file mode 100644 (file)
index 0000000..b66265f
--- /dev/null
@@ -0,0 +1,59 @@
+From 82069a7a02632aa60fa5c69415bf891ede7d6fd4 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 5 Dec 2023 16:55:17 +0000
+Subject: [PATCH] dts: bcm2712: put usb under /axi not /soc
+
+On 2712, the DWC USB controller is no longer attached to the Videocore
+30-bit bus with its associated aliases, and can see the bottom 4GB of
+RAM directly.
+
+Ideally it should make use of IOMMU6 but for now software bounce buffers
+get it working.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 26 +++++++++++++-------------
+ 1 file changed, 13 insertions(+), 13 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -133,19 +133,6 @@
+                       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>;
+@@ -1145,6 +1132,19 @@
+                       reg = <0x10 0x00400018  0x0 0x18>;
+               };
++              usb: usb@480000 {
++                      compatible = "brcm,bcm2835-usb";
++                      reg = <0x10 0x00480000 0x0 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";
++              };
++
+               rpivid: codec@800000 {
+                       compatible = "raspberrypi,rpivid-vid-decoder";
+                       reg = <0x10 0x00800000  0x0 0x10000>, /* HEVC */
diff --git a/target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch b/target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch
new file mode 100644 (file)
index 0000000..be02fd2
--- /dev/null
@@ -0,0 +1,26 @@
+From 01139e4e9141d031c6f4f00371e5eb52fa78839e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:28:19 +0000
+Subject: [PATCH] drm/vc4: Correct HVS muxing setup for the moplet
+
+The moplet registers as VC4_ENCODER_TYPE_TXP1 and can be
+fed from mux output 2 of HVS channel 1.
+
+Correct the option which checked for VC4_ENCODER_TYPE_TXP0
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -365,7 +365,7 @@ static void vc6_hvs_pv_muxing_commit(str
+                       mux = 0;
+                       break;
+-              case VC4_ENCODER_TYPE_TXP0:
++              case VC4_ENCODER_TYPE_TXP1:
+                       mux = 2;
+                       break;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch b/target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch
new file mode 100644 (file)
index 0000000..f1bb26a
--- /dev/null
@@ -0,0 +1,68 @@
+From cc948130d3e1c70ef21ae9963b56e0d500cef70b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:29:34 +0000
+Subject: [PATCH] drm/vc4: Mop and moplet have different register offsets for
+ high addr
+
+MOP uses register offset 0x24 for the high bits of the address,
+whilst Moplet uses 0x1c.
+
+Handle this difference between the block types.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 8 ++++++--
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -564,6 +564,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+       struct vc4_crtc_data    base;
+       enum vc4_encoder_type encoder_type;
++      unsigned int high_addr_ptr_reg;
+       unsigned int has_byte_enable:1;
+       unsigned int size_minus_one:1;
+       unsigned int supports_40bit_addresses:1;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -145,7 +145,8 @@
+ /* Number of lines received and committed to memory. */
+ #define TXP_PROGRESS          0x10
+-#define TXP_DST_PTR_HIGH      0x1c
++#define TXP_DST_PTR_HIGH_MOPLET       0x1c
++#define TXP_DST_PTR_HIGH_MOP  0x24
+ #define TXP_READ(offset)                                                              \
+       ({                                                                              \
+@@ -338,10 +339,11 @@ static void vc4_txp_connector_atomic_com
+       gem = drm_fb_dma_get_gem_obj(fb, 0);
+       addr = gem->dma_addr + fb->offsets[0];
++
+       TXP_WRITE(TXP_DST_PTR, lower_32_bits(addr));
+       if (txp_data->supports_40bit_addresses)
+-              TXP_WRITE(TXP_DST_PTR_HIGH, upper_32_bits(addr) & 0xff);
++              TXP_WRITE(txp_data->high_addr_ptr_reg, upper_32_bits(addr) & 0xff);
+       TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
+@@ -520,6 +522,7 @@ const struct vc4_txp_data bcm2712_mop_da
+               .hvs_output = 2,
+       },
+       .encoder_type = VC4_ENCODER_TYPE_TXP0,
++      .high_addr_ptr_reg = TXP_DST_PTR_HIGH_MOP,
+       .has_byte_enable = true,
+       .size_minus_one = true,
+       .supports_40bit_addresses = true,
+@@ -533,6 +536,7 @@ const struct vc4_txp_data bcm2712_moplet
+               .hvs_output = 4,
+       },
+       .encoder_type = VC4_ENCODER_TYPE_TXP1,
++      .high_addr_ptr_reg = TXP_DST_PTR_HIGH_MOPLET,
+       .size_minus_one = true,
+       .supports_40bit_addresses = true,
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch b/target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch
new file mode 100644 (file)
index 0000000..ece5c71
--- /dev/null
@@ -0,0 +1,25 @@
+From 36593e2e27769d635ef18301f25b5e219a23949a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:31:25 +0000
+Subject: [PATCH] arm: dt: bcm2712: Correct the size of the register range for
+ MOP
+
+The Mop covers 0x28 bytes of registers, so ensure the range is
+defined appropriately.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -135,7 +135,7 @@
+               mop: mop@7c500000 {
+                       compatible = "brcm,bcm2712-mop";
+-                      reg = <0x7c500000 0x20>;
++                      reg = <0x7c500000 0x28>;
+                       interrupt-parent = <&disp_intr>;
+                       interrupts = <1>;
+                       status = "disabled";
diff --git a/target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch b/target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch
new file mode 100644 (file)
index 0000000..3305e66
--- /dev/null
@@ -0,0 +1,388 @@
+From ae0e1b70f675f6ac7966e427f0d8f57812dbc312 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 27 Jun 2023 14:51:04 +0200
+Subject: [PATCH] media: Add MIPI CCI register access helper functions
+
+The CSI2 specification specifies a standard method to access camera sensor
+registers called "Camera Control Interface (CCI)".
+
+This uses either 8 or 16 bit (big-endian wire order) register addresses
+and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
+
+Currently a lot of Linux camera sensor drivers all have their own custom
+helpers for this, often copy and pasted from other drivers.
+
+Add a set of generic helpers for this so that all sensor drivers can
+switch to a single common implementation.
+
+These helpers take an extra optional "int *err" function parameter,
+this can be used to chain a bunch of register accesses together with
+only a single error check at the end, rather than needing to error
+check each individual register access. The first failing call will
+set the contents of err to a non 0 value and all other calls will
+then become no-ops.
+
+Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
+
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Tested-by: Tommaso Merciai <tomm.merciai@gmail.com>
+Reviewed-by: Tommaso Merciai <tomm.merciai@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+(cherry picked from commit 613cbb91e9cee7cf5a61f0816d2acab7bc117407)
+---
+ Documentation/driver-api/media/v4l2-cci.rst  |   5 +
+ Documentation/driver-api/media/v4l2-core.rst |   1 +
+ drivers/media/v4l2-core/Kconfig              |   9 +
+ drivers/media/v4l2-core/Makefile             |   1 +
+ drivers/media/v4l2-core/v4l2-cci.c           | 166 +++++++++++++++++++
+ include/media/v4l2-cci.h                     | 125 ++++++++++++++
+ 6 files changed, 307 insertions(+)
+ create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
+ create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
+ create mode 100644 include/media/v4l2-cci.h
+
+--- /dev/null
++++ b/Documentation/driver-api/media/v4l2-cci.rst
+@@ -0,0 +1,5 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++V4L2 CCI kAPI
++^^^^^^^^^^^^^
++.. kernel-doc:: include/media/v4l2-cci.h
+--- a/Documentation/driver-api/media/v4l2-core.rst
++++ b/Documentation/driver-api/media/v4l2-core.rst
+@@ -22,6 +22,7 @@ Video4Linux devices
+     v4l2-mem2mem
+     v4l2-async
+     v4l2-fwnode
++    v4l2-cci
+     v4l2-rect
+     v4l2-tuner
+     v4l2-common
+--- a/drivers/media/v4l2-core/Kconfig
++++ b/drivers/media/v4l2-core/Kconfig
+@@ -74,6 +74,15 @@ config V4L2_FWNODE
+ config V4L2_ASYNC
+       tristate
++config V4L2_CCI
++      tristate
++
++config V4L2_CCI_I2C
++      tristate
++      depends on I2C
++      select REGMAP_I2C
++      select V4L2_CCI
++
+ # Used by drivers that need Videobuf modules
+ config VIDEOBUF_GEN
+       tristate
+--- a/drivers/media/v4l2-core/Makefile
++++ b/drivers/media/v4l2-core/Makefile
+@@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l
+ # (e. g. LC_ALL=C sort Makefile)
+ obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
++obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
+ obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
+ obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
+ obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
+--- /dev/null
++++ b/drivers/media/v4l2-core/v4l2-cci.c
+@@ -0,0 +1,166 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * MIPI Camera Control Interface (CCI) register access helpers.
++ *
++ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/delay.h>
++#include <linux/dev_printk.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/types.h>
++
++#include <asm/unaligned.h>
++
++#include <media/v4l2-cci.h>
++
++int cci_read(struct regmap *map, u32 reg, u64 *val, int *err)
++{
++      unsigned int len;
++      u8 buf[8];
++      int ret;
++
++      if (err && *err)
++              return *err;
++
++      len = FIELD_GET(CCI_REG_WIDTH_MASK, reg);
++      reg = FIELD_GET(CCI_REG_ADDR_MASK, reg);
++
++      ret = regmap_bulk_read(map, reg, buf, len);
++      if (ret) {
++              dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n",
++                      reg, ret);
++              goto out;
++      }
++
++      switch (len) {
++      case 1:
++              *val = buf[0];
++              break;
++      case 2:
++              *val = get_unaligned_be16(buf);
++              break;
++      case 3:
++              *val = get_unaligned_be24(buf);
++              break;
++      case 4:
++              *val = get_unaligned_be32(buf);
++              break;
++      case 8:
++              *val = get_unaligned_be64(buf);
++              break;
++      default:
++              dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
++                      len, reg);
++              ret = -EINVAL;
++              break;
++      }
++
++out:
++      if (ret && err)
++              *err = ret;
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(cci_read);
++
++int cci_write(struct regmap *map, u32 reg, u64 val, int *err)
++{
++      unsigned int len;
++      u8 buf[8];
++      int ret;
++
++      if (err && *err)
++              return *err;
++
++      len = FIELD_GET(CCI_REG_WIDTH_MASK, reg);
++      reg = FIELD_GET(CCI_REG_ADDR_MASK, reg);
++
++      switch (len) {
++      case 1:
++              buf[0] = val;
++              break;
++      case 2:
++              put_unaligned_be16(val, buf);
++              break;
++      case 3:
++              put_unaligned_be24(val, buf);
++              break;
++      case 4:
++              put_unaligned_be32(val, buf);
++              break;
++      case 8:
++              put_unaligned_be64(val, buf);
++              break;
++      default:
++              dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
++                      len, reg);
++              ret = -EINVAL;
++              goto out;
++      }
++
++      ret = regmap_bulk_write(map, reg, buf, len);
++      if (ret)
++              dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n",
++                      reg, ret);
++
++out:
++      if (ret && err)
++              *err = ret;
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(cci_write);
++
++int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err)
++{
++      u64 readval;
++      int ret;
++
++      ret = cci_read(map, reg, &readval, err);
++      if (ret)
++              return ret;
++
++      val = (readval & ~mask) | (val & mask);
++
++      return cci_write(map, reg, val, err);
++}
++EXPORT_SYMBOL_GPL(cci_update_bits);
++
++int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
++                      unsigned int num_regs, int *err)
++{
++      unsigned int i;
++      int ret;
++
++      for (i = 0; i < num_regs; i++) {
++              ret = cci_write(map, regs[i].reg, regs[i].val, err);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cci_multi_reg_write);
++
++#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
++struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
++                                      int reg_addr_bits)
++{
++      struct regmap_config config = {
++              .reg_bits = reg_addr_bits,
++              .val_bits = 8,
++              .reg_format_endian = REGMAP_ENDIAN_BIG,
++              .disable_locking = true,
++      };
++
++      return devm_regmap_init_i2c(client, &config);
++}
++EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
++MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");
+--- /dev/null
++++ b/include/media/v4l2-cci.h
+@@ -0,0 +1,125 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * MIPI Camera Control Interface (CCI) register access helpers.
++ *
++ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
++ */
++#ifndef _V4L2_CCI_H
++#define _V4L2_CCI_H
++
++#include <linux/types.h>
++
++struct i2c_client;
++struct regmap;
++
++/**
++ * struct cci_reg_sequence - An individual write from a sequence of CCI writes
++ *
++ * @reg: Register address, use CCI_REG#() macros to encode reg width
++ * @val: Register value
++ *
++ * Register/value pairs for sequences of writes.
++ */
++struct cci_reg_sequence {
++      u32 reg;
++      u64 val;
++};
++
++/*
++ * Macros to define register address with the register width encoded
++ * into the higher bits.
++ */
++#define CCI_REG_ADDR_MASK             GENMASK(15, 0)
++#define CCI_REG_WIDTH_SHIFT           16
++#define CCI_REG_WIDTH_MASK            GENMASK(19, 16)
++
++#define CCI_REG8(x)                   ((1 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG16(x)                  ((2 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG24(x)                  ((3 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG32(x)                  ((4 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG64(x)                  ((8 << CCI_REG_WIDTH_SHIFT) | (x))
++
++/**
++ * cci_read() - Read a value from a single CCI register
++ *
++ * @map: Register map to read from
++ * @reg: Register address to read, use CCI_REG#() macros to encode reg width
++ * @val: Pointer to store read value
++ * @err: Optional pointer to store errors, if a previous error is set
++ *       then the read will be skipped
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_read(struct regmap *map, u32 reg, u64 *val, int *err);
++
++/**
++ * cci_write() - Write a value to a single CCI register
++ *
++ * @map: Register map to write to
++ * @reg: Register address to write, use CCI_REG#() macros to encode reg width
++ * @val: Value to be written
++ * @err: Optional pointer to store errors, if a previous error is set
++ *       then the write will be skipped
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_write(struct regmap *map, u32 reg, u64 val, int *err);
++
++/**
++ * cci_update_bits() - Perform a read/modify/write cycle on
++ *                     a single CCI register
++ *
++ * @map: Register map to update
++ * @reg: Register address to update, use CCI_REG#() macros to encode reg width
++ * @mask: Bitmask to change
++ * @val: New value for bitmask
++ * @err: Optional pointer to store errors, if a previous error is set
++ *       then the update will be skipped
++ *
++ * Note this uses read-modify-write to update the bits, atomicity with regards
++ * to other cci_*() register access functions is NOT guaranteed.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err);
++
++/**
++ * cci_multi_reg_write() - Write multiple registers to the device
++ *
++ * @map: Register map to write to
++ * @regs: Array of structures containing register-address, -value pairs to be
++ *        written, register-addresses use CCI_REG#() macros to encode reg width
++ * @num_regs: Number of registers to write
++ * @err: Optional pointer to store errors, if a previous error is set
++ *       then the write will be skipped
++ *
++ * Write multiple registers to the device where the set of register, value
++ * pairs are supplied in any order, possibly not all in a single range.
++ *
++ * Use of the CCI_REG#() macros to encode reg width is mandatory.
++ *
++ * For raw lists of register-address, -value pairs with only 8 bit
++ * wide writes regmap_multi_reg_write() can be used instead.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
++                      unsigned int num_regs, int *err);
++
++#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
++/**
++ * devm_cci_regmap_init_i2c() - Create regmap to use with cci_*() register
++ *                              access functions
++ *
++ * @client: i2c_client to create the regmap for
++ * @reg_addr_bits: register address width to use (8 or 16)
++ *
++ * Note the memory for the created regmap is devm() managed, tied to the client.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
++                                      int reg_addr_bits);
++#endif
++
++#endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch b/target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch
new file mode 100644 (file)
index 0000000..649e8bc
--- /dev/null
@@ -0,0 +1,114 @@
+From 3d108604ca669b83bb4918c4f5f0a02ddef84972 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Sun, 1 Oct 2023 13:20:12 +0200
+Subject: [PATCH] media: dt-bindings: Add OmniVision OV64A40
+
+Add bindings for OmniVision OV64A40.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ .../bindings/media/i2c/ovti,ov64a40.yaml      | 98 +++++++++++++++++++
+ 1 file changed, 98 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
+@@ -0,0 +1,98 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/ovti,ov64a40.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: OmniVision OV64A40 Image Sensor
++
++maintainers:
++  - Jacopo Mondi <jacopo.mondi@ideasonboard.com>
++
++allOf:
++  - $ref: /schemas/media/video-interface-devices.yaml#
++
++properties:
++  compatible:
++    const: ovti,ov64a40
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    maxItems: 1
++
++  avdd-supply:
++    description: Analog voltage supply, 2.8 volts
++
++  dvdd-supply:
++    description: Digital core voltage supply, 1.1 volts
++
++  dovdd-supply:
++    description: Digital I/O voltage supply, 1.8 volts
++
++  powerdown-gpios:
++    maxItems: 1
++
++  reset-gpios:
++    maxItems: 1
++
++  port:
++    $ref: /schemas/graph.yaml#/$defs/port-base
++    additionalProperties: false
++
++    properties:
++      endpoint:
++        $ref: /schemas/media/video-interfaces.yaml#
++        additionalProperties: false
++
++        properties:
++          bus-type:
++            enum:
++              - 1 # MIPI CSI-2 C-PHY
++              - 4 # MIPI CSI-2 D-PHY
++          data-lanes: true
++          link-frequencies: true
++          clock-noncontinuous: true
++          remote-endpoint: true
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - port
++
++unevaluatedProperties: false
++
++examples:
++  - |
++      #include <dt-bindings/gpio/gpio.h>
++
++      i2c {
++          #address-cells = <1>;
++          #size-cells = <0>;
++
++          camera@36 {
++              compatible = "ovti,ov64a40";
++              reg = <0x36>;
++              clocks = <&camera_clk>;
++              dovdd-supply = <&vgen4_reg>;
++              avdd-supply = <&vgen3_reg>;
++              dvdd-supply = <&vgen2_reg>;
++              powerdown-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
++              reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
++              rotation = <180>;
++              orientation = <2>;
++
++              port {
++                  endpoint {
++                      remote-endpoint = <&mipi_csi2_in>;
++                      bus-type = <4>;
++                      data-lanes = <1 2 3 4>;
++                      link-frequencies = /bits/ 64 <456000000>;
++                  };
++              };
++          };
++      };
++
++...
diff --git a/target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch
new file mode 100644 (file)
index 0000000..82eda14
--- /dev/null
@@ -0,0 +1,83 @@
+From 31c2999e543c245f7b96af3e73cd18e1036bfe7b Mon Sep 17 00:00:00 2001
+From: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Date: Thu, 14 Sep 2023 17:03:24 +0100
+Subject: [PATCH] media: dt-bindings: i2c: Add Rohm BU64754 bindings
+
+Add YAML device tree bindings for the ROHM BU64754 VCM Motor Driver for
+Camera Autofocus.
+
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ .../bindings/media/i2c/rohm,bu64754.yaml      | 48 +++++++++++++++++++
+ MAINTAINERS                                   |  7 +++
+ 2 files changed, 55 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
+@@ -0,0 +1,48 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++# Copyright (C) 2023 Ideas on Board Oy.
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/rohm,bu64754.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: ROHM BU64754 Actuator Driver for Camera Autofocus
++
++maintainers:
++  - Kieran Bingham <kieran.bingham@ideasonboard.com>
++
++description: |
++  The BU64754GWZ is an actuator driver IC which can control the actuator
++  position precisely using an internal Hall Sensor.
++
++properties:
++  compatible:
++    items:
++      - enum:
++          - rohm,bu64754
++
++  reg:
++    maxItems: 1
++
++  vdd-supply:
++    description:
++      Definition of the regulator used as VDD power supply to the driver.
++
++required:
++  - compatible
++  - reg
++
++additionalProperties: false
++
++examples:
++  - |
++    i2c {
++        #address-cells = <1>;
++        #size-cells = <0>;
++
++        lens@76 {
++            compatible = "rohm,bu64754";
++            reg = <0x76>;
++            vdd-supply = <&cam1_reg>;
++        };
++    };
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -17895,6 +17895,13 @@ S:    Maintained
+ F:    Documentation/devicetree/bindings/iio/light/bh1750.yaml
+ F:    drivers/iio/light/bh1750.c
++ROHM BU64754 MOTOR DRIVER FOR CAMERA AUTOFOCUS
++M:    Kieran Bingham <kieran.bingham@ideasonboard.com>
++L:    linux-media@vger.kernel.org
++S:    Maintained
++T:    git git://linuxtv.org/media_tree.git
++F:    Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
++
+ ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
+ M:    Marek Vasut <marek.vasut+renesas@gmail.com>
+ L:    linux-kernel@vger.kernel.org
diff --git a/target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch b/target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch
new file mode 100644 (file)
index 0000000..2f3ea4b
--- /dev/null
@@ -0,0 +1,3763 @@
+From 87e3fcaad3017bdc91a7a79d2d1c874422ef87b0 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Thu, 20 Jul 2023 11:44:40 +0200
+Subject: [PATCH] media: i2c: Add driver for OmniVision OV64A40
+
+Add a driver for the OmniVision OV64A40 image sensor.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ MAINTAINERS                 |    8 +
+ drivers/media/i2c/Kconfig   |   14 +
+ drivers/media/i2c/Makefile  |    1 +
+ drivers/media/i2c/ov64a40.c | 3694 +++++++++++++++++++++++++++++++++++
+ 4 files changed, 3717 insertions(+)
+ create mode 100644 drivers/media/i2c/ov64a40.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15321,6 +15321,14 @@ S:    Maintained
+ T:    git git://linuxtv.org/media_tree.git
+ F:    drivers/media/i2c/ov5695.c
++OMNIVISION OV64A40 SENSOR DRIVER
++M:    Jacopo Mondi <jacopo.mondi@ideasonboard.com>
++L:    linux-media@vger.kernel.org
++S:    Maintained
++T:    git git://linuxtv.org/media_tree.git
++F:    Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
++F:    drivers/media/i2c/ov64a40.c
++
+ OMNIVISION OV7670 SENSOR DRIVER
+ L:    linux-media@vger.kernel.org
+ S:    Orphan
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -631,6 +631,20 @@ config VIDEO_OV5695
+         To compile this driver as a module, choose M here: the
+         module will be called ov5695.
++config VIDEO_OV64A40
++      tristate "OmniVision OV64A40 sensor support"
++      depends on I2C && VIDEO_DEV
++      select MEDIA_CONTROLLER
++      select VIDEO_V4L2_SUBDEV_API
++      select V4L2_FWNODE
++      select V4L2_CCI_I2C
++      help
++        This is a Video4Linux2 sensor driver for the OmniVision
++        OV64A40 camera.
++
++        To compile this driver as a module, choose M here: the
++        module will be called ov64a40.
++
+ config VIDEO_OV6650
+       tristate "OmniVision OV6650 sensor support"
+       depends on I2C && VIDEO_DEV
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -96,6 +96,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+ obj-$(CONFIG_VIDEO_OV5675) += ov5675.o
+ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
+ obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
++obj-$(CONFIG_VIDEO_OV64A40) += ov64a40.o
+ obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
+ obj-$(CONFIG_VIDEO_OV7251) += ov7251.o
+ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
+--- /dev/null
++++ b/drivers/media/i2c/ov64a40.c
+@@ -0,0 +1,3694 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * V4L2 sensor driver for OmniVision OV64A40
++ *
++ * Copyright (C) 2023 Ideas On Board Oy
++ * Copyright (C) 2023 Arducam
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++
++#include <media/v4l2-cci.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <media/v4l2-subdev.h>
++
++#define OV64A40_XCLK_FREQ             24000000
++
++#define OV64A40_NATIVE_WIDTH          9286
++#define OV64A40_NATIVE_HEIGHT         6976
++#define OV64A40_PIXEL_ARRAY_TOP               0
++#define OV64A40_PIXEL_ARRAY_LEFT      0
++#define OV64A40_PIXEL_ARRAY_WIDTH     9248
++#define OV64A40_PIXEL_ARRAY_HEIGHT    6944
++
++#define OV64A40_PIXEL_RATE            300000000
++
++#define OV64A40_LINK_FREQ_360M                360000000
++#define OV64A40_LINK_FREQ_456M                456000000
++
++#define OV64A40_PLL1_PRE_DIV0         CCI_REG8(0x0301)
++#define OV64A40_PLL1_PRE_DIV          CCI_REG8(0x0303)
++#define OV64A40_PLL1_MULTIPLIER               CCI_REG16(0x0304)
++#define OV64A40_PLL1_M_DIV            CCI_REG8(0x0307)
++#define OV64A40_PLL2_SEL_BAK_SA1      CCI_REG8(0x0320)
++#define OV64A40_PLL2_PRE_DIV          CCI_REG8(0x0323)
++#define OV64A40_PLL2_MULTIPLIER               CCI_REG16(0x0324)
++#define OV64A40_PLL2_PRE_DIV0         CCI_REG8(0x0326)
++#define OV64A40_PLL2_DIVDAC           CCI_REG8(0x0329)
++#define OV64A40_PLL2_DIVSP            CCI_REG8(0x032d)
++#define OV64A40_PLL2_DACPREDIV                CCI_REG8(0x032e)
++
++/* TODO: validate vblank_min, it's not characterized in the datasheet. */
++#define OV64A40_VBLANK_MIN            128
++#define OV64A40_VTS_MAX                       0xffffff
++
++#define OV64A40_REG_MEC_LONG_EXPO     CCI_REG24(0x3500)
++#define OV64A40_EXPOSURE_MIN          16
++#define OV64A40_EXPOSURE_MARGIN               32
++
++#define OV64A40_REG_MEC_LONG_GAIN     CCI_REG16(0x3508)
++#define OV64A40_ANA_GAIN_MIN          0x80
++#define OV64A40_ANA_GAIN_MAX          0x7ff
++#define OV64A40_ANA_GAIN_DEFAULT      0x80
++
++#define OV64A40_REG_TIMING_CTRL0      CCI_REG16(0x3800)
++#define OV64A40_REG_TIMING_CTRL2      CCI_REG16(0x3802)
++#define OV64A40_REG_TIMING_CTRL4      CCI_REG16(0x3804)
++#define OV64A40_REG_TIMING_CTRL6      CCI_REG16(0x3806)
++#define OV64A40_REG_TIMING_CTRL8      CCI_REG16(0x3808)
++#define OV64A40_REG_TIMING_CTRLA      CCI_REG16(0x380a)
++#define OV64A40_REG_TIMING_CTRLC      CCI_REG16(0x380c)
++#define OV64A40_REG_TIMING_CTRLE      CCI_REG16(0x380e)
++#define OV64A40_REG_TIMING_CTRL10     CCI_REG16(0x3810)
++#define OV64A40_REG_TIMING_CTRL12     CCI_REG16(0x3812)
++
++/*
++ * Careful: a typo in the datasheet calls this register
++ * OV64A40_REG_TIMING_CTRL20.
++ */
++#define OV64A40_REG_TIMING_CTRL14     CCI_REG8(0x3814)
++#define OV64A40_REG_TIMING_CTRL15     CCI_REG8(0x3815)
++#define OV64A40_ODD_INC_SHIFT         4
++#define OV64A40_SKIPPING_CONFIG(_odd, _even) \
++                              (((_odd) << OV64A40_ODD_INC_SHIFT) | (_even))
++
++#define OV64A40_REG_TIMING_CTRL_20    CCI_REG8(0x3820)
++#define OV64A40_TIMING_CTRL_20_VFLIP  BIT(2)
++#define OV64A40_TIMING_CTRL_20_VBIN   BIT(1)
++
++#define OV64A40_REG_TIMING_CTRL_21    CCI_REG8(0x3821)
++#define OV64A40_TIMING_CTRL_21_HBIN   BIT(4)
++#define OV64A40_TIMING_CTRL_21_HFLIP  BIT(2)
++#define OV64A40_TIMING_CTRL_21_DSPEED BIT(0)
++#define OV64A40_TIMING_CTRL_21_HBIN_CONF \
++                                      (OV64A40_TIMING_CTRL_21_HBIN | \
++                                       OV64A40_TIMING_CTRL_21_DSPEED)
++
++#define OV64A40_REG_TIMINGS_VTS_HIGH  CCI_REG8(0x3840)
++#define OV64A40_REG_TIMINGS_VTS_MID   CCI_REG8(0x380e)
++#define OV64A40_REG_TIMINGS_VTS_LOW   CCI_REG8(0x380f)
++
++/* The test pattern control is weirdly named PRE_ISP_2325_D2V2_TOP_1 in TRM. */
++#define OV64A40_REG_TEST_PATTERN      CCI_REG8(0x50c1)
++#define OV64A40_TEST_PATTERN_DISABLED 0x00
++#define OV64A40_TEST_PATTERN_TYPE1    BIT(0)
++#define OV64A40_TEST_PATTERN_TYPE2    (BIT(4) | BIT(0))
++#define OV64A40_TEST_PATTERN_TYPE3    (BIT(5) | BIT(0))
++#define OV64A40_TEST_PATTERN_TYPE4    (BIT(5) | BIT(4) | BIT(0))
++
++#define OV64A40_REG_CHIP_ID           CCI_REG24(0x300a)
++#define OV64A40_CHIP_ID                       0x566441
++
++#define OV64A40_REG_SMIA              CCI_REG8(0x0100)
++#define OV64A40_REG_SMIA_STREAMING    BIT(0)
++
++enum ov64a40_link_freq_ids {
++      OV64A40_LINK_FREQ_456M_ID,
++      OV64A40_LINK_FREQ_360M_ID,
++      OV64A40_NUM_LINK_FREQ,
++};
++
++static const char * const ov64a40_supply_names[] = {
++      /* Supplies can be enabled in any order */
++      "avdd",         /* Analog (2.8V) supply */
++      "dovdd",        /* Digital Core (1.8V) supply */
++      "dvdd",         /* IF (1.1V) supply */
++};
++
++static const char * const ov64a40_test_pattern_menu[] = {
++      "Disabled",
++      "Type1",
++      "Type2",
++      "Type3",
++      "Type4",
++};
++
++static const int ov64a40_test_pattern_val[] = {
++      OV64A40_TEST_PATTERN_DISABLED,
++      OV64A40_TEST_PATTERN_TYPE1,
++      OV64A40_TEST_PATTERN_TYPE2,
++      OV64A40_TEST_PATTERN_TYPE3,
++      OV64A40_TEST_PATTERN_TYPE4,
++};
++
++static const unsigned int ov64a40_mbus_codes[] = {
++      MEDIA_BUS_FMT_SBGGR10_1X10,
++      MEDIA_BUS_FMT_SGRBG10_1X10,
++      MEDIA_BUS_FMT_SGBRG10_1X10,
++      MEDIA_BUS_FMT_SRGGB10_1X10,
++};
++
++static const struct cci_reg_sequence ov64a40_init[] = {
++      { CCI_REG8(0x0103), 0x01 }, { CCI_REG8(0x0301), 0x88 },
++      { CCI_REG8(0x0304), 0x00 }, { CCI_REG8(0x0305), 0x96 },
++      { CCI_REG8(0x0306), 0x03 }, { CCI_REG8(0x0307), 0x00 },
++      { CCI_REG8(0x0345), 0x2c }, { CCI_REG8(0x034a), 0x02 },
++      { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x0350), 0xc0 },
++      { CCI_REG8(0x0360), 0x09 }, { CCI_REG8(0x3012), 0x31 },
++      { CCI_REG8(0x3015), 0xf0 }, { CCI_REG8(0x3017), 0xf0 },
++      { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 },
++      { CCI_REG8(0x3022), 0xf0 }, { CCI_REG8(0x3400), 0x08 },
++      { CCI_REG8(0x3608), 0x41 }, { CCI_REG8(0x3421), 0x02 },
++      { CCI_REG8(0x3500), 0x00 }, { CCI_REG8(0x3501), 0x00 },
++      { CCI_REG8(0x3502), 0x18 }, { CCI_REG8(0x3504), 0x0c },
++      { CCI_REG8(0x3508), 0x01 }, { CCI_REG8(0x3509), 0x00 },
++      { CCI_REG8(0x350a), 0x01 }, { CCI_REG8(0x350b), 0x00 },
++      { CCI_REG8(0x350b), 0x00 }, { CCI_REG8(0x3540), 0x00 },
++      { CCI_REG8(0x3541), 0x00 }, { CCI_REG8(0x3542), 0x08 },
++      { CCI_REG8(0x3548), 0x01 }, { CCI_REG8(0x3549), 0xa0 },
++      { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3549), 0x00 },
++      { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3580), 0x00 },
++      { CCI_REG8(0x3581), 0x00 }, { CCI_REG8(0x3582), 0x04 },
++      { CCI_REG8(0x3588), 0x01 }, { CCI_REG8(0x3589), 0xf0 },
++      { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x3589), 0x00 },
++      { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x360d), 0x83 },
++      { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3617), 0x31 },
++      { CCI_REG8(0x3623), 0x10 }, { CCI_REG8(0x3633), 0x03 },
++      { CCI_REG8(0x3634), 0x03 }, { CCI_REG8(0x3635), 0x77 },
++      { CCI_REG8(0x3640), 0x19 }, { CCI_REG8(0x3641), 0x80 },
++      { CCI_REG8(0x364d), 0x0f }, { CCI_REG8(0x3680), 0x80 },
++      { CCI_REG8(0x3682), 0x00 }, { CCI_REG8(0x3683), 0x00 },
++      { CCI_REG8(0x3684), 0x07 }, { CCI_REG8(0x3688), 0x01 },
++      { CCI_REG8(0x3689), 0x08 }, { CCI_REG8(0x368a), 0x26 },
++      { CCI_REG8(0x368b), 0xc8 }, { CCI_REG8(0x368e), 0x70 },
++      { CCI_REG8(0x368f), 0x00 }, { CCI_REG8(0x3692), 0x04 },
++      { CCI_REG8(0x3693), 0x00 }, { CCI_REG8(0x3696), 0xd1 },
++      { CCI_REG8(0x3697), 0xe0 }, { CCI_REG8(0x3698), 0x80 },
++      { CCI_REG8(0x3699), 0x2b }, { CCI_REG8(0x369a), 0x00 },
++      { CCI_REG8(0x369d), 0x00 }, { CCI_REG8(0x369e), 0x14 },
++      { CCI_REG8(0x369f), 0x20 }, { CCI_REG8(0x36a5), 0x80 },
++      { CCI_REG8(0x36a6), 0x00 }, { CCI_REG8(0x36a7), 0x00 },
++      { CCI_REG8(0x36a8), 0x00 }, { CCI_REG8(0x36b5), 0x17 },
++      { CCI_REG8(0x3701), 0x30 }, { CCI_REG8(0x3706), 0x2b },
++      { CCI_REG8(0x3709), 0x8d }, { CCI_REG8(0x370b), 0x4f },
++      { CCI_REG8(0x3711), 0x00 }, { CCI_REG8(0x3712), 0x01 },
++      { CCI_REG8(0x3713), 0x00 }, { CCI_REG8(0x3720), 0x08 },
++      { CCI_REG8(0x3727), 0x22 }, { CCI_REG8(0x3728), 0x01 },
++      { CCI_REG8(0x375e), 0x00 }, { CCI_REG8(0x3760), 0x08 },
++      { CCI_REG8(0x3761), 0x10 }, { CCI_REG8(0x3762), 0x08 },
++      { CCI_REG8(0x3765), 0x10 }, { CCI_REG8(0x3766), 0x18 },
++      { CCI_REG8(0x376a), 0x08 }, { CCI_REG8(0x376b), 0x00 },
++      { CCI_REG8(0x376d), 0x1b }, { CCI_REG8(0x3791), 0x2b },
++      { CCI_REG8(0x3793), 0x2b }, { CCI_REG8(0x3795), 0x2b },
++      { CCI_REG8(0x3797), 0x4f }, { CCI_REG8(0x3799), 0x4f },
++      { CCI_REG8(0x379b), 0x4f }, { CCI_REG8(0x37a0), 0x22 },
++      { CCI_REG8(0x37da), 0x04 }, { CCI_REG8(0x37f9), 0x02 },
++      { CCI_REG8(0x37fa), 0x02 }, { CCI_REG8(0x37fb), 0x02 },
++      { CCI_REG8(0x3814), 0x11 }, { CCI_REG8(0x3815), 0x11 },
++      { CCI_REG8(0x3820), 0x40 }, { CCI_REG8(0x3821), 0x04 },
++      { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3823), 0x04 },
++      { CCI_REG8(0x3827), 0x08 }, { CCI_REG8(0x3828), 0x00 },
++      { CCI_REG8(0x382a), 0x81 }, { CCI_REG8(0x382e), 0x70 },
++      { CCI_REG8(0x3837), 0x10 }, { CCI_REG8(0x3839), 0x00 },
++      { CCI_REG8(0x383b), 0x00 }, { CCI_REG8(0x383c), 0x00 },
++      { CCI_REG8(0x383d), 0x10 }, { CCI_REG8(0x383f), 0x00 },
++      { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0x8c },
++      { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x10 },
++      { CCI_REG8(0x3857), 0x10 }, { CCI_REG8(0x3858), 0x20 },
++      { CCI_REG8(0x3859), 0x20 }, { CCI_REG8(0x3894), 0x00 },
++      { CCI_REG8(0x3895), 0x00 }, { CCI_REG8(0x3896), 0x00 },
++      { CCI_REG8(0x3897), 0x00 }, { CCI_REG8(0x3900), 0x40 },
++      { CCI_REG8(0x3aed), 0x6e }, { CCI_REG8(0x3af1), 0x73 },
++      { CCI_REG8(0x3d86), 0x12 }, { CCI_REG8(0x3d87), 0x30 },
++      { CCI_REG8(0x3d8c), 0xab }, { CCI_REG8(0x3d8d), 0xb0 },
++      { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f00), 0x12 },
++      { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f01), 0x03 },
++      { CCI_REG8(0x4009), 0x01 }, { CCI_REG8(0x400e), 0xc6 },
++      { CCI_REG8(0x400f), 0x00 }, { CCI_REG8(0x4010), 0x28 },
++      { CCI_REG8(0x4011), 0x01 }, { CCI_REG8(0x4012), 0x0c },
++      { CCI_REG8(0x4015), 0x00 }, { CCI_REG8(0x4016), 0x1f },
++      { CCI_REG8(0x4017), 0x00 }, { CCI_REG8(0x4018), 0x07 },
++      { CCI_REG8(0x401a), 0x40 }, { CCI_REG8(0x4028), 0x01 },
++      { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4506), 0x01 },
++      { CCI_REG8(0x4508), 0x00 }, { CCI_REG8(0x4509), 0x35 },
++      { CCI_REG8(0x450a), 0x08 }, { CCI_REG8(0x450c), 0x00 },
++      { CCI_REG8(0x450d), 0x20 }, { CCI_REG8(0x450e), 0x00 },
++      { CCI_REG8(0x450f), 0x20 }, { CCI_REG8(0x451e), 0x00 },
++      { CCI_REG8(0x451f), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++      { CCI_REG8(0x4526), 0x00 }, { CCI_REG8(0x4527), 0x18 },
++      { CCI_REG8(0x4580), 0x01 }, { CCI_REG8(0x4583), 0x00 },
++      { CCI_REG8(0x4584), 0x00 }, { CCI_REG8(0x45c0), 0xa1 },
++      { CCI_REG8(0x4602), 0x08 }, { CCI_REG8(0x4603), 0x05 },
++      { CCI_REG8(0x4606), 0x12 }, { CCI_REG8(0x4607), 0x30 },
++      { CCI_REG8(0x460b), 0x00 }, { CCI_REG8(0x460d), 0x00 },
++      { CCI_REG8(0x4640), 0x00 }, { CCI_REG8(0x4641), 0x24 },
++      { CCI_REG8(0x4643), 0x08 }, { CCI_REG8(0x4645), 0x14 },
++      { CCI_REG8(0x4648), 0x0a }, { CCI_REG8(0x4649), 0x06 },
++      { CCI_REG8(0x464a), 0x00 }, { CCI_REG8(0x464b), 0x30 },
++      { CCI_REG8(0x4800), 0x04 }, { CCI_REG8(0x4802), 0x02 },
++      { CCI_REG8(0x480b), 0x10 }, { CCI_REG8(0x480c), 0x80 },
++      { CCI_REG8(0x480e), 0x04 }, { CCI_REG8(0x480f), 0x32 },
++      { CCI_REG8(0x481b), 0x12 }, { CCI_REG8(0x4833), 0x30 },
++      { CCI_REG8(0x4837), 0x08 }, { CCI_REG8(0x484b), 0x27 },
++      { CCI_REG8(0x4850), 0x42 }, { CCI_REG8(0x4851), 0xaa },
++      { CCI_REG8(0x4860), 0x01 }, { CCI_REG8(0x4861), 0xec },
++      { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x4888), 0x00 },
++      { CCI_REG8(0x4889), 0x03 }, { CCI_REG8(0x488c), 0x60 },
++      { CCI_REG8(0x4910), 0x28 }, { CCI_REG8(0x4911), 0x01 },
++      { CCI_REG8(0x4912), 0x0c }, { CCI_REG8(0x491a), 0x40 },
++      { CCI_REG8(0x4915), 0x00 }, { CCI_REG8(0x4916), 0x0f },
++      { CCI_REG8(0x4917), 0x00 }, { CCI_REG8(0x4918), 0x07 },
++      { CCI_REG8(0x4a10), 0x28 }, { CCI_REG8(0x4a11), 0x01 },
++      { CCI_REG8(0x4a12), 0x0c }, { CCI_REG8(0x4a1a), 0x40 },
++      { CCI_REG8(0x4a15), 0x00 }, { CCI_REG8(0x4a16), 0x0f },
++      { CCI_REG8(0x4a17), 0x00 }, { CCI_REG8(0x4a18), 0x07 },
++      { CCI_REG8(0x4d00), 0x04 }, { CCI_REG8(0x4d01), 0x5a },
++      { CCI_REG8(0x4d02), 0xbb }, { CCI_REG8(0x4d03), 0x84 },
++      { CCI_REG8(0x4d04), 0xd1 }, { CCI_REG8(0x4d05), 0x68 },
++      { CCI_REG8(0xc4fa), 0x10 }, { CCI_REG8(0x3b56), 0x0a },
++      { CCI_REG8(0x3b57), 0x0a }, { CCI_REG8(0x3b58), 0x0c },
++      { CCI_REG8(0x3b59), 0x10 }, { CCI_REG8(0x3a1d), 0x30 },
++      { CCI_REG8(0x3a1e), 0x30 }, { CCI_REG8(0x3a21), 0x30 },
++      { CCI_REG8(0x3a22), 0x30 }, { CCI_REG8(0x3992), 0x02 },
++      { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x39fb), 0x30 },
++      { CCI_REG8(0x39fc), 0x30 }, { CCI_REG8(0x39fd), 0x30 },
++      { CCI_REG8(0x39fe), 0x30 }, { CCI_REG8(0x3a6d), 0x83 },
++      { CCI_REG8(0x3a5e), 0x83 }, { CCI_REG8(0xc500), 0x12 },
++      { CCI_REG8(0xc501), 0x12 }, { CCI_REG8(0xc502), 0x12 },
++      { CCI_REG8(0xc503), 0x12 }, { CCI_REG8(0xc505), 0x12 },
++      { CCI_REG8(0xc506), 0x12 }, { CCI_REG8(0xc507), 0x12 },
++      { CCI_REG8(0xc508), 0x12 }, { CCI_REG8(0x3a77), 0x12 },
++      { CCI_REG8(0x3a73), 0x12 }, { CCI_REG8(0x3a7b), 0x12 },
++      { CCI_REG8(0x3a7f), 0x12 }, { CCI_REG8(0x3b2e), 0x13 },
++      { CCI_REG8(0x3b29), 0x13 }, { CCI_REG8(0xc439), 0x13 },
++      { CCI_REG8(0xc469), 0x13 }, { CCI_REG8(0xc41c), 0x89 },
++      { CCI_REG8(0x3618), 0x80 }, { CCI_REG8(0xc514), 0x51 },
++      { CCI_REG8(0xc515), 0x2c }, { CCI_REG8(0xc516), 0x16 },
++      { CCI_REG8(0xc517), 0x0d }, { CCI_REG8(0x3615), 0x7f },
++      { CCI_REG8(0x3632), 0x99 }, { CCI_REG8(0x3642), 0x00 },
++      { CCI_REG8(0x3645), 0x80 }, { CCI_REG8(0x3702), 0x2a },
++      { CCI_REG8(0x3703), 0x2a }, { CCI_REG8(0x3708), 0x2f },
++      { CCI_REG8(0x3721), 0x15 }, { CCI_REG8(0x3744), 0x28 },
++      { CCI_REG8(0x3991), 0x0c }, { CCI_REG8(0x371d), 0x24 },
++      { CCI_REG8(0x371f), 0x0c }, { CCI_REG8(0x374b), 0x03 },
++      { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x391d), 0x55 },
++      { CCI_REG8(0x391e), 0x52 }, { CCI_REG8(0x399d), 0x0c },
++      { CCI_REG8(0x3a2f), 0x01 }, { CCI_REG8(0x3a30), 0x01 },
++      { CCI_REG8(0x3a31), 0x01 }, { CCI_REG8(0x3a32), 0x01 },
++      { CCI_REG8(0x3a34), 0x01 }, { CCI_REG8(0x3a35), 0x01 },
++      { CCI_REG8(0x3a36), 0x01 }, { CCI_REG8(0x3a37), 0x01 },
++      { CCI_REG8(0x3a43), 0x01 }, { CCI_REG8(0x3a44), 0x01 },
++      { CCI_REG8(0x3a45), 0x01 }, { CCI_REG8(0x3a46), 0x01 },
++      { CCI_REG8(0x3a48), 0x01 }, { CCI_REG8(0x3a49), 0x01 },
++      { CCI_REG8(0x3a4a), 0x01 }, { CCI_REG8(0x3a4b), 0x01 },
++      { CCI_REG8(0x3a50), 0x14 }, { CCI_REG8(0x3a54), 0x14 },
++      { CCI_REG8(0x3a60), 0x20 }, { CCI_REG8(0x3a6f), 0x20 },
++      { CCI_REG8(0x3ac5), 0x01 }, { CCI_REG8(0x3ac6), 0x01 },
++      { CCI_REG8(0x3ac7), 0x01 }, { CCI_REG8(0x3ac8), 0x01 },
++      { CCI_REG8(0x3ac9), 0x01 }, { CCI_REG8(0x3aca), 0x01 },
++      { CCI_REG8(0x3acb), 0x01 }, { CCI_REG8(0x3acc), 0x01 },
++      { CCI_REG8(0x3acd), 0x01 }, { CCI_REG8(0x3ace), 0x01 },
++      { CCI_REG8(0x3acf), 0x01 }, { CCI_REG8(0x3ad0), 0x01 },
++      { CCI_REG8(0x3ad1), 0x01 }, { CCI_REG8(0x3ad2), 0x01 },
++      { CCI_REG8(0x3ad3), 0x01 }, { CCI_REG8(0x3ad4), 0x01 },
++      { CCI_REG8(0x3add), 0x1f }, { CCI_REG8(0x3adf), 0x24 },
++      { CCI_REG8(0x3aef), 0x1f }, { CCI_REG8(0x3af0), 0x24 },
++      { CCI_REG8(0x3b92), 0x08 }, { CCI_REG8(0x3b93), 0x08 },
++      { CCI_REG8(0x3b94), 0x08 }, { CCI_REG8(0x3b95), 0x08 },
++      { CCI_REG8(0x3be7), 0x1e }, { CCI_REG8(0x3be8), 0x26 },
++      { CCI_REG8(0xc44a), 0x20 }, { CCI_REG8(0xc44c), 0x20 },
++      { CCI_REG8(0xc483), 0x00 }, { CCI_REG8(0xc484), 0x00 },
++      { CCI_REG8(0xc485), 0x00 }, { CCI_REG8(0xc486), 0x00 },
++      { CCI_REG8(0xc487), 0x01 }, { CCI_REG8(0xc488), 0x01 },
++      { CCI_REG8(0xc489), 0x01 }, { CCI_REG8(0xc48a), 0x01 },
++      { CCI_REG8(0xc4c1), 0x00 }, { CCI_REG8(0xc4c2), 0x00 },
++      { CCI_REG8(0xc4c3), 0x00 }, { CCI_REG8(0xc4c4), 0x00 },
++      { CCI_REG8(0xc4c6), 0x10 }, { CCI_REG8(0xc4c7), 0x10 },
++      { CCI_REG8(0xc4c8), 0x10 }, { CCI_REG8(0xc4c9), 0x10 },
++      { CCI_REG8(0xc4ca), 0x10 }, { CCI_REG8(0xc4cb), 0x10 },
++      { CCI_REG8(0xc4cc), 0x10 }, { CCI_REG8(0xc4cd), 0x10 },
++      { CCI_REG8(0xc4ea), 0x07 }, { CCI_REG8(0xc4eb), 0x07 },
++      { CCI_REG8(0xc4ec), 0x07 }, { CCI_REG8(0xc4ed), 0x07 },
++      { CCI_REG8(0xc4ee), 0x07 }, { CCI_REG8(0xc4f6), 0x10 },
++      { CCI_REG8(0xc4f7), 0x10 }, { CCI_REG8(0xc4f8), 0x10 },
++      { CCI_REG8(0xc4f9), 0x10 }, { CCI_REG8(0xc518), 0x0e },
++      { CCI_REG8(0xc519), 0x0e }, { CCI_REG8(0xc51a), 0x0e },
++      { CCI_REG8(0xc51b), 0x0e }, { CCI_REG8(0xc51c), 0x0e },
++      { CCI_REG8(0xc51d), 0x0e }, { CCI_REG8(0xc51e), 0x0e },
++      { CCI_REG8(0xc51f), 0x0e }, { CCI_REG8(0xc520), 0x0e },
++      { CCI_REG8(0xc521), 0x0e }, { CCI_REG8(0xc522), 0x0e },
++      { CCI_REG8(0xc523), 0x0e }, { CCI_REG8(0xc524), 0x0e },
++      { CCI_REG8(0xc525), 0x0e }, { CCI_REG8(0xc526), 0x0e },
++      { CCI_REG8(0xc527), 0x0e }, { CCI_REG8(0xc528), 0x0e },
++      { CCI_REG8(0xc529), 0x0e }, { CCI_REG8(0xc52a), 0x0e },
++      { CCI_REG8(0xc52b), 0x0e }, { CCI_REG8(0xc52c), 0x0e },
++      { CCI_REG8(0xc52d), 0x0e }, { CCI_REG8(0xc52e), 0x0e },
++      { CCI_REG8(0xc52f), 0x0e }, { CCI_REG8(0xc530), 0x0e },
++      { CCI_REG8(0xc531), 0x0e }, { CCI_REG8(0xc532), 0x0e },
++      { CCI_REG8(0xc533), 0x0e }, { CCI_REG8(0xc534), 0x0e },
++      { CCI_REG8(0xc535), 0x0e }, { CCI_REG8(0xc536), 0x0e },
++      { CCI_REG8(0xc537), 0x0e }, { CCI_REG8(0xc538), 0x0e },
++      { CCI_REG8(0xc539), 0x0e }, { CCI_REG8(0xc53a), 0x0e },
++      { CCI_REG8(0xc53b), 0x0e }, { CCI_REG8(0xc53c), 0x0e },
++      { CCI_REG8(0xc53d), 0x0e }, { CCI_REG8(0xc53e), 0x0e },
++      { CCI_REG8(0xc53f), 0x0e }, { CCI_REG8(0xc540), 0x0e },
++      { CCI_REG8(0xc541), 0x0e }, { CCI_REG8(0xc542), 0x0e },
++      { CCI_REG8(0xc543), 0x0e }, { CCI_REG8(0xc544), 0x0e },
++      { CCI_REG8(0xc545), 0x0e }, { CCI_REG8(0xc546), 0x0e },
++      { CCI_REG8(0xc547), 0x0e }, { CCI_REG8(0xc548), 0x0e },
++      { CCI_REG8(0xc549), 0x0e }, { CCI_REG8(0xc57f), 0x22 },
++      { CCI_REG8(0xc580), 0x22 }, { CCI_REG8(0xc581), 0x22 },
++      { CCI_REG8(0xc582), 0x22 }, { CCI_REG8(0xc583), 0x22 },
++      { CCI_REG8(0xc584), 0x22 }, { CCI_REG8(0xc585), 0x22 },
++      { CCI_REG8(0xc586), 0x22 }, { CCI_REG8(0xc587), 0x22 },
++      { CCI_REG8(0xc588), 0x22 }, { CCI_REG8(0xc589), 0x22 },
++      { CCI_REG8(0xc58a), 0x22 }, { CCI_REG8(0xc58b), 0x22 },
++      { CCI_REG8(0xc58c), 0x22 }, { CCI_REG8(0xc58d), 0x22 },
++      { CCI_REG8(0xc58e), 0x22 }, { CCI_REG8(0xc58f), 0x22 },
++      { CCI_REG8(0xc590), 0x22 }, { CCI_REG8(0xc591), 0x22 },
++      { CCI_REG8(0xc592), 0x22 }, { CCI_REG8(0xc598), 0x22 },
++      { CCI_REG8(0xc599), 0x22 }, { CCI_REG8(0xc59a), 0x22 },
++      { CCI_REG8(0xc59b), 0x22 }, { CCI_REG8(0xc59c), 0x22 },
++      { CCI_REG8(0xc59d), 0x22 }, { CCI_REG8(0xc59e), 0x22 },
++      { CCI_REG8(0xc59f), 0x22 }, { CCI_REG8(0xc5a0), 0x22 },
++      { CCI_REG8(0xc5a1), 0x22 }, { CCI_REG8(0xc5a2), 0x22 },
++      { CCI_REG8(0xc5a3), 0x22 }, { CCI_REG8(0xc5a4), 0x22 },
++      { CCI_REG8(0xc5a5), 0x22 }, { CCI_REG8(0xc5a6), 0x22 },
++      { CCI_REG8(0xc5a7), 0x22 }, { CCI_REG8(0xc5a8), 0x22 },
++      { CCI_REG8(0xc5a9), 0x22 }, { CCI_REG8(0xc5aa), 0x22 },
++      { CCI_REG8(0xc5ab), 0x22 }, { CCI_REG8(0xc5b1), 0x2a },
++      { CCI_REG8(0xc5b2), 0x2a }, { CCI_REG8(0xc5b3), 0x2a },
++      { CCI_REG8(0xc5b4), 0x2a }, { CCI_REG8(0xc5b5), 0x2a },
++      { CCI_REG8(0xc5b6), 0x2a }, { CCI_REG8(0xc5b7), 0x2a },
++      { CCI_REG8(0xc5b8), 0x2a }, { CCI_REG8(0xc5b9), 0x2a },
++      { CCI_REG8(0xc5ba), 0x2a }, { CCI_REG8(0xc5bb), 0x2a },
++      { CCI_REG8(0xc5bc), 0x2a }, { CCI_REG8(0xc5bd), 0x2a },
++      { CCI_REG8(0xc5be), 0x2a }, { CCI_REG8(0xc5bf), 0x2a },
++      { CCI_REG8(0xc5c0), 0x2a }, { CCI_REG8(0xc5c1), 0x2a },
++      { CCI_REG8(0xc5c2), 0x2a }, { CCI_REG8(0xc5c3), 0x2a },
++      { CCI_REG8(0xc5c4), 0x2a }, { CCI_REG8(0xc5ca), 0x2a },
++      { CCI_REG8(0xc5cb), 0x2a }, { CCI_REG8(0xc5cc), 0x2a },
++      { CCI_REG8(0xc5cd), 0x2a }, { CCI_REG8(0xc5ce), 0x2a },
++      { CCI_REG8(0xc5cf), 0x2a }, { CCI_REG8(0xc5d0), 0x2a },
++      { CCI_REG8(0xc5d1), 0x2a }, { CCI_REG8(0xc5d2), 0x2a },
++      { CCI_REG8(0xc5d3), 0x2a }, { CCI_REG8(0xc5d4), 0x2a },
++      { CCI_REG8(0xc5d5), 0x2a }, { CCI_REG8(0xc5d6), 0x2a },
++      { CCI_REG8(0xc5d7), 0x2a }, { CCI_REG8(0xc5d8), 0x2a },
++      { CCI_REG8(0xc5d9), 0x2a }, { CCI_REG8(0xc5da), 0x2a },
++      { CCI_REG8(0xc5db), 0x2a }, { CCI_REG8(0xc5dc), 0x2a },
++      { CCI_REG8(0xc5dd), 0x2a }, { CCI_REG8(0xc5e8), 0x22 },
++      { CCI_REG8(0xc5ea), 0x22 }, { CCI_REG8(0x4540), 0x12 },
++      { CCI_REG8(0x4541), 0x30 }, { CCI_REG8(0x3d86), 0x12 },
++      { CCI_REG8(0x3d87), 0x30 }, { CCI_REG8(0x4606), 0x12 },
++      { CCI_REG8(0x4607), 0x30 }, { CCI_REG8(0x4648), 0x0a },
++      { CCI_REG8(0x4649), 0x06 }, { CCI_REG8(0x3220), 0x12 },
++      { CCI_REG8(0x3221), 0x30 }, { CCI_REG8(0x40c2), 0x12 },
++      { CCI_REG8(0x49c2), 0x12 }, { CCI_REG8(0x4ac2), 0x12 },
++      { CCI_REG8(0x40c3), 0x30 }, { CCI_REG8(0x49c3), 0x30 },
++      { CCI_REG8(0x4ac3), 0x30 }, { CCI_REG8(0x36b0), 0x12 },
++      { CCI_REG8(0x36b1), 0x30 }, { CCI_REG8(0x45cb), 0x12 },
++      { CCI_REG8(0x45cc), 0x30 }, { CCI_REG8(0x4585), 0x12 },
++      { CCI_REG8(0x4586), 0x30 }, { CCI_REG8(0x36b2), 0x12 },
++      { CCI_REG8(0x36b3), 0x30 }, { CCI_REG8(0x5a40), 0x75 },
++      { CCI_REG8(0x5a41), 0x75 }, { CCI_REG8(0x5a42), 0x75 },
++      { CCI_REG8(0x5a43), 0x75 }, { CCI_REG8(0x5a44), 0x75 },
++      { CCI_REG8(0x5a45), 0x75 }, { CCI_REG8(0x5a46), 0x75 },
++      { CCI_REG8(0x5a47), 0x75 }, { CCI_REG8(0x5a48), 0x75 },
++      { CCI_REG8(0x5a49), 0x75 }, { CCI_REG8(0x5a4a), 0x75 },
++      { CCI_REG8(0x5a4b), 0x75 }, { CCI_REG8(0x5a4c), 0x75 },
++      { CCI_REG8(0x5a4d), 0x75 }, { CCI_REG8(0x5a4e), 0x75 },
++      { CCI_REG8(0x5a4f), 0x75 }, { CCI_REG8(0x5a50), 0x75 },
++      { CCI_REG8(0x5a51), 0x75 }, { CCI_REG8(0x5a52), 0x75 },
++      { CCI_REG8(0x5a53), 0x75 }, { CCI_REG8(0x5a54), 0x75 },
++      { CCI_REG8(0x5a55), 0x75 }, { CCI_REG8(0x5a56), 0x75 },
++      { CCI_REG8(0x5a57), 0x75 }, { CCI_REG8(0x5a58), 0x75 },
++      { CCI_REG8(0x5a59), 0x75 }, { CCI_REG8(0x5a5a), 0x75 },
++      { CCI_REG8(0x5a5b), 0x75 }, { CCI_REG8(0x5a5c), 0x75 },
++      { CCI_REG8(0x5a5d), 0x75 }, { CCI_REG8(0x5a5e), 0x75 },
++      { CCI_REG8(0x5a5f), 0x75 }, { CCI_REG8(0x5a60), 0x75 },
++      { CCI_REG8(0x5a61), 0x75 }, { CCI_REG8(0x5a62), 0x75 },
++      { CCI_REG8(0x5a63), 0x75 }, { CCI_REG8(0x5a64), 0x75 },
++      { CCI_REG8(0x5a65), 0x75 }, { CCI_REG8(0x5a66), 0x75 },
++      { CCI_REG8(0x5a67), 0x75 }, { CCI_REG8(0x5a68), 0x75 },
++      { CCI_REG8(0x5a69), 0x75 }, { CCI_REG8(0x5a6a), 0x75 },
++      { CCI_REG8(0x5a6b), 0x75 }, { CCI_REG8(0x5a6c), 0x75 },
++      { CCI_REG8(0x5a6d), 0x75 }, { CCI_REG8(0x5a6e), 0x75 },
++      { CCI_REG8(0x5a6f), 0x75 }, { CCI_REG8(0x5a70), 0x75 },
++      { CCI_REG8(0x5a71), 0x75 }, { CCI_REG8(0x5a72), 0x75 },
++      { CCI_REG8(0x5a73), 0x75 }, { CCI_REG8(0x5a74), 0x75 },
++      { CCI_REG8(0x5a75), 0x75 }, { CCI_REG8(0x5a76), 0x75 },
++      { CCI_REG8(0x5a77), 0x75 }, { CCI_REG8(0x5a78), 0x75 },
++      { CCI_REG8(0x5a79), 0x75 }, { CCI_REG8(0x5a7a), 0x75 },
++      { CCI_REG8(0x5a7b), 0x75 }, { CCI_REG8(0x5a7c), 0x75 },
++      { CCI_REG8(0x5a7d), 0x75 }, { CCI_REG8(0x5a7e), 0x75 },
++      { CCI_REG8(0x5a7f), 0x75 }, { CCI_REG8(0x5a80), 0x75 },
++      { CCI_REG8(0x5a81), 0x75 }, { CCI_REG8(0x5a82), 0x75 },
++      { CCI_REG8(0x5a83), 0x75 }, { CCI_REG8(0x5a84), 0x75 },
++      { CCI_REG8(0x5a85), 0x75 }, { CCI_REG8(0x5a86), 0x75 },
++      { CCI_REG8(0x5a87), 0x75 }, { CCI_REG8(0x5a88), 0x75 },
++      { CCI_REG8(0x5a89), 0x75 }, { CCI_REG8(0x5a8a), 0x75 },
++      { CCI_REG8(0x5a8b), 0x75 }, { CCI_REG8(0x5a8c), 0x75 },
++      { CCI_REG8(0x5a8d), 0x75 }, { CCI_REG8(0x5a8e), 0x75 },
++      { CCI_REG8(0x5a8f), 0x75 }, { CCI_REG8(0x5a90), 0x75 },
++      { CCI_REG8(0x5a91), 0x75 }, { CCI_REG8(0x5a92), 0x75 },
++      { CCI_REG8(0x5a93), 0x75 }, { CCI_REG8(0x5a94), 0x75 },
++      { CCI_REG8(0x5a95), 0x75 }, { CCI_REG8(0x5a96), 0x75 },
++      { CCI_REG8(0x5a97), 0x75 }, { CCI_REG8(0x5a98), 0x75 },
++      { CCI_REG8(0x5a99), 0x75 }, { CCI_REG8(0x5a9a), 0x75 },
++      { CCI_REG8(0x5a9b), 0x75 }, { CCI_REG8(0x5a9c), 0x75 },
++      { CCI_REG8(0x5a9d), 0x75 }, { CCI_REG8(0x5a9e), 0x75 },
++      { CCI_REG8(0x5a9f), 0x75 }, { CCI_REG8(0x5aa0), 0x75 },
++      { CCI_REG8(0x5aa1), 0x75 }, { CCI_REG8(0x5aa2), 0x75 },
++      { CCI_REG8(0x5aa3), 0x75 }, { CCI_REG8(0x5aa4), 0x75 },
++      { CCI_REG8(0x5aa5), 0x75 }, { CCI_REG8(0x5aa6), 0x75 },
++      { CCI_REG8(0x5aa7), 0x75 }, { CCI_REG8(0x5aa8), 0x75 },
++      { CCI_REG8(0x5aa9), 0x75 }, { CCI_REG8(0x5aaa), 0x75 },
++      { CCI_REG8(0x5aab), 0x75 }, { CCI_REG8(0x5aac), 0x75 },
++      { CCI_REG8(0x5aad), 0x75 }, { CCI_REG8(0x5aae), 0x75 },
++      { CCI_REG8(0x5aaf), 0x75 }, { CCI_REG8(0x5ab0), 0x75 },
++      { CCI_REG8(0x5ab1), 0x75 }, { CCI_REG8(0x5ab2), 0x75 },
++      { CCI_REG8(0x5ab3), 0x75 }, { CCI_REG8(0x5ab4), 0x75 },
++      { CCI_REG8(0x5ab5), 0x75 }, { CCI_REG8(0x5ab6), 0x75 },
++      { CCI_REG8(0x5ab7), 0x75 }, { CCI_REG8(0x5ab8), 0x75 },
++      { CCI_REG8(0x5ab9), 0x75 }, { CCI_REG8(0x5aba), 0x75 },
++      { CCI_REG8(0x5abb), 0x75 }, { CCI_REG8(0x5abc), 0x75 },
++      { CCI_REG8(0x5abd), 0x75 }, { CCI_REG8(0x5abe), 0x75 },
++      { CCI_REG8(0x5abf), 0x75 }, { CCI_REG8(0x5ac0), 0x75 },
++      { CCI_REG8(0x5ac1), 0x75 }, { CCI_REG8(0x5ac2), 0x75 },
++      { CCI_REG8(0x5ac3), 0x75 }, { CCI_REG8(0x5ac4), 0x75 },
++      { CCI_REG8(0x5ac5), 0x75 }, { CCI_REG8(0x5ac6), 0x75 },
++      { CCI_REG8(0x5ac7), 0x75 }, { CCI_REG8(0x5ac8), 0x75 },
++      { CCI_REG8(0x5ac9), 0x75 }, { CCI_REG8(0x5aca), 0x75 },
++      { CCI_REG8(0x5acb), 0x75 }, { CCI_REG8(0x5acc), 0x75 },
++      { CCI_REG8(0x5acd), 0x75 }, { CCI_REG8(0x5ace), 0x75 },
++      { CCI_REG8(0x5acf), 0x75 }, { CCI_REG8(0x5ad0), 0x75 },
++      { CCI_REG8(0x5ad1), 0x75 }, { CCI_REG8(0x5ad2), 0x75 },
++      { CCI_REG8(0x5ad3), 0x75 }, { CCI_REG8(0x5ad4), 0x75 },
++      { CCI_REG8(0x5ad5), 0x75 }, { CCI_REG8(0x5ad6), 0x75 },
++      { CCI_REG8(0x5ad7), 0x75 }, { CCI_REG8(0x5ad8), 0x75 },
++      { CCI_REG8(0x5ad9), 0x75 }, { CCI_REG8(0x5ada), 0x75 },
++      { CCI_REG8(0x5adb), 0x75 }, { CCI_REG8(0x5adc), 0x75 },
++      { CCI_REG8(0x5add), 0x75 }, { CCI_REG8(0x5ade), 0x75 },
++      { CCI_REG8(0x5adf), 0x75 }, { CCI_REG8(0x5ae0), 0x75 },
++      { CCI_REG8(0x5ae1), 0x75 }, { CCI_REG8(0x5ae2), 0x75 },
++      { CCI_REG8(0x5ae3), 0x75 }, { CCI_REG8(0x5ae4), 0x75 },
++      { CCI_REG8(0x5ae5), 0x75 }, { CCI_REG8(0x5ae6), 0x75 },
++      { CCI_REG8(0x5ae7), 0x75 }, { CCI_REG8(0x5ae8), 0x75 },
++      { CCI_REG8(0x5ae9), 0x75 }, { CCI_REG8(0x5aea), 0x75 },
++      { CCI_REG8(0x5aeb), 0x75 }, { CCI_REG8(0x5aec), 0x75 },
++      { CCI_REG8(0x5aed), 0x75 }, { CCI_REG8(0x5aee), 0x75 },
++      { CCI_REG8(0x5aef), 0x75 }, { CCI_REG8(0x5af0), 0x75 },
++      { CCI_REG8(0x5af1), 0x75 }, { CCI_REG8(0x5af2), 0x75 },
++      { CCI_REG8(0x5af3), 0x75 }, { CCI_REG8(0x5af4), 0x75 },
++      { CCI_REG8(0x5af5), 0x75 }, { CCI_REG8(0x5af6), 0x75 },
++      { CCI_REG8(0x5af7), 0x75 }, { CCI_REG8(0x5af8), 0x75 },
++      { CCI_REG8(0x5af9), 0x75 }, { CCI_REG8(0x5afa), 0x75 },
++      { CCI_REG8(0x5afb), 0x75 }, { CCI_REG8(0x5afc), 0x75 },
++      { CCI_REG8(0x5afd), 0x75 }, { CCI_REG8(0x5afe), 0x75 },
++      { CCI_REG8(0x5aff), 0x75 }, { CCI_REG8(0x5b00), 0x75 },
++      { CCI_REG8(0x5b01), 0x75 }, { CCI_REG8(0x5b02), 0x75 },
++      { CCI_REG8(0x5b03), 0x75 }, { CCI_REG8(0x5b04), 0x75 },
++      { CCI_REG8(0x5b05), 0x75 }, { CCI_REG8(0x5b06), 0x75 },
++      { CCI_REG8(0x5b07), 0x75 }, { CCI_REG8(0x5b08), 0x75 },
++      { CCI_REG8(0x5b09), 0x75 }, { CCI_REG8(0x5b0a), 0x75 },
++      { CCI_REG8(0x5b0b), 0x75 }, { CCI_REG8(0x5b0c), 0x75 },
++      { CCI_REG8(0x5b0d), 0x75 }, { CCI_REG8(0x5b0e), 0x75 },
++      { CCI_REG8(0x5b0f), 0x75 }, { CCI_REG8(0x5b10), 0x75 },
++      { CCI_REG8(0x5b11), 0x75 }, { CCI_REG8(0x5b12), 0x75 },
++      { CCI_REG8(0x5b13), 0x75 }, { CCI_REG8(0x5b14), 0x75 },
++      { CCI_REG8(0x5b15), 0x75 }, { CCI_REG8(0x5b16), 0x75 },
++      { CCI_REG8(0x5b17), 0x75 }, { CCI_REG8(0x5b18), 0x75 },
++      { CCI_REG8(0x5b19), 0x75 }, { CCI_REG8(0x5b1a), 0x75 },
++      { CCI_REG8(0x5b1b), 0x75 }, { CCI_REG8(0x5b1c), 0x75 },
++      { CCI_REG8(0x5b1d), 0x75 }, { CCI_REG8(0x5b1e), 0x75 },
++      { CCI_REG8(0x5b1f), 0x75 }, { CCI_REG8(0x5b20), 0x75 },
++      { CCI_REG8(0x5b21), 0x75 }, { CCI_REG8(0x5b22), 0x75 },
++      { CCI_REG8(0x5b23), 0x75 }, { CCI_REG8(0x5b24), 0x75 },
++      { CCI_REG8(0x5b25), 0x75 }, { CCI_REG8(0x5b26), 0x75 },
++      { CCI_REG8(0x5b27), 0x75 }, { CCI_REG8(0x5b28), 0x75 },
++      { CCI_REG8(0x5b29), 0x75 }, { CCI_REG8(0x5b2a), 0x75 },
++      { CCI_REG8(0x5b2b), 0x75 }, { CCI_REG8(0x5b2c), 0x75 },
++      { CCI_REG8(0x5b2d), 0x75 }, { CCI_REG8(0x5b2e), 0x75 },
++      { CCI_REG8(0x5b2f), 0x75 }, { CCI_REG8(0x5b30), 0x75 },
++      { CCI_REG8(0x5b31), 0x75 }, { CCI_REG8(0x5b32), 0x75 },
++      { CCI_REG8(0x5b33), 0x75 }, { CCI_REG8(0x5b34), 0x75 },
++      { CCI_REG8(0x5b35), 0x75 }, { CCI_REG8(0x5b36), 0x75 },
++      { CCI_REG8(0x5b37), 0x75 }, { CCI_REG8(0x5b38), 0x75 },
++      { CCI_REG8(0x5b39), 0x75 }, { CCI_REG8(0x5b3a), 0x75 },
++      { CCI_REG8(0x5b3b), 0x75 }, { CCI_REG8(0x5b3c), 0x75 },
++      { CCI_REG8(0x5b3d), 0x75 }, { CCI_REG8(0x5b3e), 0x75 },
++      { CCI_REG8(0x5b3f), 0x75 }, { CCI_REG8(0x5b40), 0x75 },
++      { CCI_REG8(0x5b41), 0x75 }, { CCI_REG8(0x5b42), 0x75 },
++      { CCI_REG8(0x5b43), 0x75 }, { CCI_REG8(0x5b44), 0x75 },
++      { CCI_REG8(0x5b45), 0x75 }, { CCI_REG8(0x5b46), 0x75 },
++      { CCI_REG8(0x5b47), 0x75 }, { CCI_REG8(0x5b48), 0x75 },
++      { CCI_REG8(0x5b49), 0x75 }, { CCI_REG8(0x5b4a), 0x75 },
++      { CCI_REG8(0x5b4b), 0x75 }, { CCI_REG8(0x5b4c), 0x75 },
++      { CCI_REG8(0x5b4d), 0x75 }, { CCI_REG8(0x5b4e), 0x75 },
++      { CCI_REG8(0x5b4f), 0x75 }, { CCI_REG8(0x5b50), 0x75 },
++      { CCI_REG8(0x5b51), 0x75 }, { CCI_REG8(0x5b52), 0x75 },
++      { CCI_REG8(0x5b53), 0x75 }, { CCI_REG8(0x5b54), 0x75 },
++      { CCI_REG8(0x5b55), 0x75 }, { CCI_REG8(0x5b56), 0x75 },
++      { CCI_REG8(0x5b57), 0x75 }, { CCI_REG8(0x5b58), 0x75 },
++      { CCI_REG8(0x5b59), 0x75 }, { CCI_REG8(0x5b5a), 0x75 },
++      { CCI_REG8(0x5b5b), 0x75 }, { CCI_REG8(0x5b5c), 0x75 },
++      { CCI_REG8(0x5b5d), 0x75 }, { CCI_REG8(0x5b5e), 0x75 },
++      { CCI_REG8(0x5b5f), 0x75 }, { CCI_REG8(0x5b80), 0x75 },
++      { CCI_REG8(0x5b81), 0x75 }, { CCI_REG8(0x5b82), 0x75 },
++      { CCI_REG8(0x5b83), 0x75 }, { CCI_REG8(0x5b84), 0x75 },
++      { CCI_REG8(0x5b85), 0x75 }, { CCI_REG8(0x5b86), 0x75 },
++      { CCI_REG8(0x5b87), 0x75 }, { CCI_REG8(0x5b88), 0x75 },
++      { CCI_REG8(0x5b89), 0x75 }, { CCI_REG8(0x5b8a), 0x75 },
++      { CCI_REG8(0x5b8b), 0x75 }, { CCI_REG8(0x5b8c), 0x75 },
++      { CCI_REG8(0x5b8d), 0x75 }, { CCI_REG8(0x5b8e), 0x75 },
++      { CCI_REG8(0x5b8f), 0x75 }, { CCI_REG8(0x5b90), 0x75 },
++      { CCI_REG8(0x5b91), 0x75 }, { CCI_REG8(0x5b92), 0x75 },
++      { CCI_REG8(0x5b93), 0x75 }, { CCI_REG8(0x5b94), 0x75 },
++      { CCI_REG8(0x5b95), 0x75 }, { CCI_REG8(0x5b96), 0x75 },
++      { CCI_REG8(0x5b97), 0x75 }, { CCI_REG8(0x5b98), 0x75 },
++      { CCI_REG8(0x5b99), 0x75 }, { CCI_REG8(0x5b9a), 0x75 },
++      { CCI_REG8(0x5b9b), 0x75 }, { CCI_REG8(0x5b9c), 0x75 },
++      { CCI_REG8(0x5b9d), 0x75 }, { CCI_REG8(0x5b9e), 0x75 },
++      { CCI_REG8(0x5b9f), 0x75 }, { CCI_REG8(0x5ba0), 0x75 },
++      { CCI_REG8(0x5ba1), 0x75 }, { CCI_REG8(0x5ba2), 0x75 },
++      { CCI_REG8(0x5ba3), 0x75 }, { CCI_REG8(0x5ba4), 0x75 },
++      { CCI_REG8(0x5ba5), 0x75 }, { CCI_REG8(0x5ba6), 0x75 },
++      { CCI_REG8(0x5ba7), 0x75 }, { CCI_REG8(0x5ba8), 0x75 },
++      { CCI_REG8(0x5ba9), 0x75 }, { CCI_REG8(0x5baa), 0x75 },
++      { CCI_REG8(0x5bab), 0x75 }, { CCI_REG8(0x5bac), 0x75 },
++      { CCI_REG8(0x5bad), 0x75 }, { CCI_REG8(0x5bae), 0x75 },
++      { CCI_REG8(0x5baf), 0x75 }, { CCI_REG8(0x5bb0), 0x75 },
++      { CCI_REG8(0x5bb1), 0x75 }, { CCI_REG8(0x5bb2), 0x75 },
++      { CCI_REG8(0x5bb3), 0x75 }, { CCI_REG8(0x5bb4), 0x75 },
++      { CCI_REG8(0x5bb5), 0x75 }, { CCI_REG8(0x5bb6), 0x75 },
++      { CCI_REG8(0x5bb7), 0x75 }, { CCI_REG8(0x5bb8), 0x75 },
++      { CCI_REG8(0x5bb9), 0x75 }, { CCI_REG8(0x5bba), 0x75 },
++      { CCI_REG8(0x5bbb), 0x75 }, { CCI_REG8(0x5bbc), 0x75 },
++      { CCI_REG8(0x5bbd), 0x75 }, { CCI_REG8(0x5bbe), 0x75 },
++      { CCI_REG8(0x5bbf), 0x75 }, { CCI_REG8(0x5bc0), 0x75 },
++      { CCI_REG8(0x5bc1), 0x75 }, { CCI_REG8(0x5bc2), 0x75 },
++      { CCI_REG8(0x5bc3), 0x75 }, { CCI_REG8(0x5bc4), 0x75 },
++      { CCI_REG8(0x5bc5), 0x75 }, { CCI_REG8(0x5bc6), 0x75 },
++      { CCI_REG8(0x5bc7), 0x75 }, { CCI_REG8(0x5bc8), 0x75 },
++      { CCI_REG8(0x5bc9), 0x75 }, { CCI_REG8(0x5bca), 0x75 },
++      { CCI_REG8(0x5bcb), 0x75 }, { CCI_REG8(0x5bcc), 0x75 },
++      { CCI_REG8(0x5bcd), 0x75 }, { CCI_REG8(0x5bce), 0x75 },
++      { CCI_REG8(0x5bcf), 0x75 }, { CCI_REG8(0x5bd0), 0x75 },
++      { CCI_REG8(0x5bd1), 0x75 }, { CCI_REG8(0x5bd2), 0x75 },
++      { CCI_REG8(0x5bd3), 0x75 }, { CCI_REG8(0x5bd4), 0x75 },
++      { CCI_REG8(0x5bd5), 0x75 }, { CCI_REG8(0x5bd6), 0x75 },
++      { CCI_REG8(0x5bd7), 0x75 }, { CCI_REG8(0x5bd8), 0x75 },
++      { CCI_REG8(0x5bd9), 0x75 }, { CCI_REG8(0x5bda), 0x75 },
++      { CCI_REG8(0x5bdb), 0x75 }, { CCI_REG8(0x5bdc), 0x75 },
++      { CCI_REG8(0x5bdd), 0x75 }, { CCI_REG8(0x5bde), 0x75 },
++      { CCI_REG8(0x5bdf), 0x75 }, { CCI_REG8(0x5be0), 0x75 },
++      { CCI_REG8(0x5be1), 0x75 }, { CCI_REG8(0x5be2), 0x75 },
++      { CCI_REG8(0x5be3), 0x75 }, { CCI_REG8(0x5be4), 0x75 },
++      { CCI_REG8(0x5be5), 0x75 }, { CCI_REG8(0x5be6), 0x75 },
++      { CCI_REG8(0x5be7), 0x75 }, { CCI_REG8(0x5be8), 0x75 },
++      { CCI_REG8(0x5be9), 0x75 }, { CCI_REG8(0x5bea), 0x75 },
++      { CCI_REG8(0x5beb), 0x75 }, { CCI_REG8(0x5bec), 0x75 },
++      { CCI_REG8(0x5bed), 0x75 }, { CCI_REG8(0x5bee), 0x75 },
++      { CCI_REG8(0x5bef), 0x75 }, { CCI_REG8(0x5bf0), 0x75 },
++      { CCI_REG8(0x5bf1), 0x75 }, { CCI_REG8(0x5bf2), 0x75 },
++      { CCI_REG8(0x5bf3), 0x75 }, { CCI_REG8(0x5bf4), 0x75 },
++      { CCI_REG8(0x5bf5), 0x75 }, { CCI_REG8(0x5bf6), 0x75 },
++      { CCI_REG8(0x5bf7), 0x75 }, { CCI_REG8(0x5bf8), 0x75 },
++      { CCI_REG8(0x5bf9), 0x75 }, { CCI_REG8(0x5bfa), 0x75 },
++      { CCI_REG8(0x5bfb), 0x75 }, { CCI_REG8(0x5bfc), 0x75 },
++      { CCI_REG8(0x5bfd), 0x75 }, { CCI_REG8(0x5bfe), 0x75 },
++      { CCI_REG8(0x5bff), 0x75 }, { CCI_REG8(0x5c00), 0x75 },
++      { CCI_REG8(0x5c01), 0x75 }, { CCI_REG8(0x5c02), 0x75 },
++      { CCI_REG8(0x5c03), 0x75 }, { CCI_REG8(0x5c04), 0x75 },
++      { CCI_REG8(0x5c05), 0x75 }, { CCI_REG8(0x5c06), 0x75 },
++      { CCI_REG8(0x5c07), 0x75 }, { CCI_REG8(0x5c08), 0x75 },
++      { CCI_REG8(0x5c09), 0x75 }, { CCI_REG8(0x5c0a), 0x75 },
++      { CCI_REG8(0x5c0b), 0x75 }, { CCI_REG8(0x5c0c), 0x75 },
++      { CCI_REG8(0x5c0d), 0x75 }, { CCI_REG8(0x5c0e), 0x75 },
++      { CCI_REG8(0x5c0f), 0x75 }, { CCI_REG8(0x5c10), 0x75 },
++      { CCI_REG8(0x5c11), 0x75 }, { CCI_REG8(0x5c12), 0x75 },
++      { CCI_REG8(0x5c13), 0x75 }, { CCI_REG8(0x5c14), 0x75 },
++      { CCI_REG8(0x5c15), 0x75 }, { CCI_REG8(0x5c16), 0x75 },
++      { CCI_REG8(0x5c17), 0x75 }, { CCI_REG8(0x5c18), 0x75 },
++      { CCI_REG8(0x5c19), 0x75 }, { CCI_REG8(0x5c1a), 0x75 },
++      { CCI_REG8(0x5c1b), 0x75 }, { CCI_REG8(0x5c1c), 0x75 },
++      { CCI_REG8(0x5c1d), 0x75 }, { CCI_REG8(0x5c1e), 0x75 },
++      { CCI_REG8(0x5c1f), 0x75 }, { CCI_REG8(0x5c20), 0x75 },
++      { CCI_REG8(0x5c21), 0x75 }, { CCI_REG8(0x5c22), 0x75 },
++      { CCI_REG8(0x5c23), 0x75 }, { CCI_REG8(0x5c24), 0x75 },
++      { CCI_REG8(0x5c25), 0x75 }, { CCI_REG8(0x5c26), 0x75 },
++      { CCI_REG8(0x5c27), 0x75 }, { CCI_REG8(0x5c28), 0x75 },
++      { CCI_REG8(0x5c29), 0x75 }, { CCI_REG8(0x5c2a), 0x75 },
++      { CCI_REG8(0x5c2b), 0x75 }, { CCI_REG8(0x5c2c), 0x75 },
++      { CCI_REG8(0x5c2d), 0x75 }, { CCI_REG8(0x5c2e), 0x75 },
++      { CCI_REG8(0x5c2f), 0x75 }, { CCI_REG8(0x5c30), 0x75 },
++      { CCI_REG8(0x5c31), 0x75 }, { CCI_REG8(0x5c32), 0x75 },
++      { CCI_REG8(0x5c33), 0x75 }, { CCI_REG8(0x5c34), 0x75 },
++      { CCI_REG8(0x5c35), 0x75 }, { CCI_REG8(0x5c36), 0x75 },
++      { CCI_REG8(0x5c37), 0x75 }, { CCI_REG8(0x5c38), 0x75 },
++      { CCI_REG8(0x5c39), 0x75 }, { CCI_REG8(0x5c3a), 0x75 },
++      { CCI_REG8(0x5c3b), 0x75 }, { CCI_REG8(0x5c3c), 0x75 },
++      { CCI_REG8(0x5c3d), 0x75 }, { CCI_REG8(0x5c3e), 0x75 },
++      { CCI_REG8(0x5c3f), 0x75 }, { CCI_REG8(0x5c40), 0x75 },
++      { CCI_REG8(0x5c41), 0x75 }, { CCI_REG8(0x5c42), 0x75 },
++      { CCI_REG8(0x5c43), 0x75 }, { CCI_REG8(0x5c44), 0x75 },
++      { CCI_REG8(0x5c45), 0x75 }, { CCI_REG8(0x5c46), 0x75 },
++      { CCI_REG8(0x5c47), 0x75 }, { CCI_REG8(0x5c48), 0x75 },
++      { CCI_REG8(0x5c49), 0x75 }, { CCI_REG8(0x5c4a), 0x75 },
++      { CCI_REG8(0x5c4b), 0x75 }, { CCI_REG8(0x5c4c), 0x75 },
++      { CCI_REG8(0x5c4d), 0x75 }, { CCI_REG8(0x5c4e), 0x75 },
++      { CCI_REG8(0x5c4f), 0x75 }, { CCI_REG8(0x5c50), 0x75 },
++      { CCI_REG8(0x5c51), 0x75 }, { CCI_REG8(0x5c52), 0x75 },
++      { CCI_REG8(0x5c53), 0x75 }, { CCI_REG8(0x5c54), 0x75 },
++      { CCI_REG8(0x5c55), 0x75 }, { CCI_REG8(0x5c56), 0x75 },
++      { CCI_REG8(0x5c57), 0x75 }, { CCI_REG8(0x5c58), 0x75 },
++      { CCI_REG8(0x5c59), 0x75 }, { CCI_REG8(0x5c5a), 0x75 },
++      { CCI_REG8(0x5c5b), 0x75 }, { CCI_REG8(0x5c5c), 0x75 },
++      { CCI_REG8(0x5c5d), 0x75 }, { CCI_REG8(0x5c5e), 0x75 },
++      { CCI_REG8(0x5c5f), 0x75 }, { CCI_REG8(0x5c60), 0x75 },
++      { CCI_REG8(0x5c61), 0x75 }, { CCI_REG8(0x5c62), 0x75 },
++      { CCI_REG8(0x5c63), 0x75 }, { CCI_REG8(0x5c64), 0x75 },
++      { CCI_REG8(0x5c65), 0x75 }, { CCI_REG8(0x5c66), 0x75 },
++      { CCI_REG8(0x5c67), 0x75 }, { CCI_REG8(0x5c68), 0x75 },
++      { CCI_REG8(0x5c69), 0x75 }, { CCI_REG8(0x5c6a), 0x75 },
++      { CCI_REG8(0x5c6b), 0x75 }, { CCI_REG8(0x5c6c), 0x75 },
++      { CCI_REG8(0x5c6d), 0x75 }, { CCI_REG8(0x5c6e), 0x75 },
++      { CCI_REG8(0x5c6f), 0x75 }, { CCI_REG8(0x5c70), 0x75 },
++      { CCI_REG8(0x5c71), 0x75 }, { CCI_REG8(0x5c72), 0x75 },
++      { CCI_REG8(0x5c73), 0x75 }, { CCI_REG8(0x5c74), 0x75 },
++      { CCI_REG8(0x5c75), 0x75 }, { CCI_REG8(0x5c76), 0x75 },
++      { CCI_REG8(0x5c77), 0x75 }, { CCI_REG8(0x5c78), 0x75 },
++      { CCI_REG8(0x5c79), 0x75 }, { CCI_REG8(0x5c7a), 0x75 },
++      { CCI_REG8(0x5c7b), 0x75 }, { CCI_REG8(0x5c7c), 0x75 },
++      { CCI_REG8(0x5c7d), 0x75 }, { CCI_REG8(0x5c7e), 0x75 },
++      { CCI_REG8(0x5c7f), 0x75 }, { CCI_REG8(0x5c80), 0x75 },
++      { CCI_REG8(0x5c81), 0x75 }, { CCI_REG8(0x5c82), 0x75 },
++      { CCI_REG8(0x5c83), 0x75 }, { CCI_REG8(0x5c84), 0x75 },
++      { CCI_REG8(0x5c85), 0x75 }, { CCI_REG8(0x5c86), 0x75 },
++      { CCI_REG8(0x5c87), 0x75 }, { CCI_REG8(0x5c88), 0x75 },
++      { CCI_REG8(0x5c89), 0x75 }, { CCI_REG8(0x5c8a), 0x75 },
++      { CCI_REG8(0x5c8b), 0x75 }, { CCI_REG8(0x5c8c), 0x75 },
++      { CCI_REG8(0x5c8d), 0x75 }, { CCI_REG8(0x5c8e), 0x75 },
++      { CCI_REG8(0x5c8f), 0x75 }, { CCI_REG8(0x5c90), 0x75 },
++      { CCI_REG8(0x5c91), 0x75 }, { CCI_REG8(0x5c92), 0x75 },
++      { CCI_REG8(0x5c93), 0x75 }, { CCI_REG8(0x5c94), 0x75 },
++      { CCI_REG8(0x5c95), 0x75 }, { CCI_REG8(0x5c96), 0x75 },
++      { CCI_REG8(0x5c97), 0x75 }, { CCI_REG8(0x5c98), 0x75 },
++      { CCI_REG8(0x5c99), 0x75 }, { CCI_REG8(0x5c9a), 0x75 },
++      { CCI_REG8(0x5c9b), 0x75 }, { CCI_REG8(0x5c9c), 0x75 },
++      { CCI_REG8(0x5c9d), 0x75 }, { CCI_REG8(0x5c9e), 0x75 },
++      { CCI_REG8(0x5c9f), 0x75 }, { CCI_REG8(0x5ca0), 0x75 },
++      { CCI_REG8(0x5ca1), 0x75 }, { CCI_REG8(0x5ca2), 0x75 },
++      { CCI_REG8(0x5ca3), 0x75 }, { CCI_REG8(0x5ca4), 0x75 },
++      { CCI_REG8(0x5ca5), 0x75 }, { CCI_REG8(0x5ca6), 0x75 },
++      { CCI_REG8(0x5ca7), 0x75 }, { CCI_REG8(0x5ca8), 0x75 },
++      { CCI_REG8(0x5ca9), 0x75 }, { CCI_REG8(0x5caa), 0x75 },
++      { CCI_REG8(0x5cab), 0x75 }, { CCI_REG8(0x5cac), 0x75 },
++      { CCI_REG8(0x5cad), 0x75 }, { CCI_REG8(0x5cae), 0x75 },
++      { CCI_REG8(0x5caf), 0x75 }, { CCI_REG8(0x5cb0), 0x75 },
++      { CCI_REG8(0x5cb1), 0x75 }, { CCI_REG8(0x5cb2), 0x75 },
++      { CCI_REG8(0x5cb3), 0x75 }, { CCI_REG8(0x5cb4), 0x75 },
++      { CCI_REG8(0x5cb5), 0x75 }, { CCI_REG8(0x5cb6), 0x75 },
++      { CCI_REG8(0x5cb7), 0x75 }, { CCI_REG8(0x5cb8), 0x75 },
++      { CCI_REG8(0x5cb9), 0x75 }, { CCI_REG8(0x5cba), 0x75 },
++      { CCI_REG8(0x5cbb), 0x75 }, { CCI_REG8(0x5cbc), 0x75 },
++      { CCI_REG8(0x5cbd), 0x75 }, { CCI_REG8(0x5cbe), 0x75 },
++      { CCI_REG8(0x5cbf), 0x75 }, { CCI_REG8(0x5cc0), 0x75 },
++      { CCI_REG8(0x5cc1), 0x75 }, { CCI_REG8(0x5cc2), 0x75 },
++      { CCI_REG8(0x5cc3), 0x75 }, { CCI_REG8(0x5cc4), 0x75 },
++      { CCI_REG8(0x5cc5), 0x75 }, { CCI_REG8(0x5cc6), 0x75 },
++      { CCI_REG8(0x5cc7), 0x75 }, { CCI_REG8(0x5cc8), 0x75 },
++      { CCI_REG8(0x5cc9), 0x75 }, { CCI_REG8(0x5cca), 0x75 },
++      { CCI_REG8(0x5ccb), 0x75 }, { CCI_REG8(0x5ccc), 0x75 },
++      { CCI_REG8(0x5ccd), 0x75 }, { CCI_REG8(0x5cce), 0x75 },
++      { CCI_REG8(0x5ccf), 0x75 }, { CCI_REG8(0x5cd0), 0x75 },
++      { CCI_REG8(0x5cd1), 0x75 }, { CCI_REG8(0x5cd2), 0x75 },
++      { CCI_REG8(0x5cd3), 0x75 }, { CCI_REG8(0x5cd4), 0x75 },
++      { CCI_REG8(0x5cd5), 0x75 }, { CCI_REG8(0x5cd6), 0x75 },
++      { CCI_REG8(0x5cd7), 0x75 }, { CCI_REG8(0x5cd8), 0x75 },
++      { CCI_REG8(0x5cd9), 0x75 }, { CCI_REG8(0x5cda), 0x75 },
++      { CCI_REG8(0x5cdb), 0x75 }, { CCI_REG8(0x5cdc), 0x75 },
++      { CCI_REG8(0x5cdd), 0x75 }, { CCI_REG8(0x5cde), 0x75 },
++      { CCI_REG8(0x5cdf), 0x75 }, { CCI_REG8(0x5ce0), 0x75 },
++      { CCI_REG8(0x5ce1), 0x75 }, { CCI_REG8(0x5ce2), 0x75 },
++      { CCI_REG8(0x5ce3), 0x75 }, { CCI_REG8(0x5ce4), 0x75 },
++      { CCI_REG8(0x5ce5), 0x75 }, { CCI_REG8(0x5ce6), 0x75 },
++      { CCI_REG8(0x5ce7), 0x75 }, { CCI_REG8(0x5ce8), 0x75 },
++      { CCI_REG8(0x5ce9), 0x75 }, { CCI_REG8(0x5cea), 0x75 },
++      { CCI_REG8(0x5ceb), 0x75 }, { CCI_REG8(0x5cec), 0x75 },
++      { CCI_REG8(0x5ced), 0x75 }, { CCI_REG8(0x5cee), 0x75 },
++      { CCI_REG8(0x5cef), 0x75 }, { CCI_REG8(0x5cf0), 0x75 },
++      { CCI_REG8(0x5cf1), 0x75 }, { CCI_REG8(0x5cf2), 0x75 },
++      { CCI_REG8(0x5cf3), 0x75 }, { CCI_REG8(0x5cf4), 0x75 },
++      { CCI_REG8(0x5cf5), 0x75 }, { CCI_REG8(0x5cf6), 0x75 },
++      { CCI_REG8(0x5cf7), 0x75 }, { CCI_REG8(0x5cf8), 0x75 },
++      { CCI_REG8(0x5cf9), 0x75 }, { CCI_REG8(0x5cfa), 0x75 },
++      { CCI_REG8(0x5cfb), 0x75 }, { CCI_REG8(0x5cfc), 0x75 },
++      { CCI_REG8(0x5cfd), 0x75 }, { CCI_REG8(0x5cfe), 0x75 },
++      { CCI_REG8(0x5cff), 0x75 }, { CCI_REG8(0x5d00), 0x75 },
++      { CCI_REG8(0x5d01), 0x75 }, { CCI_REG8(0x5d02), 0x75 },
++      { CCI_REG8(0x5d03), 0x75 }, { CCI_REG8(0x5d04), 0x75 },
++      { CCI_REG8(0x5d05), 0x75 }, { CCI_REG8(0x5d06), 0x75 },
++      { CCI_REG8(0x5d07), 0x75 }, { CCI_REG8(0x5d08), 0x75 },
++      { CCI_REG8(0x5d09), 0x75 }, { CCI_REG8(0x5d0a), 0x75 },
++      { CCI_REG8(0x5d0b), 0x75 }, { CCI_REG8(0x5d0c), 0x75 },
++      { CCI_REG8(0x5d0d), 0x75 }, { CCI_REG8(0x5d0e), 0x75 },
++      { CCI_REG8(0x5d0f), 0x75 }, { CCI_REG8(0x5d10), 0x75 },
++      { CCI_REG8(0x5d11), 0x75 }, { CCI_REG8(0x5d12), 0x75 },
++      { CCI_REG8(0x5d13), 0x75 }, { CCI_REG8(0x5d14), 0x75 },
++      { CCI_REG8(0x5d15), 0x75 }, { CCI_REG8(0x5d16), 0x75 },
++      { CCI_REG8(0x5d17), 0x75 }, { CCI_REG8(0x5d18), 0x75 },
++      { CCI_REG8(0x5d19), 0x75 }, { CCI_REG8(0x5d1a), 0x75 },
++      { CCI_REG8(0x5d1b), 0x75 }, { CCI_REG8(0x5d1c), 0x75 },
++      { CCI_REG8(0x5d1d), 0x75 }, { CCI_REG8(0x5d1e), 0x75 },
++      { CCI_REG8(0x5d1f), 0x75 }, { CCI_REG8(0x5d20), 0x75 },
++      { CCI_REG8(0x5d21), 0x75 }, { CCI_REG8(0x5d22), 0x75 },
++      { CCI_REG8(0x5d23), 0x75 }, { CCI_REG8(0x5d24), 0x75 },
++      { CCI_REG8(0x5d25), 0x75 }, { CCI_REG8(0x5d26), 0x75 },
++      { CCI_REG8(0x5d27), 0x75 }, { CCI_REG8(0x5d28), 0x75 },
++      { CCI_REG8(0x5d29), 0x75 }, { CCI_REG8(0x5d2a), 0x75 },
++      { CCI_REG8(0x5d2b), 0x75 }, { CCI_REG8(0x5d2c), 0x75 },
++      { CCI_REG8(0x5d2d), 0x75 }, { CCI_REG8(0x5d2e), 0x75 },
++      { CCI_REG8(0x5d2f), 0x75 }, { CCI_REG8(0x5d30), 0x75 },
++      { CCI_REG8(0x5d31), 0x75 }, { CCI_REG8(0x5d32), 0x75 },
++      { CCI_REG8(0x5d33), 0x75 }, { CCI_REG8(0x5d34), 0x75 },
++      { CCI_REG8(0x5d35), 0x75 }, { CCI_REG8(0x5d36), 0x75 },
++      { CCI_REG8(0x5d37), 0x75 }, { CCI_REG8(0x5d38), 0x75 },
++      { CCI_REG8(0x5d39), 0x75 }, { CCI_REG8(0x5d3a), 0x75 },
++      { CCI_REG8(0x5d3b), 0x75 }, { CCI_REG8(0x5d3c), 0x75 },
++      { CCI_REG8(0x5d3d), 0x75 }, { CCI_REG8(0x5d3e), 0x75 },
++      { CCI_REG8(0x5d3f), 0x75 }, { CCI_REG8(0x5d40), 0x75 },
++      { CCI_REG8(0x5d41), 0x75 }, { CCI_REG8(0x5d42), 0x75 },
++      { CCI_REG8(0x5d43), 0x75 }, { CCI_REG8(0x5d44), 0x75 },
++      { CCI_REG8(0x5d45), 0x75 }, { CCI_REG8(0x5d46), 0x75 },
++      { CCI_REG8(0x5d47), 0x75 }, { CCI_REG8(0x5d48), 0x75 },
++      { CCI_REG8(0x5d49), 0x75 }, { CCI_REG8(0x5d4a), 0x75 },
++      { CCI_REG8(0x5d4b), 0x75 }, { CCI_REG8(0x5d4c), 0x75 },
++      { CCI_REG8(0x5d4d), 0x75 }, { CCI_REG8(0x5d4e), 0x75 },
++      { CCI_REG8(0x5d4f), 0x75 }, { CCI_REG8(0x5d50), 0x75 },
++      { CCI_REG8(0x5d51), 0x75 }, { CCI_REG8(0x5d52), 0x75 },
++      { CCI_REG8(0x5d53), 0x75 }, { CCI_REG8(0x5d54), 0x75 },
++      { CCI_REG8(0x5d55), 0x75 }, { CCI_REG8(0x5d56), 0x75 },
++      { CCI_REG8(0x5d57), 0x75 }, { CCI_REG8(0x5d58), 0x75 },
++      { CCI_REG8(0x5d59), 0x75 }, { CCI_REG8(0x5d5a), 0x75 },
++      { CCI_REG8(0x5d5b), 0x75 }, { CCI_REG8(0x5d5c), 0x75 },
++      { CCI_REG8(0x5d5d), 0x75 }, { CCI_REG8(0x5d5e), 0x75 },
++      { CCI_REG8(0x5d5f), 0x75 }, { CCI_REG8(0x5d60), 0x75 },
++      { CCI_REG8(0x5d61), 0x75 }, { CCI_REG8(0x5d62), 0x75 },
++      { CCI_REG8(0x5d63), 0x75 }, { CCI_REG8(0x5d64), 0x75 },
++      { CCI_REG8(0x5d65), 0x75 }, { CCI_REG8(0x5d66), 0x75 },
++      { CCI_REG8(0x5d67), 0x75 }, { CCI_REG8(0x5d68), 0x75 },
++      { CCI_REG8(0x5d69), 0x75 }, { CCI_REG8(0x5d6a), 0x75 },
++      { CCI_REG8(0x5d6b), 0x75 }, { CCI_REG8(0x5d6c), 0x75 },
++      { CCI_REG8(0x5d6d), 0x75 }, { CCI_REG8(0x5d6e), 0x75 },
++      { CCI_REG8(0x5d6f), 0x75 }, { CCI_REG8(0x5d70), 0x75 },
++      { CCI_REG8(0x5d71), 0x75 }, { CCI_REG8(0x5d72), 0x75 },
++      { CCI_REG8(0x5d73), 0x75 }, { CCI_REG8(0x5d74), 0x75 },
++      { CCI_REG8(0x5d75), 0x75 }, { CCI_REG8(0x5d76), 0x75 },
++      { CCI_REG8(0x5d77), 0x75 }, { CCI_REG8(0x5d78), 0x75 },
++      { CCI_REG8(0x5d79), 0x75 }, { CCI_REG8(0x5d7a), 0x75 },
++      { CCI_REG8(0x5d7b), 0x75 }, { CCI_REG8(0x5d7c), 0x75 },
++      { CCI_REG8(0x5d7d), 0x75 }, { CCI_REG8(0x5d7e), 0x75 },
++      { CCI_REG8(0x5d7f), 0x75 }, { CCI_REG8(0x5d80), 0x75 },
++      { CCI_REG8(0x5d81), 0x75 }, { CCI_REG8(0x5d82), 0x75 },
++      { CCI_REG8(0x5d83), 0x75 }, { CCI_REG8(0x5d84), 0x75 },
++      { CCI_REG8(0x5d85), 0x75 }, { CCI_REG8(0x5d86), 0x75 },
++      { CCI_REG8(0x5d87), 0x75 }, { CCI_REG8(0x5d88), 0x75 },
++      { CCI_REG8(0x5d89), 0x75 }, { CCI_REG8(0x5d8a), 0x75 },
++      { CCI_REG8(0x5d8b), 0x75 }, { CCI_REG8(0x5d8c), 0x75 },
++      { CCI_REG8(0x5d8d), 0x75 }, { CCI_REG8(0x5d8e), 0x75 },
++      { CCI_REG8(0x5d8f), 0x75 }, { CCI_REG8(0x5d90), 0x75 },
++      { CCI_REG8(0x5d91), 0x75 }, { CCI_REG8(0x5d92), 0x75 },
++      { CCI_REG8(0x5d93), 0x75 }, { CCI_REG8(0x5d94), 0x75 },
++      { CCI_REG8(0x5d95), 0x75 }, { CCI_REG8(0x5d96), 0x75 },
++      { CCI_REG8(0x5d97), 0x75 }, { CCI_REG8(0x5d98), 0x75 },
++      { CCI_REG8(0x5d99), 0x75 }, { CCI_REG8(0x5d9a), 0x75 },
++      { CCI_REG8(0x5d9b), 0x75 }, { CCI_REG8(0x5d9c), 0x75 },
++      { CCI_REG8(0x5d9d), 0x75 }, { CCI_REG8(0x5d9e), 0x75 },
++      { CCI_REG8(0x5d9f), 0x75 }, { CCI_REG8(0x5da0), 0x75 },
++      { CCI_REG8(0x5da1), 0x75 }, { CCI_REG8(0x5da2), 0x75 },
++      { CCI_REG8(0x5da3), 0x75 }, { CCI_REG8(0x5da4), 0x75 },
++      { CCI_REG8(0x5da5), 0x75 }, { CCI_REG8(0x5da6), 0x75 },
++      { CCI_REG8(0x5da7), 0x75 }, { CCI_REG8(0x5da8), 0x75 },
++      { CCI_REG8(0x5da9), 0x75 }, { CCI_REG8(0x5daa), 0x75 },
++      { CCI_REG8(0x5dab), 0x75 }, { CCI_REG8(0x5dac), 0x75 },
++      { CCI_REG8(0x5dad), 0x75 }, { CCI_REG8(0x5dae), 0x75 },
++      { CCI_REG8(0x5daf), 0x75 }, { CCI_REG8(0x5db0), 0x75 },
++      { CCI_REG8(0x5db1), 0x75 }, { CCI_REG8(0x5db2), 0x75 },
++      { CCI_REG8(0x5db3), 0x75 }, { CCI_REG8(0x5db4), 0x75 },
++      { CCI_REG8(0x5db5), 0x75 }, { CCI_REG8(0x5db6), 0x75 },
++      { CCI_REG8(0x5db7), 0x75 }, { CCI_REG8(0x5db8), 0x75 },
++      { CCI_REG8(0x5db9), 0x75 }, { CCI_REG8(0x5dba), 0x75 },
++      { CCI_REG8(0x5dbb), 0x75 }, { CCI_REG8(0x5dbc), 0x75 },
++      { CCI_REG8(0x5dbd), 0x75 }, { CCI_REG8(0x5dbe), 0x75 },
++      { CCI_REG8(0x5dbf), 0x75 }, { CCI_REG8(0x5dc0), 0x75 },
++      { CCI_REG8(0x5dc1), 0x75 }, { CCI_REG8(0x5dc2), 0x75 },
++      { CCI_REG8(0x5dc3), 0x75 }, { CCI_REG8(0x5dc4), 0x75 },
++      { CCI_REG8(0x5dc5), 0x75 }, { CCI_REG8(0x5dc6), 0x75 },
++      { CCI_REG8(0x5dc7), 0x75 }, { CCI_REG8(0x5dc8), 0x75 },
++      { CCI_REG8(0x5dc9), 0x75 }, { CCI_REG8(0x5dca), 0x75 },
++      { CCI_REG8(0x5dcb), 0x75 }, { CCI_REG8(0x5dcc), 0x75 },
++      { CCI_REG8(0x5dcd), 0x75 }, { CCI_REG8(0x5dce), 0x75 },
++      { CCI_REG8(0x5dcf), 0x75 }, { CCI_REG8(0x5dd0), 0x75 },
++      { CCI_REG8(0x5dd1), 0x75 }, { CCI_REG8(0x5dd2), 0x75 },
++      { CCI_REG8(0x5dd3), 0x75 }, { CCI_REG8(0x5dd4), 0x75 },
++      { CCI_REG8(0x5dd5), 0x75 }, { CCI_REG8(0x5dd6), 0x75 },
++      { CCI_REG8(0x5dd7), 0x75 }, { CCI_REG8(0x5dd8), 0x75 },
++      { CCI_REG8(0x5dd9), 0x75 }, { CCI_REG8(0x5dda), 0x75 },
++      { CCI_REG8(0x5ddb), 0x75 }, { CCI_REG8(0x5ddc), 0x75 },
++      { CCI_REG8(0x5ddd), 0x75 }, { CCI_REG8(0x5dde), 0x75 },
++      { CCI_REG8(0x5ddf), 0x75 }, { CCI_REG8(0x5de0), 0x75 },
++      { CCI_REG8(0x5de1), 0x75 }, { CCI_REG8(0x5de2), 0x75 },
++      { CCI_REG8(0x5de3), 0x75 }, { CCI_REG8(0x5de4), 0x75 },
++      { CCI_REG8(0x5de5), 0x75 }, { CCI_REG8(0x5de6), 0x75 },
++      { CCI_REG8(0x5de7), 0x75 }, { CCI_REG8(0x5de8), 0x75 },
++      { CCI_REG8(0x5de9), 0x75 }, { CCI_REG8(0x5dea), 0x75 },
++      { CCI_REG8(0x5deb), 0x75 }, { CCI_REG8(0x5dec), 0x75 },
++      { CCI_REG8(0x5ded), 0x75 }, { CCI_REG8(0x5dee), 0x75 },
++      { CCI_REG8(0x5def), 0x75 }, { CCI_REG8(0x5df0), 0x75 },
++      { CCI_REG8(0x5df1), 0x75 }, { CCI_REG8(0x5df2), 0x75 },
++      { CCI_REG8(0x5df3), 0x75 }, { CCI_REG8(0x5df4), 0x75 },
++      { CCI_REG8(0x5df5), 0x75 }, { CCI_REG8(0x5df6), 0x75 },
++      { CCI_REG8(0x5df7), 0x75 }, { CCI_REG8(0x5df8), 0x75 },
++      { CCI_REG8(0x5df9), 0x75 }, { CCI_REG8(0x5dfa), 0x75 },
++      { CCI_REG8(0x5dfb), 0x75 }, { CCI_REG8(0x5dfc), 0x75 },
++      { CCI_REG8(0x5dfd), 0x75 }, { CCI_REG8(0x5dfe), 0x75 },
++      { CCI_REG8(0x5dff), 0x75 }, { CCI_REG8(0x5e00), 0x75 },
++      { CCI_REG8(0x5e01), 0x75 }, { CCI_REG8(0x5e02), 0x75 },
++      { CCI_REG8(0x5e03), 0x75 }, { CCI_REG8(0x5e04), 0x75 },
++      { CCI_REG8(0x5e05), 0x75 }, { CCI_REG8(0x5e06), 0x75 },
++      { CCI_REG8(0x5e07), 0x75 }, { CCI_REG8(0x5e08), 0x75 },
++      { CCI_REG8(0x5e09), 0x75 }, { CCI_REG8(0x5e0a), 0x75 },
++      { CCI_REG8(0x5e0b), 0x75 }, { CCI_REG8(0x5e0c), 0x75 },
++      { CCI_REG8(0x5e0d), 0x75 }, { CCI_REG8(0x5e0e), 0x75 },
++      { CCI_REG8(0x5e0f), 0x75 }, { CCI_REG8(0x5e10), 0x75 },
++      { CCI_REG8(0x5e11), 0x75 }, { CCI_REG8(0x5e12), 0x75 },
++      { CCI_REG8(0x5e13), 0x75 }, { CCI_REG8(0x5e14), 0x75 },
++      { CCI_REG8(0x5e15), 0x75 }, { CCI_REG8(0x5e16), 0x75 },
++      { CCI_REG8(0x5e17), 0x75 }, { CCI_REG8(0x5e18), 0x75 },
++      { CCI_REG8(0x5e19), 0x75 }, { CCI_REG8(0x5e1a), 0x75 },
++      { CCI_REG8(0x5e1b), 0x75 }, { CCI_REG8(0x5e1c), 0x75 },
++      { CCI_REG8(0x5e1d), 0x75 }, { CCI_REG8(0x5e1e), 0x75 },
++      { CCI_REG8(0x5e1f), 0x75 }, { CCI_REG8(0x5e20), 0x75 },
++      { CCI_REG8(0x5e21), 0x75 }, { CCI_REG8(0x5e22), 0x75 },
++      { CCI_REG8(0x5e23), 0x75 }, { CCI_REG8(0x5e24), 0x75 },
++      { CCI_REG8(0x5e25), 0x75 }, { CCI_REG8(0x5e26), 0x75 },
++      { CCI_REG8(0x5e27), 0x75 }, { CCI_REG8(0x5e28), 0x75 },
++      { CCI_REG8(0x5e29), 0x75 }, { CCI_REG8(0x5e2a), 0x75 },
++      { CCI_REG8(0x5e2b), 0x75 }, { CCI_REG8(0x5e2c), 0x75 },
++      { CCI_REG8(0x5e2d), 0x75 }, { CCI_REG8(0x5e2e), 0x75 },
++      { CCI_REG8(0x5e2f), 0x75 }, { CCI_REG8(0x5e30), 0x75 },
++      { CCI_REG8(0x5e31), 0x75 }, { CCI_REG8(0x5e32), 0x75 },
++      { CCI_REG8(0x5e33), 0x75 }, { CCI_REG8(0x5e34), 0x75 },
++      { CCI_REG8(0x5e35), 0x75 }, { CCI_REG8(0x5e36), 0x75 },
++      { CCI_REG8(0x5e37), 0x75 }, { CCI_REG8(0x5e38), 0x75 },
++      { CCI_REG8(0x5e39), 0x75 }, { CCI_REG8(0x5e3a), 0x75 },
++      { CCI_REG8(0x5e3b), 0x75 }, { CCI_REG8(0x5e3c), 0x75 },
++      { CCI_REG8(0x5e3d), 0x75 }, { CCI_REG8(0x5e3e), 0x75 },
++      { CCI_REG8(0x5e3f), 0x75 }, { CCI_REG8(0x5e40), 0x75 },
++      { CCI_REG8(0x5e41), 0x75 }, { CCI_REG8(0x5e42), 0x75 },
++      { CCI_REG8(0x5e43), 0x75 }, { CCI_REG8(0x5e44), 0x75 },
++      { CCI_REG8(0x5e45), 0x75 }, { CCI_REG8(0x5e46), 0x75 },
++      { CCI_REG8(0x5e47), 0x75 }, { CCI_REG8(0x5e48), 0x75 },
++      { CCI_REG8(0x5e49), 0x75 }, { CCI_REG8(0x5e4a), 0x75 },
++      { CCI_REG8(0x5e4b), 0x75 }, { CCI_REG8(0x5e4c), 0x75 },
++      { CCI_REG8(0x5e4d), 0x75 }, { CCI_REG8(0x5e4e), 0x75 },
++      { CCI_REG8(0x5e4f), 0x75 }, { CCI_REG8(0x5e50), 0x75 },
++      { CCI_REG8(0x5e51), 0x75 }, { CCI_REG8(0x5e52), 0x75 },
++      { CCI_REG8(0x5e53), 0x75 }, { CCI_REG8(0x5e54), 0x75 },
++      { CCI_REG8(0x5e55), 0x75 }, { CCI_REG8(0x5e56), 0x75 },
++      { CCI_REG8(0x5e57), 0x75 }, { CCI_REG8(0x5e58), 0x75 },
++      { CCI_REG8(0x5e59), 0x75 }, { CCI_REG8(0x5e5a), 0x75 },
++      { CCI_REG8(0x5e5b), 0x75 }, { CCI_REG8(0x5e5c), 0x75 },
++      { CCI_REG8(0x5e5d), 0x75 }, { CCI_REG8(0x5e5e), 0x75 },
++      { CCI_REG8(0x5e5f), 0x75 }, { CCI_REG8(0x5e60), 0x75 },
++      { CCI_REG8(0x5e61), 0x75 }, { CCI_REG8(0x5e62), 0x75 },
++      { CCI_REG8(0x5e63), 0x75 }, { CCI_REG8(0x5e64), 0x75 },
++      { CCI_REG8(0x5e65), 0x75 }, { CCI_REG8(0x5e66), 0x75 },
++      { CCI_REG8(0x5e67), 0x75 }, { CCI_REG8(0x5e68), 0x75 },
++      { CCI_REG8(0x5e69), 0x75 }, { CCI_REG8(0x5e6a), 0x75 },
++      { CCI_REG8(0x5e6b), 0x75 }, { CCI_REG8(0x5e6c), 0x75 },
++      { CCI_REG8(0x5e6d), 0x75 }, { CCI_REG8(0x5e6e), 0x75 },
++      { CCI_REG8(0x5e6f), 0x75 }, { CCI_REG8(0x5e70), 0x75 },
++      { CCI_REG8(0x5e71), 0x75 }, { CCI_REG8(0x5e72), 0x75 },
++      { CCI_REG8(0x5e73), 0x75 }, { CCI_REG8(0x5e74), 0x75 },
++      { CCI_REG8(0x5e75), 0x75 }, { CCI_REG8(0x5e76), 0x75 },
++      { CCI_REG8(0x5e77), 0x75 }, { CCI_REG8(0x5e78), 0x75 },
++      { CCI_REG8(0x5e79), 0x75 }, { CCI_REG8(0x5e7a), 0x75 },
++      { CCI_REG8(0x5e7b), 0x75 }, { CCI_REG8(0x5e7c), 0x75 },
++      { CCI_REG8(0x5e7d), 0x75 }, { CCI_REG8(0x5e7e), 0x75 },
++      { CCI_REG8(0x5e7f), 0x75 }, { CCI_REG8(0x5e80), 0x75 },
++      { CCI_REG8(0x5e81), 0x75 }, { CCI_REG8(0x5e82), 0x75 },
++      { CCI_REG8(0x5e83), 0x75 }, { CCI_REG8(0x5e84), 0x75 },
++      { CCI_REG8(0x5e85), 0x75 }, { CCI_REG8(0x5e86), 0x75 },
++      { CCI_REG8(0x5e87), 0x75 }, { CCI_REG8(0x5e88), 0x75 },
++      { CCI_REG8(0x5e89), 0x75 }, { CCI_REG8(0x5e8a), 0x75 },
++      { CCI_REG8(0x5e8b), 0x75 }, { CCI_REG8(0x5e8c), 0x75 },
++      { CCI_REG8(0x5e8d), 0x75 }, { CCI_REG8(0x5e8e), 0x75 },
++      { CCI_REG8(0x5e8f), 0x75 }, { CCI_REG8(0x5e90), 0x75 },
++      { CCI_REG8(0x5e91), 0x75 }, { CCI_REG8(0x5e92), 0x75 },
++      { CCI_REG8(0x5e93), 0x75 }, { CCI_REG8(0x5e94), 0x75 },
++      { CCI_REG8(0x5e95), 0x75 }, { CCI_REG8(0x5e96), 0x75 },
++      { CCI_REG8(0x5e97), 0x75 }, { CCI_REG8(0x5e98), 0x75 },
++      { CCI_REG8(0x5e99), 0x75 }, { CCI_REG8(0x5e9a), 0x75 },
++      { CCI_REG8(0x5e9b), 0x75 }, { CCI_REG8(0x5e9c), 0x75 },
++      { CCI_REG8(0x5e9d), 0x75 }, { CCI_REG8(0x5e9e), 0x75 },
++      { CCI_REG8(0x5e9f), 0x75 }, { CCI_REG8(0x5ea0), 0x75 },
++      { CCI_REG8(0x5ea1), 0x75 }, { CCI_REG8(0x5ea2), 0x75 },
++      { CCI_REG8(0x5ea3), 0x75 }, { CCI_REG8(0x5ea4), 0x75 },
++      { CCI_REG8(0x5ea5), 0x75 }, { CCI_REG8(0x5ea6), 0x75 },
++      { CCI_REG8(0x5ea7), 0x75 }, { CCI_REG8(0x5ea8), 0x75 },
++      { CCI_REG8(0x5ea9), 0x75 }, { CCI_REG8(0x5eaa), 0x75 },
++      { CCI_REG8(0x5eab), 0x75 }, { CCI_REG8(0x5eac), 0x75 },
++      { CCI_REG8(0x5ead), 0x75 }, { CCI_REG8(0x5eae), 0x75 },
++      { CCI_REG8(0x5eaf), 0x75 }, { CCI_REG8(0x5eb0), 0x75 },
++      { CCI_REG8(0x5eb1), 0x75 }, { CCI_REG8(0x5eb2), 0x75 },
++      { CCI_REG8(0x5eb3), 0x75 }, { CCI_REG8(0x5eb4), 0x75 },
++      { CCI_REG8(0x5eb5), 0x75 }, { CCI_REG8(0x5eb6), 0x75 },
++      { CCI_REG8(0x5eb7), 0x75 }, { CCI_REG8(0x5eb8), 0x75 },
++      { CCI_REG8(0x5eb9), 0x75 }, { CCI_REG8(0x5eba), 0x75 },
++      { CCI_REG8(0x5ebb), 0x75 }, { CCI_REG8(0x5ebc), 0x75 },
++      { CCI_REG8(0x5ebd), 0x75 }, { CCI_REG8(0x5ebe), 0x75 },
++      { CCI_REG8(0x5ebf), 0x75 }, { CCI_REG8(0x5ec0), 0x75 },
++      { CCI_REG8(0x5ec1), 0x75 }, { CCI_REG8(0x5ec2), 0x75 },
++      { CCI_REG8(0x5ec3), 0x75 }, { CCI_REG8(0x5ec4), 0x75 },
++      { CCI_REG8(0x5ec5), 0x75 }, { CCI_REG8(0x5ec6), 0x75 },
++      { CCI_REG8(0x5ec7), 0x75 }, { CCI_REG8(0x5ec8), 0x75 },
++      { CCI_REG8(0x5ec9), 0x75 }, { CCI_REG8(0x5eca), 0x75 },
++      { CCI_REG8(0x5ecb), 0x75 }, { CCI_REG8(0x5ecc), 0x75 },
++      { CCI_REG8(0x5ecd), 0x75 }, { CCI_REG8(0x5ece), 0x75 },
++      { CCI_REG8(0x5ecf), 0x75 }, { CCI_REG8(0x5ed0), 0x75 },
++      { CCI_REG8(0x5ed1), 0x75 }, { CCI_REG8(0x5ed2), 0x75 },
++      { CCI_REG8(0x5ed3), 0x75 }, { CCI_REG8(0x5ed4), 0x75 },
++      { CCI_REG8(0x5ed5), 0x75 }, { CCI_REG8(0x5ed6), 0x75 },
++      { CCI_REG8(0x5ed7), 0x75 }, { CCI_REG8(0x5ed8), 0x75 },
++      { CCI_REG8(0x5ed9), 0x75 }, { CCI_REG8(0x5eda), 0x75 },
++      { CCI_REG8(0x5edb), 0x75 }, { CCI_REG8(0x5edc), 0x75 },
++      { CCI_REG8(0x5edd), 0x75 }, { CCI_REG8(0x5ede), 0x75 },
++      { CCI_REG8(0x5edf), 0x75 }, { CCI_REG8(0xfff9), 0x08 },
++      { CCI_REG8(0x1570), 0x00 }, { CCI_REG8(0x15d0), 0x00 },
++      { CCI_REG8(0x15a0), 0x02 }, { CCI_REG8(0x15a1), 0x00 },
++      { CCI_REG8(0x15a2), 0x02 }, { CCI_REG8(0x15a3), 0x76 },
++      { CCI_REG8(0x15a4), 0x03 }, { CCI_REG8(0x15a5), 0x08 },
++      { CCI_REG8(0x15a6), 0x00 }, { CCI_REG8(0x15a7), 0x60 },
++      { CCI_REG8(0x15a8), 0x01 }, { CCI_REG8(0x15a9), 0x00 },
++      { CCI_REG8(0x15aa), 0x02 }, { CCI_REG8(0x15ab), 0x00 },
++      { CCI_REG8(0x1600), 0x02 }, { CCI_REG8(0x1601), 0x00 },
++      { CCI_REG8(0x1602), 0x02 }, { CCI_REG8(0x1603), 0x76 },
++      { CCI_REG8(0x1604), 0x03 }, { CCI_REG8(0x1605), 0x08 },
++      { CCI_REG8(0x1606), 0x00 }, { CCI_REG8(0x1607), 0x60 },
++      { CCI_REG8(0x1608), 0x01 }, { CCI_REG8(0x1609), 0x00 },
++      { CCI_REG8(0x160a), 0x02 }, { CCI_REG8(0x160b), 0x00 },
++      { CCI_REG8(0x1633), 0x03 }, { CCI_REG8(0x1634), 0x01 },
++      { CCI_REG8(0x163c), 0x3a }, { CCI_REG8(0x163d), 0x01 },
++      { CCI_REG8(0x1648), 0x32 }, { CCI_REG8(0x1658), 0x01 },
++      { CCI_REG8(0x1659), 0x01 }, { CCI_REG8(0x165f), 0x01 },
++      { CCI_REG8(0x1677), 0x01 }, { CCI_REG8(0x1690), 0x08 },
++      { CCI_REG8(0x1691), 0x00 }, { CCI_REG8(0x1692), 0x20 },
++      { CCI_REG8(0x1693), 0x00 }, { CCI_REG8(0x1694), 0x10 },
++      { CCI_REG8(0x1695), 0x14 }, { CCI_REG8(0x1696), 0x10 },
++      { CCI_REG8(0x1697), 0x0e }, { CCI_REG8(0x1730), 0x01 },
++      { CCI_REG8(0x1732), 0x00 }, { CCI_REG8(0x1733), 0x10 },
++      { CCI_REG8(0x1734), 0x01 }, { CCI_REG8(0x1735), 0x00 },
++      { CCI_REG8(0x1748), 0x01 }, { CCI_REG8(0xfff9), 0x06 },
++      { CCI_REG8(0x5000), 0xff }, { CCI_REG8(0x5001), 0x3d },
++      { CCI_REG8(0x5002), 0xf5 }, { CCI_REG8(0x5004), 0x80 },
++      { CCI_REG8(0x5006), 0x04 }, { CCI_REG8(0x5061), 0x20 },
++      { CCI_REG8(0x5063), 0x20 }, { CCI_REG8(0x5064), 0x24 },
++      { CCI_REG8(0x5065), 0x00 }, { CCI_REG8(0x5066), 0x1b },
++      { CCI_REG8(0x5067), 0x00 }, { CCI_REG8(0x5068), 0x03 },
++      { CCI_REG8(0x5069), 0x10 }, { CCI_REG8(0x506a), 0x20 },
++      { CCI_REG8(0x506b), 0x04 }, { CCI_REG8(0x506c), 0x04 },
++      { CCI_REG8(0x506d), 0x0c }, { CCI_REG8(0x506e), 0x0c },
++      { CCI_REG8(0x506f), 0x04 }, { CCI_REG8(0x5070), 0x0c },
++      { CCI_REG8(0x5071), 0x14 }, { CCI_REG8(0x5072), 0x1c },
++      { CCI_REG8(0x5073), 0x01 }, { CCI_REG8(0x5074), 0x01 },
++      { CCI_REG8(0x5075), 0xbe }, { CCI_REG8(0x5083), 0x00 },
++      { CCI_REG8(0x5114), 0x03 }, { CCI_REG8(0x51b0), 0x00 },
++      { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x02 },
++      { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++      { CCI_REG8(0x51b8), 0x00 }, { CCI_REG8(0x51b9), 0x70 },
++      { CCI_REG8(0x51ba), 0x00 }, { CCI_REG8(0x51bb), 0x10 },
++      { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++      { CCI_REG8(0x51d2), 0xff }, { CCI_REG8(0x51d3), 0x1c },
++      { CCI_REG8(0x5250), 0x34 }, { CCI_REG8(0x5251), 0x00 },
++      { CCI_REG8(0x525b), 0x00 }, { CCI_REG8(0x525d), 0x00 },
++      { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x38 },
++      { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x4b },
++      { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 },
++      { CCI_REG8(0x5290), 0x00 }, { CCI_REG8(0x5291), 0x50 },
++      { CCI_REG8(0x5292), 0x00 }, { CCI_REG8(0x5293), 0x50 },
++      { CCI_REG8(0x5294), 0x00 }, { CCI_REG8(0x5295), 0x50 },
++      { CCI_REG8(0x5296), 0x00 }, { CCI_REG8(0x5297), 0x50 },
++      { CCI_REG8(0x5298), 0x00 }, { CCI_REG8(0x5299), 0x50 },
++      { CCI_REG8(0x529a), 0x01 }, { CCI_REG8(0x529b), 0x00 },
++      { CCI_REG8(0x529c), 0x01 }, { CCI_REG8(0x529d), 0x00 },
++      { CCI_REG8(0x529e), 0x00 }, { CCI_REG8(0x529f), 0x50 },
++      { CCI_REG8(0x52a0), 0x00 }, { CCI_REG8(0x52a1), 0x50 },
++      { CCI_REG8(0x52a2), 0x01 }, { CCI_REG8(0x52a3), 0x00 },
++      { CCI_REG8(0x52a4), 0x01 }, { CCI_REG8(0x52a5), 0x00 },
++      { CCI_REG8(0x52a6), 0x00 }, { CCI_REG8(0x52a7), 0x50 },
++      { CCI_REG8(0x52a8), 0x00 }, { CCI_REG8(0x52a9), 0x50 },
++      { CCI_REG8(0x52aa), 0x00 }, { CCI_REG8(0x52ab), 0x50 },
++      { CCI_REG8(0x52ac), 0x00 }, { CCI_REG8(0x52ad), 0x50 },
++      { CCI_REG8(0x52ae), 0x00 }, { CCI_REG8(0x52af), 0x50 },
++      { CCI_REG8(0x52b0), 0x00 }, { CCI_REG8(0x52b1), 0x50 },
++      { CCI_REG8(0x52b2), 0x00 }, { CCI_REG8(0x52b3), 0x50 },
++      { CCI_REG8(0x52b4), 0x00 }, { CCI_REG8(0x52b5), 0x50 },
++      { CCI_REG8(0x52b6), 0x00 }, { CCI_REG8(0x52b7), 0x50 },
++      { CCI_REG8(0x52b8), 0x00 }, { CCI_REG8(0x52b9), 0x50 },
++      { CCI_REG8(0x52ba), 0x01 }, { CCI_REG8(0x52bb), 0x00 },
++      { CCI_REG8(0x52bc), 0x01 }, { CCI_REG8(0x52bd), 0x00 },
++      { CCI_REG8(0x52be), 0x00 }, { CCI_REG8(0x52bf), 0x50 },
++      { CCI_REG8(0x52c0), 0x00 }, { CCI_REG8(0x52c1), 0x50 },
++      { CCI_REG8(0x52c2), 0x01 }, { CCI_REG8(0x52c3), 0x00 },
++      { CCI_REG8(0x52c4), 0x01 }, { CCI_REG8(0x52c5), 0x00 },
++      { CCI_REG8(0x52c6), 0x00 }, { CCI_REG8(0x52c7), 0x50 },
++      { CCI_REG8(0x52c8), 0x00 }, { CCI_REG8(0x52c9), 0x50 },
++      { CCI_REG8(0x52ca), 0x00 }, { CCI_REG8(0x52cb), 0x50 },
++      { CCI_REG8(0x52cc), 0x00 }, { CCI_REG8(0x52cd), 0x50 },
++      { CCI_REG8(0x52ce), 0x00 }, { CCI_REG8(0x52cf), 0x50 },
++      { CCI_REG8(0x52f0), 0x04 }, { CCI_REG8(0x52f1), 0x03 },
++      { CCI_REG8(0x52f2), 0x02 }, { CCI_REG8(0x52f3), 0x01 },
++      { CCI_REG8(0x52f4), 0x08 }, { CCI_REG8(0x52f5), 0x07 },
++      { CCI_REG8(0x52f6), 0x06 }, { CCI_REG8(0x52f7), 0x05 },
++      { CCI_REG8(0x52f8), 0x0c }, { CCI_REG8(0x52f9), 0x0b },
++      { CCI_REG8(0x52fa), 0x0a }, { CCI_REG8(0x52fb), 0x09 },
++      { CCI_REG8(0x52fc), 0x10 }, { CCI_REG8(0x52fd), 0x0f },
++      { CCI_REG8(0x52fe), 0x0e }, { CCI_REG8(0x52ff), 0x0d },
++      { CCI_REG8(0x5300), 0x14 }, { CCI_REG8(0x5301), 0x13 },
++      { CCI_REG8(0x5302), 0x12 }, { CCI_REG8(0x5303), 0x11 },
++      { CCI_REG8(0x5304), 0x18 }, { CCI_REG8(0x5305), 0x17 },
++      { CCI_REG8(0x5306), 0x16 }, { CCI_REG8(0x5307), 0x15 },
++      { CCI_REG8(0x5308), 0x1c }, { CCI_REG8(0x5309), 0x1b },
++      { CCI_REG8(0x530a), 0x1a }, { CCI_REG8(0x530b), 0x19 },
++      { CCI_REG8(0x530c), 0x20 }, { CCI_REG8(0x530d), 0x1f },
++      { CCI_REG8(0x530e), 0x1e }, { CCI_REG8(0x530f), 0x1d },
++      { CCI_REG8(0x5310), 0x03 }, { CCI_REG8(0x5311), 0xe8 },
++      { CCI_REG8(0x5331), 0x0a }, { CCI_REG8(0x5332), 0x43 },
++      { CCI_REG8(0x5333), 0x45 }, { CCI_REG8(0x5353), 0x09 },
++      { CCI_REG8(0x5354), 0x00 }, { CCI_REG8(0x5414), 0x03 },
++      { CCI_REG8(0x54b0), 0x10 }, { CCI_REG8(0x54b3), 0x0e },
++      { CCI_REG8(0x54b5), 0x02 }, { CCI_REG8(0x54b6), 0x00 },
++      { CCI_REG8(0x54b7), 0x00 }, { CCI_REG8(0x54b8), 0x00 },
++      { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54ba), 0x00 },
++      { CCI_REG8(0x54bb), 0x10 }, { CCI_REG8(0x54bc), 0x00 },
++      { CCI_REG8(0x54bd), 0x00 }, { CCI_REG8(0x54d2), 0xff },
++      { CCI_REG8(0x54d3), 0x1c }, { CCI_REG8(0x5510), 0x03 },
++      { CCI_REG8(0x5511), 0xe8 }, { CCI_REG8(0x5550), 0x6c },
++      { CCI_REG8(0x5551), 0x00 }, { CCI_REG8(0x557a), 0x00 },
++      { CCI_REG8(0x557b), 0x38 }, { CCI_REG8(0x557c), 0x00 },
++      { CCI_REG8(0x557d), 0x4b }, { CCI_REG8(0x5590), 0x00 },
++      { CCI_REG8(0x5591), 0x50 }, { CCI_REG8(0x5592), 0x00 },
++      { CCI_REG8(0x5593), 0x50 }, { CCI_REG8(0x5594), 0x00 },
++      { CCI_REG8(0x5595), 0x50 }, { CCI_REG8(0x5596), 0x00 },
++      { CCI_REG8(0x5597), 0x50 }, { CCI_REG8(0x5598), 0x00 },
++      { CCI_REG8(0x5599), 0x50 }, { CCI_REG8(0x559a), 0x01 },
++      { CCI_REG8(0x559b), 0x00 }, { CCI_REG8(0x559c), 0x01 },
++      { CCI_REG8(0x559d), 0x00 }, { CCI_REG8(0x559e), 0x00 },
++      { CCI_REG8(0x559f), 0x50 }, { CCI_REG8(0x55a0), 0x00 },
++      { CCI_REG8(0x55a1), 0x50 }, { CCI_REG8(0x55a2), 0x01 },
++      { CCI_REG8(0x55a3), 0x00 }, { CCI_REG8(0x55a4), 0x01 },
++      { CCI_REG8(0x55a5), 0x00 }, { CCI_REG8(0x55a6), 0x00 },
++      { CCI_REG8(0x55a7), 0x50 }, { CCI_REG8(0x55a8), 0x00 },
++      { CCI_REG8(0x55a9), 0x50 }, { CCI_REG8(0x55aa), 0x00 },
++      { CCI_REG8(0x55ab), 0x50 }, { CCI_REG8(0x55ac), 0x00 },
++      { CCI_REG8(0x55ad), 0x50 }, { CCI_REG8(0x55ae), 0x00 },
++      { CCI_REG8(0x55af), 0x50 }, { CCI_REG8(0x55b0), 0x00 },
++      { CCI_REG8(0x55b1), 0x50 }, { CCI_REG8(0x55b2), 0x00 },
++      { CCI_REG8(0x55b3), 0x50 }, { CCI_REG8(0x55b4), 0x00 },
++      { CCI_REG8(0x55b5), 0x50 }, { CCI_REG8(0x55b6), 0x00 },
++      { CCI_REG8(0x55b7), 0x50 }, { CCI_REG8(0x55b8), 0x00 },
++      { CCI_REG8(0x55b9), 0x50 }, { CCI_REG8(0x55ba), 0x01 },
++      { CCI_REG8(0x55bb), 0x00 }, { CCI_REG8(0x55bc), 0x01 },
++      { CCI_REG8(0x55bd), 0x00 }, { CCI_REG8(0x55be), 0x00 },
++      { CCI_REG8(0x55bf), 0x50 }, { CCI_REG8(0x55c0), 0x00 },
++      { CCI_REG8(0x55c1), 0x50 }, { CCI_REG8(0x55c2), 0x01 },
++      { CCI_REG8(0x55c3), 0x00 }, { CCI_REG8(0x55c4), 0x01 },
++      { CCI_REG8(0x55c5), 0x00 }, { CCI_REG8(0x55c6), 0x00 },
++      { CCI_REG8(0x55c7), 0x50 }, { CCI_REG8(0x55c8), 0x00 },
++      { CCI_REG8(0x55c9), 0x50 }, { CCI_REG8(0x55ca), 0x00 },
++      { CCI_REG8(0x55cb), 0x50 }, { CCI_REG8(0x55cc), 0x00 },
++      { CCI_REG8(0x55cd), 0x50 }, { CCI_REG8(0x55ce), 0x00 },
++      { CCI_REG8(0x55cf), 0x50 }, { CCI_REG8(0x55f0), 0x04 },
++      { CCI_REG8(0x55f1), 0x03 }, { CCI_REG8(0x55f2), 0x02 },
++      { CCI_REG8(0x55f3), 0x01 }, { CCI_REG8(0x55f4), 0x08 },
++      { CCI_REG8(0x55f5), 0x07 }, { CCI_REG8(0x55f6), 0x06 },
++      { CCI_REG8(0x55f7), 0x05 }, { CCI_REG8(0x55f8), 0x0c },
++      { CCI_REG8(0x55f9), 0x0b }, { CCI_REG8(0x55fa), 0x0a },
++      { CCI_REG8(0x55fb), 0x09 }, { CCI_REG8(0x55fc), 0x10 },
++      { CCI_REG8(0x55fd), 0x0f }, { CCI_REG8(0x55fe), 0x0e },
++      { CCI_REG8(0x55ff), 0x0d }, { CCI_REG8(0x5600), 0x14 },
++      { CCI_REG8(0x5601), 0x13 }, { CCI_REG8(0x5602), 0x12 },
++      { CCI_REG8(0x5603), 0x11 }, { CCI_REG8(0x5604), 0x18 },
++      { CCI_REG8(0x5605), 0x17 }, { CCI_REG8(0x5606), 0x16 },
++      { CCI_REG8(0x5607), 0x15 }, { CCI_REG8(0x5608), 0x1c },
++      { CCI_REG8(0x5609), 0x1b }, { CCI_REG8(0x560a), 0x1a },
++      { CCI_REG8(0x560b), 0x19 }, { CCI_REG8(0x560c), 0x20 },
++      { CCI_REG8(0x560d), 0x1f }, { CCI_REG8(0x560e), 0x1e },
++      { CCI_REG8(0x560f), 0x1d }, { CCI_REG8(0x5631), 0x02 },
++      { CCI_REG8(0x5632), 0x42 }, { CCI_REG8(0x5633), 0x24 },
++      { CCI_REG8(0x5653), 0x09 }, { CCI_REG8(0x5654), 0x00 },
++      { CCI_REG8(0x5714), 0x03 }, { CCI_REG8(0x57b0), 0x10 },
++      { CCI_REG8(0x57b3), 0x0e }, { CCI_REG8(0x57b5), 0x02 },
++      { CCI_REG8(0x57b6), 0x00 }, { CCI_REG8(0x57b7), 0x00 },
++      { CCI_REG8(0x57b8), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++      { CCI_REG8(0x57ba), 0x00 }, { CCI_REG8(0x57bb), 0x10 },
++      { CCI_REG8(0x57bc), 0x00 }, { CCI_REG8(0x57bd), 0x00 },
++      { CCI_REG8(0x57d2), 0xff }, { CCI_REG8(0x57d3), 0x1c },
++      { CCI_REG8(0x5810), 0x03 }, { CCI_REG8(0x5811), 0xe8 },
++      { CCI_REG8(0x5850), 0x6c }, { CCI_REG8(0x5851), 0x00 },
++      { CCI_REG8(0x587a), 0x00 }, { CCI_REG8(0x587b), 0x38 },
++      { CCI_REG8(0x587c), 0x00 }, { CCI_REG8(0x587d), 0x4b },
++      { CCI_REG8(0x5890), 0x00 }, { CCI_REG8(0x5891), 0x50 },
++      { CCI_REG8(0x5892), 0x00 }, { CCI_REG8(0x5893), 0x50 },
++      { CCI_REG8(0x5894), 0x00 }, { CCI_REG8(0x5895), 0x50 },
++      { CCI_REG8(0x5896), 0x00 }, { CCI_REG8(0x5897), 0x50 },
++      { CCI_REG8(0x5898), 0x00 }, { CCI_REG8(0x5899), 0x50 },
++      { CCI_REG8(0x589a), 0x01 }, { CCI_REG8(0x589b), 0x00 },
++      { CCI_REG8(0x589c), 0x01 }, { CCI_REG8(0x589d), 0x00 },
++      { CCI_REG8(0x589e), 0x00 }, { CCI_REG8(0x589f), 0x50 },
++      { CCI_REG8(0x58a0), 0x00 }, { CCI_REG8(0x58a1), 0x50 },
++      { CCI_REG8(0x58a2), 0x01 }, { CCI_REG8(0x58a3), 0x00 },
++      { CCI_REG8(0x58a4), 0x01 }, { CCI_REG8(0x58a5), 0x00 },
++      { CCI_REG8(0x58a6), 0x00 }, { CCI_REG8(0x58a7), 0x50 },
++      { CCI_REG8(0x58a8), 0x00 }, { CCI_REG8(0x58a9), 0x50 },
++      { CCI_REG8(0x58aa), 0x00 }, { CCI_REG8(0x58ab), 0x50 },
++      { CCI_REG8(0x58ac), 0x00 }, { CCI_REG8(0x58ad), 0x50 },
++      { CCI_REG8(0x58ae), 0x00 }, { CCI_REG8(0x58af), 0x50 },
++      { CCI_REG8(0x58b0), 0x00 }, { CCI_REG8(0x58b1), 0x50 },
++      { CCI_REG8(0x58b2), 0x00 }, { CCI_REG8(0x58b3), 0x50 },
++      { CCI_REG8(0x58b4), 0x00 }, { CCI_REG8(0x58b5), 0x50 },
++      { CCI_REG8(0x58b6), 0x00 }, { CCI_REG8(0x58b7), 0x50 },
++      { CCI_REG8(0x58b8), 0x00 }, { CCI_REG8(0x58b9), 0x50 },
++      { CCI_REG8(0x58ba), 0x01 }, { CCI_REG8(0x58bb), 0x00 },
++      { CCI_REG8(0x58bc), 0x01 }, { CCI_REG8(0x58bd), 0x00 },
++      { CCI_REG8(0x58be), 0x00 }, { CCI_REG8(0x58bf), 0x50 },
++      { CCI_REG8(0x58c0), 0x00 }, { CCI_REG8(0x58c1), 0x50 },
++      { CCI_REG8(0x58c2), 0x01 }, { CCI_REG8(0x58c3), 0x00 },
++      { CCI_REG8(0x58c4), 0x01 }, { CCI_REG8(0x58c5), 0x00 },
++      { CCI_REG8(0x58c6), 0x00 }, { CCI_REG8(0x58c7), 0x50 },
++      { CCI_REG8(0x58c8), 0x00 }, { CCI_REG8(0x58c9), 0x50 },
++      { CCI_REG8(0x58ca), 0x00 }, { CCI_REG8(0x58cb), 0x50 },
++      { CCI_REG8(0x58cc), 0x00 }, { CCI_REG8(0x58cd), 0x50 },
++      { CCI_REG8(0x58ce), 0x00 }, { CCI_REG8(0x58cf), 0x50 },
++      { CCI_REG8(0x58f0), 0x04 }, { CCI_REG8(0x58f1), 0x03 },
++      { CCI_REG8(0x58f2), 0x02 }, { CCI_REG8(0x58f3), 0x01 },
++      { CCI_REG8(0x58f4), 0x08 }, { CCI_REG8(0x58f5), 0x07 },
++      { CCI_REG8(0x58f6), 0x06 }, { CCI_REG8(0x58f7), 0x05 },
++      { CCI_REG8(0x58f8), 0x0c }, { CCI_REG8(0x58f9), 0x0b },
++      { CCI_REG8(0x58fa), 0x0a }, { CCI_REG8(0x58fb), 0x09 },
++      { CCI_REG8(0x58fc), 0x10 }, { CCI_REG8(0x58fd), 0x0f },
++      { CCI_REG8(0x58fe), 0x0e }, { CCI_REG8(0x58ff), 0x0d },
++      { CCI_REG8(0x5900), 0x14 }, { CCI_REG8(0x5901), 0x13 },
++      { CCI_REG8(0x5902), 0x12 }, { CCI_REG8(0x5903), 0x11 },
++      { CCI_REG8(0x5904), 0x18 }, { CCI_REG8(0x5905), 0x17 },
++      { CCI_REG8(0x5906), 0x16 }, { CCI_REG8(0x5907), 0x15 },
++      { CCI_REG8(0x5908), 0x1c }, { CCI_REG8(0x5909), 0x1b },
++      { CCI_REG8(0x590a), 0x1a }, { CCI_REG8(0x590b), 0x19 },
++      { CCI_REG8(0x590c), 0x20 }, { CCI_REG8(0x590d), 0x1f },
++      { CCI_REG8(0x590e), 0x1e }, { CCI_REG8(0x590f), 0x1d },
++      { CCI_REG8(0x5931), 0x02 }, { CCI_REG8(0x5932), 0x42 },
++      { CCI_REG8(0x5933), 0x24 }, { CCI_REG8(0x5953), 0x09 },
++      { CCI_REG8(0x5954), 0x00 }, { CCI_REG8(0x5989), 0x84 },
++      { CCI_REG8(0x59c3), 0x04 }, { CCI_REG8(0x59c4), 0x24 },
++      { CCI_REG8(0x59c5), 0x40 }, { CCI_REG8(0x59c6), 0x1b },
++      { CCI_REG8(0x59c7), 0x40 }, { CCI_REG8(0x5a02), 0x0f },
++      { CCI_REG8(0x5f00), 0x29 }, { CCI_REG8(0x5f2d), 0x28 },
++      { CCI_REG8(0x5f2e), 0x28 }, { CCI_REG8(0x6801), 0x11 },
++      { CCI_REG8(0x6802), 0x3f }, { CCI_REG8(0x6803), 0xe7 },
++      { CCI_REG8(0x6825), 0x0f }, { CCI_REG8(0x6826), 0x20 },
++      { CCI_REG8(0x6827), 0x00 }, { CCI_REG8(0x6829), 0x16 },
++      { CCI_REG8(0x682b), 0xb3 }, { CCI_REG8(0x682c), 0x01 },
++      { CCI_REG8(0x6832), 0xff }, { CCI_REG8(0x6833), 0xff },
++      { CCI_REG8(0x6898), 0x80 }, { CCI_REG8(0x6899), 0x80 },
++      { CCI_REG8(0x689b), 0x40 }, { CCI_REG8(0x689c), 0x20 },
++      { CCI_REG8(0x689d), 0x20 }, { CCI_REG8(0x689e), 0x80 },
++      { CCI_REG8(0x689f), 0x60 }, { CCI_REG8(0x68a0), 0x40 },
++      { CCI_REG8(0x68a4), 0x40 }, { CCI_REG8(0x68a5), 0x20 },
++      { CCI_REG8(0x68a6), 0x00 }, { CCI_REG8(0x68b6), 0x80 },
++      { CCI_REG8(0x68b7), 0x80 }, { CCI_REG8(0x68b8), 0x80 },
++      { CCI_REG8(0x68bc), 0x80 }, { CCI_REG8(0x68bd), 0x80 },
++      { CCI_REG8(0x68be), 0x80 }, { CCI_REG8(0x68bf), 0x40 },
++      { CCI_REG8(0x68c2), 0x80 }, { CCI_REG8(0x68c3), 0x80 },
++      { CCI_REG8(0x68c4), 0x60 }, { CCI_REG8(0x68c5), 0x30 },
++      { CCI_REG8(0x6918), 0x80 }, { CCI_REG8(0x6919), 0x80 },
++      { CCI_REG8(0x691b), 0x40 }, { CCI_REG8(0x691c), 0x20 },
++      { CCI_REG8(0x691d), 0x20 }, { CCI_REG8(0x691e), 0x80 },
++      { CCI_REG8(0x691f), 0x60 }, { CCI_REG8(0x6920), 0x40 },
++      { CCI_REG8(0x6924), 0x40 }, { CCI_REG8(0x6925), 0x20 },
++      { CCI_REG8(0x6926), 0x00 }, { CCI_REG8(0x6936), 0x40 },
++      { CCI_REG8(0x6937), 0x40 }, { CCI_REG8(0x6938), 0x20 },
++      { CCI_REG8(0x6939), 0x20 }, { CCI_REG8(0x693a), 0x10 },
++      { CCI_REG8(0x693b), 0x10 }, { CCI_REG8(0x693c), 0x20 },
++      { CCI_REG8(0x693d), 0x20 }, { CCI_REG8(0x693e), 0x10 },
++      { CCI_REG8(0x693f), 0x10 }, { CCI_REG8(0x6940), 0x00 },
++      { CCI_REG8(0x6941), 0x00 }, { CCI_REG8(0x6942), 0x08 },
++      { CCI_REG8(0x6943), 0x08 }, { CCI_REG8(0x6944), 0x00 },
++      { CCI_REG8(0x69c2), 0x07 }, { CCI_REG8(0x6a20), 0x01 },
++      { CCI_REG8(0x6a23), 0x10 }, { CCI_REG8(0x6a26), 0x3d },
++      { CCI_REG8(0x6a27), 0x3e }, { CCI_REG8(0x6a38), 0x02 },
++      { CCI_REG8(0x6a39), 0x20 }, { CCI_REG8(0x6a3a), 0x02 },
++      { CCI_REG8(0x6a3b), 0x84 }, { CCI_REG8(0x6a3e), 0x02 },
++      { CCI_REG8(0x6a3f), 0x20 }, { CCI_REG8(0x6a47), 0x3b },
++      { CCI_REG8(0x6a63), 0x04 }, { CCI_REG8(0x6a65), 0x00 },
++      { CCI_REG8(0x6a67), 0x0f }, { CCI_REG8(0x6b22), 0x07 },
++      { CCI_REG8(0x6b23), 0xc2 }, { CCI_REG8(0x6b2f), 0x00 },
++      { CCI_REG8(0x6b60), 0x1f }, { CCI_REG8(0x6bd2), 0x5a },
++      { CCI_REG8(0x6c20), 0x50 }, { CCI_REG8(0x6c60), 0x50 },
++      { CCI_REG8(0x6c61), 0x06 }, { CCI_REG8(0x7318), 0x04 },
++      { CCI_REG8(0x7319), 0x01 }, { CCI_REG8(0x731a), 0x04 },
++      { CCI_REG8(0x731b), 0x01 }, { CCI_REG8(0x731c), 0x00 },
++      { CCI_REG8(0x731d), 0x00 }, { CCI_REG8(0x731e), 0x04 },
++      { CCI_REG8(0x731f), 0x01 }, { CCI_REG8(0x7320), 0x04 },
++      { CCI_REG8(0x7321), 0x00 }, { CCI_REG8(0x7322), 0x04 },
++      { CCI_REG8(0x7323), 0x00 }, { CCI_REG8(0x7324), 0x04 },
++      { CCI_REG8(0x7325), 0x00 }, { CCI_REG8(0x7326), 0x04 },
++      { CCI_REG8(0x7327), 0x00 }, { CCI_REG8(0x7600), 0x00 },
++      { CCI_REG8(0x7601), 0x00 }, { CCI_REG8(0x7602), 0x10 },
++      { CCI_REG8(0x7603), 0x00 }, { CCI_REG8(0x7604), 0x00 },
++      { CCI_REG8(0x7605), 0x00 }, { CCI_REG8(0x7606), 0x10 },
++      { CCI_REG8(0x7607), 0x00 }, { CCI_REG8(0x7608), 0x00 },
++      { CCI_REG8(0x7609), 0x00 }, { CCI_REG8(0x760a), 0x10 },
++      { CCI_REG8(0x760b), 0x00 }, { CCI_REG8(0x760c), 0x00 },
++      { CCI_REG8(0x760d), 0x00 }, { CCI_REG8(0x760e), 0x10 },
++      { CCI_REG8(0x760f), 0x00 }, { CCI_REG8(0x7610), 0x00 },
++      { CCI_REG8(0x7611), 0x00 }, { CCI_REG8(0x7612), 0x10 },
++      { CCI_REG8(0x7613), 0x00 }, { CCI_REG8(0x7614), 0x00 },
++      { CCI_REG8(0x7615), 0x00 }, { CCI_REG8(0x7616), 0x10 },
++      { CCI_REG8(0x7617), 0x00 }, { CCI_REG8(0x7618), 0x00 },
++      { CCI_REG8(0x7619), 0x00 }, { CCI_REG8(0x761a), 0x10 },
++      { CCI_REG8(0x761b), 0x00 }, { CCI_REG8(0x761c), 0x00 },
++      { CCI_REG8(0x761d), 0x00 }, { CCI_REG8(0x761e), 0x10 },
++      { CCI_REG8(0x761f), 0x00 }, { CCI_REG8(0x7620), 0x00 },
++      { CCI_REG8(0x7621), 0x00 }, { CCI_REG8(0x7622), 0x10 },
++      { CCI_REG8(0x7623), 0x00 }, { CCI_REG8(0x7624), 0x00 },
++      { CCI_REG8(0x7625), 0x00 }, { CCI_REG8(0x7626), 0x10 },
++      { CCI_REG8(0x7627), 0x00 }, { CCI_REG8(0x7628), 0x00 },
++      { CCI_REG8(0x7629), 0x00 }, { CCI_REG8(0x762a), 0x10 },
++      { CCI_REG8(0x762b), 0x00 }, { CCI_REG8(0x762c), 0x00 },
++      { CCI_REG8(0x762d), 0x00 }, { CCI_REG8(0x762e), 0x10 },
++      { CCI_REG8(0x762f), 0x00 }, { CCI_REG8(0x7630), 0x00 },
++      { CCI_REG8(0x7631), 0x00 }, { CCI_REG8(0x7632), 0x10 },
++      { CCI_REG8(0x7633), 0x00 }, { CCI_REG8(0x7634), 0x00 },
++      { CCI_REG8(0x7635), 0x00 }, { CCI_REG8(0x7636), 0x10 },
++      { CCI_REG8(0x7637), 0x00 }, { CCI_REG8(0x7638), 0x00 },
++      { CCI_REG8(0x7639), 0x00 }, { CCI_REG8(0x763a), 0x10 },
++      { CCI_REG8(0x763b), 0x00 }, { CCI_REG8(0x763c), 0x00 },
++      { CCI_REG8(0x763d), 0x00 }, { CCI_REG8(0x763e), 0x10 },
++      { CCI_REG8(0x763f), 0x00 }, { CCI_REG8(0x7640), 0x00 },
++      { CCI_REG8(0x7641), 0x00 }, { CCI_REG8(0x7642), 0x10 },
++      { CCI_REG8(0x7643), 0x00 }, { CCI_REG8(0x7644), 0x00 },
++      { CCI_REG8(0x7645), 0x00 }, { CCI_REG8(0x7646), 0x10 },
++      { CCI_REG8(0x7647), 0x00 }, { CCI_REG8(0x7648), 0x00 },
++      { CCI_REG8(0x7649), 0x00 }, { CCI_REG8(0x764a), 0x10 },
++      { CCI_REG8(0x764b), 0x00 }, { CCI_REG8(0x764c), 0x00 },
++      { CCI_REG8(0x764d), 0x00 }, { CCI_REG8(0x764e), 0x10 },
++      { CCI_REG8(0x764f), 0x00 }, { CCI_REG8(0x7650), 0x00 },
++      { CCI_REG8(0x7651), 0x00 }, { CCI_REG8(0x7652), 0x10 },
++      { CCI_REG8(0x7653), 0x00 }, { CCI_REG8(0x7654), 0x00 },
++      { CCI_REG8(0x7655), 0x00 }, { CCI_REG8(0x7656), 0x10 },
++      { CCI_REG8(0x7657), 0x00 }, { CCI_REG8(0x7658), 0x00 },
++      { CCI_REG8(0x7659), 0x00 }, { CCI_REG8(0x765a), 0x10 },
++      { CCI_REG8(0x765b), 0x00 }, { CCI_REG8(0x765c), 0x00 },
++      { CCI_REG8(0x765d), 0x00 }, { CCI_REG8(0x765e), 0x10 },
++      { CCI_REG8(0x765f), 0x00 }, { CCI_REG8(0x7660), 0x00 },
++      { CCI_REG8(0x7661), 0x00 }, { CCI_REG8(0x7662), 0x10 },
++      { CCI_REG8(0x7663), 0x00 }, { CCI_REG8(0x7664), 0x00 },
++      { CCI_REG8(0x7665), 0x00 }, { CCI_REG8(0x7666), 0x10 },
++      { CCI_REG8(0x7667), 0x00 }, { CCI_REG8(0x7668), 0x00 },
++      { CCI_REG8(0x7669), 0x00 }, { CCI_REG8(0x766a), 0x10 },
++      { CCI_REG8(0x766b), 0x00 }, { CCI_REG8(0x766c), 0x00 },
++      { CCI_REG8(0x766d), 0x00 }, { CCI_REG8(0x766e), 0x10 },
++      { CCI_REG8(0x766f), 0x00 }, { CCI_REG8(0x7670), 0x00 },
++      { CCI_REG8(0x7671), 0x00 }, { CCI_REG8(0x7672), 0x10 },
++      { CCI_REG8(0x7673), 0x00 }, { CCI_REG8(0x7674), 0x00 },
++      { CCI_REG8(0x7675), 0x00 }, { CCI_REG8(0x7676), 0x10 },
++      { CCI_REG8(0x7677), 0x00 }, { CCI_REG8(0x7678), 0x00 },
++      { CCI_REG8(0x7679), 0x00 }, { CCI_REG8(0x767a), 0x10 },
++      { CCI_REG8(0x767b), 0x00 }, { CCI_REG8(0x767c), 0x00 },
++      { CCI_REG8(0x767d), 0x00 }, { CCI_REG8(0x767e), 0x10 },
++      { CCI_REG8(0x767f), 0x00 }, { CCI_REG8(0x7680), 0x00 },
++      { CCI_REG8(0x7681), 0x00 }, { CCI_REG8(0x7682), 0x10 },
++      { CCI_REG8(0x7683), 0x00 }, { CCI_REG8(0x7684), 0x00 },
++      { CCI_REG8(0x7685), 0x00 }, { CCI_REG8(0x7686), 0x10 },
++      { CCI_REG8(0x7687), 0x00 }, { CCI_REG8(0x7688), 0x00 },
++      { CCI_REG8(0x7689), 0x00 }, { CCI_REG8(0x768a), 0x10 },
++      { CCI_REG8(0x768b), 0x00 }, { CCI_REG8(0x768c), 0x00 },
++      { CCI_REG8(0x768d), 0x00 }, { CCI_REG8(0x768e), 0x10 },
++      { CCI_REG8(0x768f), 0x00 }, { CCI_REG8(0x7690), 0x00 },
++      { CCI_REG8(0x7691), 0x00 }, { CCI_REG8(0x7692), 0x10 },
++      { CCI_REG8(0x7693), 0x00 }, { CCI_REG8(0x7694), 0x00 },
++      { CCI_REG8(0x7695), 0x00 }, { CCI_REG8(0x7696), 0x10 },
++      { CCI_REG8(0x7697), 0x00 }, { CCI_REG8(0x7698), 0x00 },
++      { CCI_REG8(0x7699), 0x00 }, { CCI_REG8(0x769a), 0x10 },
++      { CCI_REG8(0x769b), 0x00 }, { CCI_REG8(0x769c), 0x00 },
++      { CCI_REG8(0x769d), 0x00 }, { CCI_REG8(0x769e), 0x10 },
++      { CCI_REG8(0x769f), 0x00 }, { CCI_REG8(0x76a0), 0x00 },
++      { CCI_REG8(0x76a1), 0x00 }, { CCI_REG8(0x76a2), 0x10 },
++      { CCI_REG8(0x76a3), 0x00 }, { CCI_REG8(0x76a4), 0x00 },
++      { CCI_REG8(0x76a5), 0x00 }, { CCI_REG8(0x76a6), 0x10 },
++      { CCI_REG8(0x76a7), 0x00 }, { CCI_REG8(0x76a8), 0x00 },
++      { CCI_REG8(0x76a9), 0x00 }, { CCI_REG8(0x76aa), 0x10 },
++      { CCI_REG8(0x76ab), 0x00 }, { CCI_REG8(0x76ac), 0x00 },
++      { CCI_REG8(0x76ad), 0x00 }, { CCI_REG8(0x76ae), 0x10 },
++      { CCI_REG8(0x76af), 0x00 }, { CCI_REG8(0x76b0), 0x00 },
++      { CCI_REG8(0x76b1), 0x00 }, { CCI_REG8(0x76b2), 0x10 },
++      { CCI_REG8(0x76b3), 0x00 }, { CCI_REG8(0x76b4), 0x00 },
++      { CCI_REG8(0x76b5), 0x00 }, { CCI_REG8(0x76b6), 0x10 },
++      { CCI_REG8(0x76b7), 0x00 }, { CCI_REG8(0x76b8), 0x00 },
++      { CCI_REG8(0x76b9), 0x00 }, { CCI_REG8(0x76ba), 0x10 },
++      { CCI_REG8(0x76bb), 0x00 }, { CCI_REG8(0x76bc), 0x00 },
++      { CCI_REG8(0x76bd), 0x00 }, { CCI_REG8(0x76be), 0x10 },
++      { CCI_REG8(0x76bf), 0x00 }, { CCI_REG8(0x76c0), 0x00 },
++      { CCI_REG8(0x76c1), 0x00 }, { CCI_REG8(0x76c2), 0x10 },
++      { CCI_REG8(0x76c3), 0x00 }, { CCI_REG8(0x76c4), 0x00 },
++      { CCI_REG8(0x76c5), 0x00 }, { CCI_REG8(0x76c6), 0x10 },
++      { CCI_REG8(0x76c7), 0x00 }, { CCI_REG8(0x76c8), 0x00 },
++      { CCI_REG8(0x76c9), 0x00 }, { CCI_REG8(0x76ca), 0x10 },
++      { CCI_REG8(0x76cb), 0x00 }, { CCI_REG8(0x76cc), 0x00 },
++      { CCI_REG8(0x76cd), 0x00 }, { CCI_REG8(0x76ce), 0x10 },
++      { CCI_REG8(0x76cf), 0x00 }, { CCI_REG8(0x76d0), 0x00 },
++      { CCI_REG8(0x76d1), 0x00 }, { CCI_REG8(0x76d2), 0x10 },
++      { CCI_REG8(0x76d3), 0x00 }, { CCI_REG8(0x76d4), 0x00 },
++      { CCI_REG8(0x76d5), 0x00 }, { CCI_REG8(0x76d6), 0x10 },
++      { CCI_REG8(0x76d7), 0x00 }, { CCI_REG8(0x76d8), 0x00 },
++      { CCI_REG8(0x76d9), 0x00 }, { CCI_REG8(0x76da), 0x10 },
++      { CCI_REG8(0x76db), 0x00 }, { CCI_REG8(0x76dc), 0x00 },
++      { CCI_REG8(0x76dd), 0x00 }, { CCI_REG8(0x76de), 0x10 },
++      { CCI_REG8(0x76df), 0x00 }, { CCI_REG8(0x76e0), 0x00 },
++      { CCI_REG8(0x76e1), 0x00 }, { CCI_REG8(0x76e2), 0x10 },
++      { CCI_REG8(0x76e3), 0x00 }, { CCI_REG8(0x76e4), 0x00 },
++      { CCI_REG8(0x76e5), 0x00 }, { CCI_REG8(0x76e6), 0x10 },
++      { CCI_REG8(0x76e7), 0x00 }, { CCI_REG8(0x76e8), 0x00 },
++      { CCI_REG8(0x76e9), 0x00 }, { CCI_REG8(0x76ea), 0x10 },
++      { CCI_REG8(0x76eb), 0x00 }, { CCI_REG8(0x76ec), 0x00 },
++      { CCI_REG8(0x76ed), 0x00 }, { CCI_REG8(0x76ee), 0x10 },
++      { CCI_REG8(0x76ef), 0x00 }, { CCI_REG8(0x76f0), 0x00 },
++      { CCI_REG8(0x76f1), 0x00 }, { CCI_REG8(0x76f2), 0x10 },
++      { CCI_REG8(0x76f3), 0x00 }, { CCI_REG8(0x76f4), 0x00 },
++      { CCI_REG8(0x76f5), 0x00 }, { CCI_REG8(0x76f6), 0x10 },
++      { CCI_REG8(0x76f7), 0x00 }, { CCI_REG8(0x76f8), 0x00 },
++      { CCI_REG8(0x76f9), 0x00 }, { CCI_REG8(0x76fa), 0x10 },
++      { CCI_REG8(0x76fb), 0x00 }, { CCI_REG8(0x76fc), 0x00 },
++      { CCI_REG8(0x76fd), 0x00 }, { CCI_REG8(0x76fe), 0x10 },
++      { CCI_REG8(0x76ff), 0x00 }, { CCI_REG8(0x7700), 0x00 },
++      { CCI_REG8(0x7701), 0x00 }, { CCI_REG8(0x7702), 0x10 },
++      { CCI_REG8(0x7703), 0x00 }, { CCI_REG8(0x7704), 0x00 },
++      { CCI_REG8(0x7705), 0x00 }, { CCI_REG8(0x7706), 0x10 },
++      { CCI_REG8(0x7707), 0x00 }, { CCI_REG8(0x7708), 0x00 },
++      { CCI_REG8(0x7709), 0x00 }, { CCI_REG8(0x770a), 0x10 },
++      { CCI_REG8(0x770b), 0x00 }, { CCI_REG8(0x770c), 0x00 },
++      { CCI_REG8(0x770d), 0x00 }, { CCI_REG8(0x770e), 0x10 },
++      { CCI_REG8(0x770f), 0x00 }, { CCI_REG8(0x7710), 0x00 },
++      { CCI_REG8(0x7711), 0x00 }, { CCI_REG8(0x7712), 0x10 },
++      { CCI_REG8(0x7713), 0x00 }, { CCI_REG8(0x7714), 0x00 },
++      { CCI_REG8(0x7715), 0x00 }, { CCI_REG8(0x7716), 0x10 },
++      { CCI_REG8(0x7717), 0x00 }, { CCI_REG8(0x7718), 0x00 },
++      { CCI_REG8(0x7719), 0x00 }, { CCI_REG8(0x771a), 0x10 },
++      { CCI_REG8(0x771b), 0x00 }, { CCI_REG8(0x771c), 0x00 },
++      { CCI_REG8(0x771d), 0x00 }, { CCI_REG8(0x771e), 0x10 },
++      { CCI_REG8(0x771f), 0x00 }, { CCI_REG8(0x7720), 0x00 },
++      { CCI_REG8(0x7721), 0x00 }, { CCI_REG8(0x7722), 0x10 },
++      { CCI_REG8(0x7723), 0x00 }, { CCI_REG8(0x7724), 0x00 },
++      { CCI_REG8(0x7725), 0x00 }, { CCI_REG8(0x7726), 0x10 },
++      { CCI_REG8(0x7727), 0x00 }, { CCI_REG8(0x7728), 0x00 },
++      { CCI_REG8(0x7729), 0x00 }, { CCI_REG8(0x772a), 0x10 },
++      { CCI_REG8(0x772b), 0x00 }, { CCI_REG8(0x772c), 0x00 },
++      { CCI_REG8(0x772d), 0x00 }, { CCI_REG8(0x772e), 0x10 },
++      { CCI_REG8(0x772f), 0x00 }, { CCI_REG8(0x7730), 0x00 },
++      { CCI_REG8(0x7731), 0x00 }, { CCI_REG8(0x7732), 0x10 },
++      { CCI_REG8(0x7733), 0x00 }, { CCI_REG8(0x7734), 0x00 },
++      { CCI_REG8(0x7735), 0x00 }, { CCI_REG8(0x7736), 0x10 },
++      { CCI_REG8(0x7737), 0x00 }, { CCI_REG8(0x7738), 0x00 },
++      { CCI_REG8(0x7739), 0x00 }, { CCI_REG8(0x773a), 0x10 },
++      { CCI_REG8(0x773b), 0x00 }, { CCI_REG8(0x773c), 0x00 },
++      { CCI_REG8(0x773d), 0x00 }, { CCI_REG8(0x773e), 0x10 },
++      { CCI_REG8(0x773f), 0x00 }, { CCI_REG8(0x7740), 0x00 },
++      { CCI_REG8(0x7741), 0x00 }, { CCI_REG8(0x7742), 0x10 },
++      { CCI_REG8(0x7743), 0x00 }, { CCI_REG8(0x3421), 0x02 },
++      { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x3632), 0x99 },
++      { CCI_REG8(0xc518), 0x1f }, { CCI_REG8(0xc519), 0x1f },
++      { CCI_REG8(0xc51a), 0x1f }, { CCI_REG8(0xc51b), 0x1f },
++      { CCI_REG8(0xc51c), 0x1f }, { CCI_REG8(0xc51d), 0x1f },
++      { CCI_REG8(0xc51e), 0x1f }, { CCI_REG8(0xc51f), 0x1f },
++      { CCI_REG8(0xc520), 0x1f }, { CCI_REG8(0xc521), 0x1f },
++      { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3615), 0xc5 },
++      { CCI_REG8(0xc4c1), 0x02 }, { CCI_REG8(0xc4c2), 0x02 },
++      { CCI_REG8(0xc4c3), 0x03 }, { CCI_REG8(0xc4c4), 0x03 },
++      { CCI_REG8(0xc4f6), 0x0a }, { CCI_REG8(0xc4f7), 0x0a },
++      { CCI_REG8(0xc4f8), 0x0a }, { CCI_REG8(0xc4f9), 0x0a },
++      { CCI_REG8(0xc4fa), 0x0a }, { CCI_REG8(0xc4c6), 0x0a },
++      { CCI_REG8(0xc4c7), 0x0a }, { CCI_REG8(0xc4c8), 0x0a },
++      { CCI_REG8(0xc4c9), 0x0a }, { CCI_REG8(0xc4ca), 0x14 },
++      { CCI_REG8(0xc4cb), 0x14 }, { CCI_REG8(0xc4cc), 0x14 },
++      { CCI_REG8(0xc4cd), 0x14 }, { CCI_REG8(0x3b92), 0x05 },
++      { CCI_REG8(0x3b93), 0x05 }, { CCI_REG8(0x3b94), 0x05 },
++      { CCI_REG8(0x3b95), 0x05 }, { CCI_REG8(0x3623), 0x10 },
++      { CCI_REG8(0xc522), 0x18 }, { CCI_REG8(0xc523), 0x12 },
++      { CCI_REG8(0xc524), 0x0e }, { CCI_REG8(0xc525), 0x0b },
++      { CCI_REG8(0xc526), 0x18 }, { CCI_REG8(0xc527), 0x12 },
++      { CCI_REG8(0xc528), 0x0c }, { CCI_REG8(0xc529), 0x08 },
++      { CCI_REG8(0xc52a), 0x18 }, { CCI_REG8(0xc52b), 0x12 },
++      { CCI_REG8(0xc52c), 0x0e }, { CCI_REG8(0xc52d), 0x0b },
++      { CCI_REG8(0xc52e), 0x18 }, { CCI_REG8(0xc52f), 0x12 },
++      { CCI_REG8(0xc530), 0x0e }, { CCI_REG8(0xc531), 0x0b },
++      { CCI_REG8(0xc532), 0x18 }, { CCI_REG8(0xc533), 0x12 },
++      { CCI_REG8(0xc534), 0x0e }, { CCI_REG8(0xc535), 0x0b },
++      { CCI_REG8(0xc536), 0x18 }, { CCI_REG8(0xc537), 0x12 },
++      { CCI_REG8(0xc538), 0x0e }, { CCI_REG8(0xc539), 0x0b },
++      { CCI_REG8(0xc53a), 0x18 }, { CCI_REG8(0xc53b), 0x12 },
++      { CCI_REG8(0xc53c), 0x0c }, { CCI_REG8(0xc53d), 0x08 },
++      { CCI_REG8(0xc53e), 0x18 }, { CCI_REG8(0xc53f), 0x12 },
++      { CCI_REG8(0xc540), 0x0e }, { CCI_REG8(0xc541), 0x0b },
++      { CCI_REG8(0xc542), 0x18 }, { CCI_REG8(0xc543), 0x12 },
++      { CCI_REG8(0xc544), 0x0e }, { CCI_REG8(0xc545), 0x0b },
++      { CCI_REG8(0xc546), 0x18 }, { CCI_REG8(0xc547), 0x12 },
++      { CCI_REG8(0xc548), 0x0e }, { CCI_REG8(0xc549), 0x0b },
++      { CCI_REG8(0x3701), 0x18 }, { CCI_REG8(0x3702), 0x38 },
++      { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3708), 0x26 },
++      { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a1d), 0x18 },
++      { CCI_REG8(0x3a1e), 0x18 }, { CCI_REG8(0x3a21), 0x18 },
++      { CCI_REG8(0x3a22), 0x18 }, { CCI_REG8(0x39fb), 0x18 },
++      { CCI_REG8(0x39fc), 0x18 }, { CCI_REG8(0x39fd), 0x18 },
++      { CCI_REG8(0x39fe), 0x18 }, { CCI_REG8(0xc44a), 0x08 },
++      { CCI_REG8(0xc44c), 0x08 }, { CCI_REG8(0xc5e8), 0x0a },
++      { CCI_REG8(0xc5ea), 0x0a }, { CCI_REG8(0x391d), 0x54 },
++      { CCI_REG8(0x391e), 0xca }, { CCI_REG8(0x3991), 0x0c },
++      { CCI_REG8(0x399d), 0x0c }, { CCI_REG8(0x3744), 0x24 },
++      { CCI_REG8(0x374b), 0x0c }, { CCI_REG8(0x3be7), 0x1e },
++      { CCI_REG8(0x3be8), 0x26 }, { CCI_REG8(0x3a50), 0x14 },
++      { CCI_REG8(0x3a54), 0x14 }, { CCI_REG8(0x3add), 0x1f },
++      { CCI_REG8(0x3adf), 0x24 }, { CCI_REG8(0x3aef), 0x1f },
++      { CCI_REG8(0x3af0), 0x24 }, { CCI_REG8(0xc57f), 0x30 },
++      { CCI_REG8(0xc580), 0x30 }, { CCI_REG8(0xc581), 0x30 },
++      { CCI_REG8(0xc582), 0x30 }, { CCI_REG8(0xc583), 0x30 },
++      { CCI_REG8(0xc584), 0x30 }, { CCI_REG8(0xc585), 0x30 },
++      { CCI_REG8(0xc586), 0x30 }, { CCI_REG8(0xc587), 0x30 },
++      { CCI_REG8(0xc588), 0x30 }, { CCI_REG8(0xc589), 0x30 },
++      { CCI_REG8(0xc58a), 0x30 }, { CCI_REG8(0xc58b), 0x30 },
++      { CCI_REG8(0xc58c), 0x30 }, { CCI_REG8(0xc58d), 0x30 },
++      { CCI_REG8(0xc58e), 0x30 }, { CCI_REG8(0xc58f), 0x30 },
++      { CCI_REG8(0xc590), 0x30 }, { CCI_REG8(0xc591), 0x30 },
++      { CCI_REG8(0xc592), 0x30 }, { CCI_REG8(0xc598), 0x30 },
++      { CCI_REG8(0xc599), 0x30 }, { CCI_REG8(0xc59a), 0x30 },
++      { CCI_REG8(0xc59b), 0x30 }, { CCI_REG8(0xc59c), 0x30 },
++      { CCI_REG8(0xc59d), 0x30 }, { CCI_REG8(0xc59e), 0x30 },
++      { CCI_REG8(0xc59f), 0x30 }, { CCI_REG8(0xc5a0), 0x30 },
++      { CCI_REG8(0xc5a1), 0x30 }, { CCI_REG8(0xc5a2), 0x30 },
++      { CCI_REG8(0xc5a3), 0x30 }, { CCI_REG8(0xc5a4), 0x30 },
++      { CCI_REG8(0xc5a5), 0x30 }, { CCI_REG8(0xc5a6), 0x30 },
++      { CCI_REG8(0xc5a7), 0x30 }, { CCI_REG8(0xc5a8), 0x30 },
++      { CCI_REG8(0xc5a9), 0x30 }, { CCI_REG8(0xc5aa), 0x30 },
++      { CCI_REG8(0xc5ab), 0x30 }, { CCI_REG8(0xc5b1), 0x38 },
++      { CCI_REG8(0xc5b2), 0x38 }, { CCI_REG8(0xc5b3), 0x38 },
++      { CCI_REG8(0xc5b4), 0x38 }, { CCI_REG8(0xc5b5), 0x38 },
++      { CCI_REG8(0xc5b6), 0x38 }, { CCI_REG8(0xc5b7), 0x38 },
++      { CCI_REG8(0xc5b8), 0x38 }, { CCI_REG8(0xc5b9), 0x38 },
++      { CCI_REG8(0xc5ba), 0x38 }, { CCI_REG8(0xc5bb), 0x38 },
++      { CCI_REG8(0xc5bc), 0x38 }, { CCI_REG8(0xc5bd), 0x38 },
++      { CCI_REG8(0xc5be), 0x38 }, { CCI_REG8(0xc5bf), 0x38 },
++      { CCI_REG8(0xc5c0), 0x38 }, { CCI_REG8(0xc5c1), 0x38 },
++      { CCI_REG8(0xc5c2), 0x38 }, { CCI_REG8(0xc5c3), 0x38 },
++      { CCI_REG8(0xc5c4), 0x38 }, { CCI_REG8(0xc5ca), 0x38 },
++      { CCI_REG8(0xc5cb), 0x38 }, { CCI_REG8(0xc5cc), 0x38 },
++      { CCI_REG8(0xc5cd), 0x38 }, { CCI_REG8(0xc5ce), 0x38 },
++      { CCI_REG8(0xc5cf), 0x38 }, { CCI_REG8(0xc5d0), 0x38 },
++      { CCI_REG8(0xc5d1), 0x38 }, { CCI_REG8(0xc5d2), 0x38 },
++      { CCI_REG8(0xc5d3), 0x38 }, { CCI_REG8(0xc5d4), 0x38 },
++      { CCI_REG8(0xc5d5), 0x38 }, { CCI_REG8(0xc5d6), 0x38 },
++      { CCI_REG8(0xc5d7), 0x38 }, { CCI_REG8(0xc5d8), 0x38 },
++      { CCI_REG8(0xc5d9), 0x38 }, { CCI_REG8(0xc5da), 0x38 },
++      { CCI_REG8(0xc5db), 0x38 }, { CCI_REG8(0xc5dc), 0x38 },
++      { CCI_REG8(0xc5dd), 0x38 }, { CCI_REG8(0x3a60), 0x68 },
++      { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc },
++      { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3aed), 0x6e },
++      { CCI_REG8(0x3af1), 0x73 }, { CCI_REG8(0x3992), 0x02 },
++      { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x371d), 0x17 },
++      { CCI_REG8(0x371f), 0x08 }, { CCI_REG8(0x3721), 0xc9 },
++      { CCI_REG8(0x401e), 0x00 }, { CCI_REG8(0x401f), 0xf8 },
++      { CCI_REG8(0x3642), 0x00 }, { CCI_REG8(0x3641), 0x7f },
++      { CCI_REG8(0x3ac5), 0x0c }, { CCI_REG8(0x3ac6), 0x09 },
++      { CCI_REG8(0x3ac7), 0x06 }, { CCI_REG8(0x3ac8), 0x02 },
++      { CCI_REG8(0x3ac9), 0x0c }, { CCI_REG8(0x3aca), 0x09 },
++      { CCI_REG8(0x3acb), 0x06 }, { CCI_REG8(0x3acc), 0x02 },
++      { CCI_REG8(0x3acd), 0x0c }, { CCI_REG8(0x3ace), 0x09 },
++      { CCI_REG8(0x3acf), 0x07 }, { CCI_REG8(0x3ad0), 0x04 },
++      { CCI_REG8(0x3ad1), 0x0c }, { CCI_REG8(0x3ad2), 0x09 },
++      { CCI_REG8(0x3ad3), 0x07 }, { CCI_REG8(0x3ad4), 0x04 },
++      { CCI_REG8(0xc483), 0x0c }, { CCI_REG8(0xc484), 0x0c },
++      { CCI_REG8(0xc485), 0x0c }, { CCI_REG8(0xc486), 0x0c },
++      { CCI_REG8(0x3a2f), 0x0c }, { CCI_REG8(0x3a30), 0x09 },
++      { CCI_REG8(0x3a31), 0x06 }, { CCI_REG8(0x3a32), 0x02 },
++      { CCI_REG8(0x3a34), 0x0c }, { CCI_REG8(0x3a35), 0x09 },
++      { CCI_REG8(0x3a36), 0x07 }, { CCI_REG8(0x3a37), 0x04 },
++      { CCI_REG8(0x3a43), 0x0c }, { CCI_REG8(0x3a44), 0x09 },
++      { CCI_REG8(0x3a45), 0x06 }, { CCI_REG8(0x3a46), 0x02 },
++      { CCI_REG8(0x3a48), 0x0c }, { CCI_REG8(0x3a49), 0x09 },
++      { CCI_REG8(0x3a4a), 0x07 }, { CCI_REG8(0x3a4b), 0x04 },
++      { CCI_REG8(0xc487), 0x0c }, { CCI_REG8(0xc488), 0x0c },
++      { CCI_REG8(0xc489), 0x0c }, { CCI_REG8(0xc48a), 0x0c },
++      { CCI_REG8(0x3645), 0xbd }, { CCI_REG8(0x373f), 0x00 },
++      { CCI_REG8(0x374f), 0x10 }, { CCI_REG8(0x3743), 0xc6 },
++      { CCI_REG8(0x3717), 0x82 }, { CCI_REG8(0x3732), 0x07 },
++      { CCI_REG8(0x3731), 0x16 }, { CCI_REG8(0x3730), 0x16 },
++      { CCI_REG8(0x3828), 0x07 }, { CCI_REG8(0x3714), 0x68 },
++      { CCI_REG8(0x371d), 0x02 }, { CCI_REG8(0x371f), 0x02 },
++      { CCI_REG8(0x37e0), 0x00 }, { CCI_REG8(0x37e1), 0x03 },
++      { CCI_REG8(0x37e2), 0x07 }, { CCI_REG8(0x3734), 0x3e },
++      { CCI_REG8(0x3736), 0x02 }, { CCI_REG8(0x37e4), 0x36 },
++      { CCI_REG8(0x37e9), 0x1c }, { CCI_REG8(0x37ea), 0x01 },
++      { CCI_REG8(0x37eb), 0x0a }, { CCI_REG8(0x37ec), 0x1c },
++      { CCI_REG8(0x37ed), 0x01 }, { CCI_REG8(0x37ee), 0x36 },
++      { CCI_REG8(0x373b), 0x1c }, { CCI_REG8(0x373c), 0x02 },
++      { CCI_REG8(0x37bb), 0x1c }, { CCI_REG8(0x37bc), 0x02 },
++      { CCI_REG8(0x37b8), 0x0c }, { CCI_REG8(0x371c), 0x01 },
++      { CCI_REG8(0x371e), 0x11 }, { CCI_REG8(0x371d), 0x01 },
++      { CCI_REG8(0x371f), 0x01 }, { CCI_REG8(0x3721), 0x01 },
++      { CCI_REG8(0x3725), 0x12 }, { CCI_REG8(0x37e3), 0x06 },
++      { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37db), 0x0a },
++      { CCI_REG8(0x37dc), 0x14 }, { CCI_REG8(0x3727), 0x20 },
++      { CCI_REG8(0x37b2), 0x80 }, { CCI_REG8(0x37da), 0x04 },
++      { CCI_REG8(0x37df), 0x01 }, { CCI_REG8(0x3731), 0x11 },
++      { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37df), 0x01 },
++      { CCI_REG8(0x37da), 0x03 }, { CCI_REG8(0x37b2), 0x80 },
++      { CCI_REG8(0x3727), 0x20 }, { CCI_REG8(0x4883), 0x26 },
++      { CCI_REG8(0x488b), 0x88 }, { CCI_REG8(0x3d85), 0x1f },
++      { CCI_REG8(0x3d81), 0x01 }, { CCI_REG8(0x3d84), 0x40 },
++      { CCI_REG8(0x3d88), 0x00 }, { CCI_REG8(0x3d89), 0x00 },
++      { CCI_REG8(0x3d8a), 0x0b }, { CCI_REG8(0x3d8b), 0xff },
++      { CCI_REG8(0x4d00), 0x05 }, { CCI_REG8(0x4d01), 0xc4 },
++      { CCI_REG8(0x4d02), 0xa3 }, { CCI_REG8(0x4d03), 0x8c },
++      { CCI_REG8(0x4d04), 0xfb }, { CCI_REG8(0x4d05), 0xed },
++      { CCI_REG8(0x4010), 0x28 }, { CCI_REG8(0x4030), 0x00 },
++      { CCI_REG8(0x4031), 0x00 }, { CCI_REG8(0x4032), 0x00 },
++      { CCI_REG8(0x4033), 0x00 }, { CCI_REG8(0x4034), 0x00 },
++      { CCI_REG8(0x4035), 0x00 }, { CCI_REG8(0x4036), 0x00 },
++      { CCI_REG8(0x4037), 0x00 }, { CCI_REG8(0x4040), 0x00 },
++      { CCI_REG8(0x4041), 0x00 }, { CCI_REG8(0x4042), 0x00 },
++      { CCI_REG8(0x4043), 0x00 }, { CCI_REG8(0x4044), 0x00 },
++      { CCI_REG8(0x4045), 0x00 }, { CCI_REG8(0x4046), 0x00 },
++      { CCI_REG8(0x4047), 0x00 }, { CCI_REG8(0x3400), 0x00 },
++      { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc },
++      { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 },
++      { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 },
++      { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 },
++      { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 },
++      { CCI_REG8(0x3053), 0x00 }, { CCI_REG8(0x3054), 0x00 },
++      { CCI_REG8(0x3055), 0x00 }, { CCI_REG8(0x3056), 0x00 },
++      { CCI_REG8(0x3057), 0x00 }, { CCI_REG8(0x3058), 0x00 },
++      { CCI_REG8(0x305c), 0x00 }, { CCI_REG8(0x340c), 0x1f },
++      { CCI_REG8(0x340d), 0x00 }, { CCI_REG8(0x3501), 0x01 },
++      { CCI_REG8(0x3542), 0x48 }, { CCI_REG8(0x3582), 0x24 },
++      { CCI_REG8(0x3015), 0xf1 }, { CCI_REG8(0x3018), 0xf2 },
++      { CCI_REG8(0x301c), 0xf2 }, { CCI_REG8(0x301d), 0xf6 },
++      { CCI_REG8(0x301e), 0xf1 }, { CCI_REG8(0x0100), 0x01 },
++      { CCI_REG8(0xfff9), 0x08 }, { CCI_REG8(0x3900), 0xcd },
++      { CCI_REG8(0x3901), 0xcd }, { CCI_REG8(0x3902), 0xcd },
++      { CCI_REG8(0x3903), 0xcd }, { CCI_REG8(0x3904), 0xcd },
++      { CCI_REG8(0x3905), 0xcd }, { CCI_REG8(0x3906), 0xcd },
++      { CCI_REG8(0x3907), 0xcd }, { CCI_REG8(0x3908), 0xcd },
++      { CCI_REG8(0x3909), 0xcd }, { CCI_REG8(0x390a), 0xcd },
++      { CCI_REG8(0x390b), 0xcd }, { CCI_REG8(0x390c), 0xcd },
++      { CCI_REG8(0x390d), 0xcd }, { CCI_REG8(0x390e), 0xcd },
++      { CCI_REG8(0x390f), 0xcd }, { CCI_REG8(0x3910), 0xcd },
++      { CCI_REG8(0x3911), 0xcd }, { CCI_REG8(0x3912), 0xcd },
++      { CCI_REG8(0x3913), 0xcd }, { CCI_REG8(0x3914), 0xcd },
++      { CCI_REG8(0x3915), 0xcd }, { CCI_REG8(0x3916), 0xcd },
++      { CCI_REG8(0x3917), 0xcd }, { CCI_REG8(0x3918), 0xcd },
++      { CCI_REG8(0x3919), 0xcd }, { CCI_REG8(0x391a), 0xcd },
++      { CCI_REG8(0x391b), 0xcd }, { CCI_REG8(0x391c), 0xcd },
++      { CCI_REG8(0x391d), 0xcd }, { CCI_REG8(0x391e), 0xcd },
++      { CCI_REG8(0x391f), 0xcd }, { CCI_REG8(0x3920), 0xcd },
++      { CCI_REG8(0x3921), 0xcd }, { CCI_REG8(0x3922), 0xcd },
++      { CCI_REG8(0x3923), 0xcd }, { CCI_REG8(0x3924), 0xcd },
++      { CCI_REG8(0x3925), 0xcd }, { CCI_REG8(0x3926), 0xcd },
++      { CCI_REG8(0x3927), 0xcd }, { CCI_REG8(0x3928), 0xcd },
++      { CCI_REG8(0x3929), 0xcd }, { CCI_REG8(0x392a), 0xcd },
++      { CCI_REG8(0x392b), 0xcd }, { CCI_REG8(0x392c), 0xcd },
++      { CCI_REG8(0x392d), 0xcd }, { CCI_REG8(0x392e), 0xcd },
++      { CCI_REG8(0x392f), 0xcd }, { CCI_REG8(0x3930), 0xcd },
++      { CCI_REG8(0x3931), 0xcd }, { CCI_REG8(0x3932), 0xcd },
++      { CCI_REG8(0x3933), 0xcd }, { CCI_REG8(0x3934), 0xcd },
++      { CCI_REG8(0x3935), 0xcd }, { CCI_REG8(0x3936), 0xcd },
++      { CCI_REG8(0x3937), 0xcd }, { CCI_REG8(0x3938), 0xcd },
++      { CCI_REG8(0x3939), 0xcd }, { CCI_REG8(0x393a), 0xcd },
++      { CCI_REG8(0x393b), 0xcd }, { CCI_REG8(0x393c), 0xcd },
++      { CCI_REG8(0x393d), 0xcd }, { CCI_REG8(0x393e), 0xcd },
++      { CCI_REG8(0x393f), 0xcd }, { CCI_REG8(0x3940), 0xcd },
++      { CCI_REG8(0x3941), 0xcd }, { CCI_REG8(0x3942), 0xcd },
++      { CCI_REG8(0x3943), 0xcd }, { CCI_REG8(0x3944), 0xcd },
++      { CCI_REG8(0x3945), 0xcd }, { CCI_REG8(0x3946), 0xcd },
++      { CCI_REG8(0x3947), 0xcd }, { CCI_REG8(0x3948), 0xcd },
++      { CCI_REG8(0x3949), 0xcd }, { CCI_REG8(0x394a), 0xcd },
++      { CCI_REG8(0x394b), 0xcd }, { CCI_REG8(0x394c), 0xcd },
++      { CCI_REG8(0x394d), 0xcd }, { CCI_REG8(0x394e), 0xcd },
++      { CCI_REG8(0x394f), 0xcd }, { CCI_REG8(0x3950), 0xcd },
++      { CCI_REG8(0x3951), 0xcd }, { CCI_REG8(0x3952), 0xcd },
++      { CCI_REG8(0x3953), 0xcd }, { CCI_REG8(0x3954), 0xcd },
++      { CCI_REG8(0x3955), 0xcd }, { CCI_REG8(0x3956), 0xcd },
++      { CCI_REG8(0x3957), 0xcd }, { CCI_REG8(0x3958), 0xcd },
++      { CCI_REG8(0x3959), 0xcd }, { CCI_REG8(0x395a), 0xcd },
++      { CCI_REG8(0x395b), 0xcd }, { CCI_REG8(0x395c), 0xcd },
++      { CCI_REG8(0x395d), 0xcd }, { CCI_REG8(0x395e), 0xcd },
++      { CCI_REG8(0x395f), 0xcd }, { CCI_REG8(0x3960), 0xcd },
++      { CCI_REG8(0x3961), 0xcd }, { CCI_REG8(0x3962), 0xcd },
++      { CCI_REG8(0x3963), 0xcd }, { CCI_REG8(0x3964), 0xcd },
++      { CCI_REG8(0x3965), 0xcd }, { CCI_REG8(0x3966), 0xcd },
++      { CCI_REG8(0x3967), 0xcd }, { CCI_REG8(0x3968), 0xcd },
++      { CCI_REG8(0x3969), 0xcd }, { CCI_REG8(0x396a), 0xcd },
++      { CCI_REG8(0x396b), 0xcd }, { CCI_REG8(0x396c), 0xcd },
++      { CCI_REG8(0x396d), 0xcd }, { CCI_REG8(0x396e), 0xcd },
++      { CCI_REG8(0x396f), 0xcd }, { CCI_REG8(0x3970), 0xcd },
++      { CCI_REG8(0x3971), 0xcd }, { CCI_REG8(0x3972), 0xcd },
++      { CCI_REG8(0x3973), 0xcd }, { CCI_REG8(0x3974), 0xcd },
++      { CCI_REG8(0x3975), 0xcd }, { CCI_REG8(0x3976), 0xcd },
++      { CCI_REG8(0x3977), 0xcd }, { CCI_REG8(0x3978), 0xcd },
++      { CCI_REG8(0x3979), 0xcd }, { CCI_REG8(0x397a), 0xcd },
++      { CCI_REG8(0x397b), 0xcd }, { CCI_REG8(0x397c), 0xcd },
++      { CCI_REG8(0x397d), 0xcd }, { CCI_REG8(0x397e), 0xcd },
++      { CCI_REG8(0x397f), 0xcd }, { CCI_REG8(0x3980), 0xcd },
++      { CCI_REG8(0x3981), 0xcd }, { CCI_REG8(0x3982), 0xcd },
++      { CCI_REG8(0x3983), 0xcd }, { CCI_REG8(0x3984), 0xcd },
++      { CCI_REG8(0x3985), 0xcd }, { CCI_REG8(0x3986), 0xcd },
++      { CCI_REG8(0x3987), 0xcd }, { CCI_REG8(0x3988), 0xcd },
++      { CCI_REG8(0x3989), 0xcd }, { CCI_REG8(0x398a), 0xcd },
++      { CCI_REG8(0x398b), 0xcd }, { CCI_REG8(0x398c), 0xcd },
++      { CCI_REG8(0x398d), 0xcd }, { CCI_REG8(0x398e), 0xcd },
++      { CCI_REG8(0x398f), 0xcd }, { CCI_REG8(0x3990), 0xcd },
++      { CCI_REG8(0x3991), 0xcd }, { CCI_REG8(0x3992), 0xcd },
++      { CCI_REG8(0x3993), 0xcd }, { CCI_REG8(0x3994), 0xcd },
++      { CCI_REG8(0x3995), 0xcd }, { CCI_REG8(0x3996), 0xcd },
++      { CCI_REG8(0x3997), 0xcd }, { CCI_REG8(0x3998), 0xcd },
++      { CCI_REG8(0x3999), 0xcd }, { CCI_REG8(0x399a), 0xcd },
++      { CCI_REG8(0x399b), 0xcd }, { CCI_REG8(0x399c), 0xcd },
++      { CCI_REG8(0x399d), 0xcd }, { CCI_REG8(0x399e), 0xcd },
++      { CCI_REG8(0x399f), 0xcd }, { CCI_REG8(0x39a0), 0xcd },
++      { CCI_REG8(0x39a1), 0xcd }, { CCI_REG8(0x39a2), 0xcd },
++      { CCI_REG8(0x39a3), 0xcd }, { CCI_REG8(0x39a4), 0xcd },
++      { CCI_REG8(0x39a5), 0xcd }, { CCI_REG8(0x39a6), 0xcd },
++      { CCI_REG8(0x39a7), 0xcd }, { CCI_REG8(0x39a8), 0xcd },
++      { CCI_REG8(0x39a9), 0xcd }, { CCI_REG8(0x39aa), 0xcd },
++      { CCI_REG8(0x39ab), 0xcd }, { CCI_REG8(0x39ac), 0xcd },
++      { CCI_REG8(0x39ad), 0xcd }, { CCI_REG8(0x39ae), 0xcd },
++      { CCI_REG8(0x39af), 0xcd }, { CCI_REG8(0x39b0), 0xcd },
++      { CCI_REG8(0x39b1), 0xcd }, { CCI_REG8(0x39b2), 0xcd },
++      { CCI_REG8(0x39b3), 0xcd }, { CCI_REG8(0x39b4), 0xcd },
++      { CCI_REG8(0x39b5), 0xcd }, { CCI_REG8(0x39b6), 0xcd },
++      { CCI_REG8(0x39b7), 0xcd }, { CCI_REG8(0x39b8), 0xcd },
++      { CCI_REG8(0x39b9), 0xcd }, { CCI_REG8(0x39ba), 0xcd },
++      { CCI_REG8(0x39bb), 0xcd }, { CCI_REG8(0x39bc), 0xcd },
++      { CCI_REG8(0x39bd), 0xcd }, { CCI_REG8(0x39be), 0xcd },
++      { CCI_REG8(0x39bf), 0xcd }, { CCI_REG8(0x39c0), 0xcd },
++      { CCI_REG8(0x39c1), 0xcd }, { CCI_REG8(0x39c2), 0xcd },
++      { CCI_REG8(0x39c3), 0xcd }, { CCI_REG8(0x39c4), 0xcd },
++      { CCI_REG8(0x39c5), 0xcd }, { CCI_REG8(0x39c6), 0xcd },
++      { CCI_REG8(0x39c7), 0xcd }, { CCI_REG8(0x39c8), 0xcd },
++      { CCI_REG8(0x39c9), 0xcd }, { CCI_REG8(0x39ca), 0xcd },
++      { CCI_REG8(0x39cb), 0xcd }, { CCI_REG8(0x39cc), 0xcd },
++      { CCI_REG8(0x39cd), 0xcd }, { CCI_REG8(0x39ce), 0xcd },
++      { CCI_REG8(0x39cf), 0xcd }, { CCI_REG8(0x39d0), 0xcd },
++      { CCI_REG8(0x39d1), 0xcd }, { CCI_REG8(0x39d2), 0xcd },
++      { CCI_REG8(0x39d3), 0xcd }, { CCI_REG8(0x39d4), 0xcd },
++      { CCI_REG8(0x39d5), 0xcd }, { CCI_REG8(0x39d6), 0xcd },
++      { CCI_REG8(0x39d7), 0xcd }, { CCI_REG8(0x39d8), 0xcd },
++      { CCI_REG8(0x39d9), 0xcd }, { CCI_REG8(0x39da), 0xcd },
++      { CCI_REG8(0x39db), 0xcd }, { CCI_REG8(0x39dc), 0xcd },
++      { CCI_REG8(0x39dd), 0xcd }, { CCI_REG8(0x39de), 0xcd },
++      { CCI_REG8(0x39df), 0xcd }, { CCI_REG8(0x39e0), 0xcd },
++      { CCI_REG8(0x39e1), 0x40 }, { CCI_REG8(0x39e2), 0x40 },
++      { CCI_REG8(0x39e3), 0x40 }, { CCI_REG8(0x39e4), 0x40 },
++      { CCI_REG8(0x39e5), 0x40 }, { CCI_REG8(0x39e6), 0x40 },
++      { CCI_REG8(0x39e7), 0x40 }, { CCI_REG8(0x39e8), 0x40 },
++      { CCI_REG8(0x39e9), 0x40 }, { CCI_REG8(0x39ea), 0x40 },
++      { CCI_REG8(0x39eb), 0x40 }, { CCI_REG8(0x39ec), 0x40 },
++      { CCI_REG8(0x39ed), 0x40 }, { CCI_REG8(0x39ee), 0x40 },
++      { CCI_REG8(0x39ef), 0x40 }, { CCI_REG8(0x39f0), 0x40 },
++      { CCI_REG8(0x39f1), 0x40 }, { CCI_REG8(0x39f2), 0x40 },
++      { CCI_REG8(0x39f3), 0x40 }, { CCI_REG8(0x39f4), 0x40 },
++      { CCI_REG8(0x39f5), 0x40 }, { CCI_REG8(0x39f6), 0x40 },
++      { CCI_REG8(0x39f7), 0x40 }, { CCI_REG8(0x39f8), 0x40 },
++      { CCI_REG8(0x39f9), 0x40 }, { CCI_REG8(0x39fa), 0x40 },
++      { CCI_REG8(0x39fb), 0x40 }, { CCI_REG8(0x39fc), 0x40 },
++      { CCI_REG8(0x39fd), 0x40 }, { CCI_REG8(0x39fe), 0x40 },
++      { CCI_REG8(0x39ff), 0x40 }, { CCI_REG8(0x3a00), 0x40 },
++      { CCI_REG8(0x3a01), 0x40 }, { CCI_REG8(0x3a02), 0x40 },
++      { CCI_REG8(0x3a03), 0x40 }, { CCI_REG8(0x3a04), 0x40 },
++      { CCI_REG8(0x3a05), 0x40 }, { CCI_REG8(0x3a06), 0x40 },
++      { CCI_REG8(0x3a07), 0x40 }, { CCI_REG8(0x3a08), 0x40 },
++      { CCI_REG8(0x3a09), 0x40 }, { CCI_REG8(0x3a0a), 0x40 },
++      { CCI_REG8(0x3a0b), 0x40 }, { CCI_REG8(0x3a0c), 0x40 },
++      { CCI_REG8(0x3a0d), 0x40 }, { CCI_REG8(0x3a0e), 0x40 },
++      { CCI_REG8(0x3a0f), 0x40 }, { CCI_REG8(0x3a10), 0x40 },
++      { CCI_REG8(0x3a11), 0x40 }, { CCI_REG8(0x3a12), 0x40 },
++      { CCI_REG8(0x3a13), 0x40 }, { CCI_REG8(0x3a14), 0x40 },
++      { CCI_REG8(0x3a15), 0x40 }, { CCI_REG8(0x3a16), 0x40 },
++      { CCI_REG8(0x3a17), 0x40 }, { CCI_REG8(0x3a18), 0x40 },
++      { CCI_REG8(0x3a19), 0x40 }, { CCI_REG8(0x3a1a), 0x40 },
++      { CCI_REG8(0x3a1b), 0x40 }, { CCI_REG8(0x3a1c), 0x40 },
++      { CCI_REG8(0x3a1d), 0x40 }, { CCI_REG8(0x3a1e), 0x40 },
++      { CCI_REG8(0x3a1f), 0x40 }, { CCI_REG8(0x3a20), 0x40 },
++      { CCI_REG8(0x3a21), 0x40 }, { CCI_REG8(0x3a22), 0x40 },
++      { CCI_REG8(0x3a23), 0x40 }, { CCI_REG8(0x3a24), 0x40 },
++      { CCI_REG8(0x3a25), 0x40 }, { CCI_REG8(0x3a26), 0x40 },
++      { CCI_REG8(0x3a27), 0x40 }, { CCI_REG8(0x3a28), 0x40 },
++      { CCI_REG8(0x3a29), 0x40 }, { CCI_REG8(0x3a2a), 0x40 },
++      { CCI_REG8(0x3a2b), 0x40 }, { CCI_REG8(0x3a2c), 0x40 },
++      { CCI_REG8(0x3a2d), 0x40 }, { CCI_REG8(0x3a2e), 0x40 },
++      { CCI_REG8(0x3a2f), 0x40 }, { CCI_REG8(0x3a30), 0x40 },
++      { CCI_REG8(0x3a31), 0x40 }, { CCI_REG8(0x3a32), 0x40 },
++      { CCI_REG8(0x3a33), 0x40 }, { CCI_REG8(0x3a34), 0x40 },
++      { CCI_REG8(0x3a35), 0x40 }, { CCI_REG8(0x3a36), 0x40 },
++      { CCI_REG8(0x3a37), 0x40 }, { CCI_REG8(0x3a38), 0x40 },
++      { CCI_REG8(0x3a39), 0x40 }, { CCI_REG8(0x3a3a), 0x40 },
++      { CCI_REG8(0x3a3b), 0xcd }, { CCI_REG8(0x3a3c), 0xcd },
++      { CCI_REG8(0x3a3d), 0xcd }, { CCI_REG8(0x3a3e), 0xcd },
++      { CCI_REG8(0x3a3f), 0xcd }, { CCI_REG8(0x3a40), 0xcd },
++      { CCI_REG8(0x3a41), 0xcd }, { CCI_REG8(0x3a42), 0xcd },
++      { CCI_REG8(0x3a43), 0xcd }, { CCI_REG8(0x3a44), 0xcd },
++      { CCI_REG8(0x3a45), 0xcd }, { CCI_REG8(0x3a46), 0xcd },
++      { CCI_REG8(0x3a47), 0xcd }, { CCI_REG8(0x3a48), 0xcd },
++      { CCI_REG8(0x3a49), 0xcd }, { CCI_REG8(0x3a4a), 0xcd },
++      { CCI_REG8(0x3a4b), 0xcd }, { CCI_REG8(0x3a4c), 0xcd },
++      { CCI_REG8(0x3a4d), 0xcd }, { CCI_REG8(0x3a4e), 0xcd },
++      { CCI_REG8(0x3a4f), 0xcd }, { CCI_REG8(0x3a50), 0xcd },
++      { CCI_REG8(0x3a51), 0xcd }, { CCI_REG8(0x3a52), 0xcd },
++      { CCI_REG8(0x3a53), 0xcd }, { CCI_REG8(0x3a54), 0xcd },
++      { CCI_REG8(0x3a55), 0xcd }, { CCI_REG8(0x3a56), 0xcd },
++      { CCI_REG8(0x3a57), 0xcd }, { CCI_REG8(0x3a58), 0xcd },
++      { CCI_REG8(0x3a59), 0xcd }, { CCI_REG8(0x3a5a), 0xcd },
++      { CCI_REG8(0x3a5b), 0xcd }, { CCI_REG8(0x3a5c), 0xcd },
++      { CCI_REG8(0x3a5d), 0xcd }, { CCI_REG8(0x3a5e), 0xcd },
++      { CCI_REG8(0x3a5f), 0xcd }, { CCI_REG8(0x3a60), 0xcd },
++      { CCI_REG8(0x3a61), 0xcd }, { CCI_REG8(0x3a62), 0xcd },
++      { CCI_REG8(0x3a63), 0xcd }, { CCI_REG8(0x3a64), 0xcd },
++      { CCI_REG8(0x3a65), 0xcd }, { CCI_REG8(0x3a66), 0xcd },
++      { CCI_REG8(0x3a67), 0xcd }, { CCI_REG8(0x3a68), 0xcd },
++      { CCI_REG8(0x3a69), 0xcd }, { CCI_REG8(0x3a6a), 0xcd },
++      { CCI_REG8(0x3a6b), 0xcd }, { CCI_REG8(0x3a6c), 0xcd },
++      { CCI_REG8(0x3a6d), 0xcd }, { CCI_REG8(0x3a6e), 0xcd },
++      { CCI_REG8(0x3a6f), 0xcd }, { CCI_REG8(0x3a70), 0xcd },
++      { CCI_REG8(0x3a71), 0xcd }, { CCI_REG8(0x3a72), 0xcd },
++      { CCI_REG8(0x3a73), 0xcd }, { CCI_REG8(0x3a74), 0xcd },
++      { CCI_REG8(0x3a75), 0xcd }, { CCI_REG8(0x3a76), 0xcd },
++      { CCI_REG8(0x3a77), 0xcd }, { CCI_REG8(0x3a78), 0xcd },
++      { CCI_REG8(0x3a79), 0xcd }, { CCI_REG8(0x3a7a), 0xcd },
++      { CCI_REG8(0x3a7b), 0xcd }, { CCI_REG8(0x3a7c), 0xcd },
++      { CCI_REG8(0x3a7d), 0xcd }, { CCI_REG8(0x3a7e), 0xcd },
++      { CCI_REG8(0x3a7f), 0xcd }, { CCI_REG8(0x3a80), 0xcd },
++      { CCI_REG8(0x3a81), 0xcd }, { CCI_REG8(0x3a82), 0xcd },
++      { CCI_REG8(0x3a83), 0xcd }, { CCI_REG8(0x3a84), 0xcd },
++      { CCI_REG8(0x3a85), 0xcd }, { CCI_REG8(0x3a86), 0xcd },
++      { CCI_REG8(0x3a87), 0xcd }, { CCI_REG8(0x3a88), 0xcd },
++      { CCI_REG8(0x3a89), 0xcd }, { CCI_REG8(0x3a8a), 0xcd },
++      { CCI_REG8(0x3a8b), 0xcd }, { CCI_REG8(0x3a8c), 0xcd },
++      { CCI_REG8(0x3a8d), 0xcd }, { CCI_REG8(0x3a8e), 0xcd },
++      { CCI_REG8(0x3a8f), 0xcd }, { CCI_REG8(0x3a90), 0xcd },
++      { CCI_REG8(0x3a91), 0xcd }, { CCI_REG8(0x3a92), 0xcd },
++      { CCI_REG8(0x3a93), 0xcd }, { CCI_REG8(0x3a94), 0xcd },
++      { CCI_REG8(0x3a95), 0x40 }, { CCI_REG8(0x3a96), 0x40 },
++      { CCI_REG8(0x3a97), 0x40 }, { CCI_REG8(0x3a98), 0x40 },
++      { CCI_REG8(0x3a99), 0x40 }, { CCI_REG8(0x3a9a), 0x40 },
++      { CCI_REG8(0x3a9b), 0x40 }, { CCI_REG8(0x3a9c), 0x40 },
++      { CCI_REG8(0x3a9d), 0x40 }, { CCI_REG8(0x3a9e), 0x40 },
++      { CCI_REG8(0x3a9f), 0x40 }, { CCI_REG8(0x3aa0), 0x40 },
++      { CCI_REG8(0x3aa1), 0x40 }, { CCI_REG8(0x3aa2), 0x40 },
++      { CCI_REG8(0x3aa3), 0x40 }, { CCI_REG8(0x3aa4), 0x40 },
++      { CCI_REG8(0x3aa5), 0x40 }, { CCI_REG8(0x3aa6), 0x40 },
++      { CCI_REG8(0x3aa7), 0x40 }, { CCI_REG8(0x3aa8), 0x40 },
++      { CCI_REG8(0x3aa9), 0x40 }, { CCI_REG8(0x3aaa), 0x40 },
++      { CCI_REG8(0x3aab), 0x40 }, { CCI_REG8(0x3aac), 0x40 },
++      { CCI_REG8(0x3aad), 0x40 }, { CCI_REG8(0x3aae), 0x40 },
++      { CCI_REG8(0x3aaf), 0x40 }, { CCI_REG8(0x3ab0), 0x40 },
++      { CCI_REG8(0x3ab1), 0x40 }, { CCI_REG8(0x3ab2), 0x40 },
++      { CCI_REG8(0x3ab3), 0x40 }, { CCI_REG8(0x3ab4), 0x40 },
++      { CCI_REG8(0x3ab5), 0x40 }, { CCI_REG8(0x3ab6), 0x40 },
++      { CCI_REG8(0x3ab7), 0x40 }, { CCI_REG8(0x3ab8), 0x40 },
++      { CCI_REG8(0x3ab9), 0x40 }, { CCI_REG8(0x3aba), 0x40 },
++      { CCI_REG8(0x3abb), 0x40 }, { CCI_REG8(0x3abc), 0x40 },
++      { CCI_REG8(0x3abd), 0x40 }, { CCI_REG8(0x3abe), 0x40 },
++      { CCI_REG8(0x3abf), 0x40 }, { CCI_REG8(0x3ac0), 0x40 },
++      { CCI_REG8(0x3ac1), 0x40 }, { CCI_REG8(0x3ac2), 0x40 },
++      { CCI_REG8(0x3ac3), 0x40 }, { CCI_REG8(0x3ac4), 0x40 },
++      { CCI_REG8(0x3ac5), 0x40 }, { CCI_REG8(0x3ac6), 0x40 },
++      { CCI_REG8(0x3ac7), 0x40 }, { CCI_REG8(0x3ac8), 0x40 },
++      { CCI_REG8(0x3ac9), 0x40 }, { CCI_REG8(0x3aca), 0x40 },
++      { CCI_REG8(0x3acb), 0x40 }, { CCI_REG8(0x3acc), 0x40 },
++      { CCI_REG8(0x3acd), 0x40 }, { CCI_REG8(0x3ace), 0x40 },
++      { CCI_REG8(0x3acf), 0x40 }, { CCI_REG8(0x3ad0), 0x40 },
++      { CCI_REG8(0x3ad1), 0x40 }, { CCI_REG8(0x3ad2), 0x40 },
++      { CCI_REG8(0x3ad3), 0x40 }, { CCI_REG8(0x3ad4), 0x40 },
++      { CCI_REG8(0x3ad5), 0x40 }, { CCI_REG8(0x3ad6), 0x40 },
++      { CCI_REG8(0x3ad7), 0x40 }, { CCI_REG8(0x3ad8), 0x40 },
++      { CCI_REG8(0x3ad9), 0x40 }, { CCI_REG8(0x3ada), 0x40 },
++      { CCI_REG8(0x3adb), 0x40 }, { CCI_REG8(0x3adc), 0x40 },
++      { CCI_REG8(0x3add), 0x40 }, { CCI_REG8(0x3ade), 0x40 },
++      { CCI_REG8(0x3adf), 0x40 }, { CCI_REG8(0x3ae0), 0x40 },
++      { CCI_REG8(0x3ae1), 0x40 }, { CCI_REG8(0x3ae2), 0x40 },
++      { CCI_REG8(0x3ae3), 0x40 }, { CCI_REG8(0x3ae4), 0x40 },
++      { CCI_REG8(0x3ae5), 0x40 }, { CCI_REG8(0x3ae6), 0x40 },
++      { CCI_REG8(0x3ae7), 0x40 }, { CCI_REG8(0x3ae8), 0x40 },
++      { CCI_REG8(0x3ae9), 0x40 }, { CCI_REG8(0x3aea), 0x40 },
++      { CCI_REG8(0x3aeb), 0x40 }, { CCI_REG8(0x3aec), 0x40 },
++      { CCI_REG8(0x3aed), 0x40 }, { CCI_REG8(0x3aee), 0x40 },
++      { CCI_REG8(0x3aef), 0xcd }, { CCI_REG8(0x3af0), 0xcd },
++      { CCI_REG8(0x3af1), 0xcd }, { CCI_REG8(0x3af2), 0xcd },
++      { CCI_REG8(0x3af3), 0xcd }, { CCI_REG8(0x3af4), 0xcd },
++      { CCI_REG8(0x3af5), 0xcd }, { CCI_REG8(0x3af6), 0xcd },
++      { CCI_REG8(0x3af7), 0xcd }, { CCI_REG8(0x3af8), 0xcd },
++      { CCI_REG8(0x3af9), 0xcd }, { CCI_REG8(0x3afa), 0xcd },
++      { CCI_REG8(0x3afb), 0xcd }, { CCI_REG8(0x3afc), 0xcd },
++      { CCI_REG8(0x3afd), 0xcd }, { CCI_REG8(0x3afe), 0xcd },
++      { CCI_REG8(0x3aff), 0xcd }, { CCI_REG8(0x3b00), 0xcd },
++      { CCI_REG8(0x3b01), 0xcd }, { CCI_REG8(0x3b02), 0xcd },
++      { CCI_REG8(0x3b03), 0xcd }, { CCI_REG8(0x3b04), 0xcd },
++      { CCI_REG8(0x3b05), 0xcd }, { CCI_REG8(0x3b06), 0xcd },
++      { CCI_REG8(0x3b07), 0xcd }, { CCI_REG8(0x3b08), 0xcd },
++      { CCI_REG8(0x3b09), 0xcd }, { CCI_REG8(0x3b0a), 0xcd },
++      { CCI_REG8(0x3b0b), 0xcd }, { CCI_REG8(0x3b0c), 0xcd },
++      { CCI_REG8(0x3b0d), 0xcd }, { CCI_REG8(0x3b0e), 0xcd },
++      { CCI_REG8(0x3b0f), 0xcd }, { CCI_REG8(0x3b10), 0xcd },
++      { CCI_REG8(0x3b11), 0xcd }, { CCI_REG8(0x3b12), 0xcd },
++      { CCI_REG8(0x3b13), 0xcd }, { CCI_REG8(0x3b14), 0xcd },
++      { CCI_REG8(0x3b15), 0xcd }, { CCI_REG8(0x3b16), 0xcd },
++      { CCI_REG8(0x3b17), 0xcd }, { CCI_REG8(0x3b18), 0xcd },
++      { CCI_REG8(0x3b19), 0xcd }, { CCI_REG8(0x3b1a), 0xcd },
++      { CCI_REG8(0x3b1b), 0xcd }, { CCI_REG8(0x3b1c), 0xcd },
++      { CCI_REG8(0x3b1d), 0xcd }, { CCI_REG8(0x3b1e), 0xcd },
++      { CCI_REG8(0x3b1f), 0xcd }, { CCI_REG8(0x3b20), 0xcd },
++      { CCI_REG8(0x3b21), 0xcd }, { CCI_REG8(0x3b22), 0xcd },
++      { CCI_REG8(0x3b23), 0xcd }, { CCI_REG8(0x3b24), 0xcd },
++      { CCI_REG8(0x3b25), 0xcd }, { CCI_REG8(0x3b26), 0xcd },
++      { CCI_REG8(0x3b27), 0xcd }, { CCI_REG8(0x3b28), 0xcd },
++      { CCI_REG8(0x3b29), 0xcd }, { CCI_REG8(0x3b2a), 0xcd },
++      { CCI_REG8(0x3b2b), 0xcd }, { CCI_REG8(0x3b2c), 0xcd },
++      { CCI_REG8(0x3b2d), 0xcd }, { CCI_REG8(0x3b2e), 0xcd },
++      { CCI_REG8(0x3b2f), 0xcd }, { CCI_REG8(0x3b30), 0xcd },
++      { CCI_REG8(0x3b31), 0xcd }, { CCI_REG8(0x3b32), 0xcd },
++      { CCI_REG8(0x3b33), 0xcd }, { CCI_REG8(0x3b34), 0xcd },
++      { CCI_REG8(0x3b35), 0xcd }, { CCI_REG8(0x3b36), 0xcd },
++      { CCI_REG8(0x3b37), 0xcd }, { CCI_REG8(0x3b38), 0xcd },
++      { CCI_REG8(0x3b39), 0xcd }, { CCI_REG8(0x3b3a), 0xcd },
++      { CCI_REG8(0x3b3b), 0xcd }, { CCI_REG8(0x3b3c), 0xcd },
++      { CCI_REG8(0x3b3d), 0xcd }, { CCI_REG8(0x3b3e), 0xcd },
++      { CCI_REG8(0x3b3f), 0xcd }, { CCI_REG8(0x3b40), 0xcd },
++      { CCI_REG8(0x3b41), 0xcd }, { CCI_REG8(0x3b42), 0xcd },
++      { CCI_REG8(0x3b43), 0xcd }, { CCI_REG8(0x3b44), 0xcd },
++      { CCI_REG8(0x3b45), 0xcd }, { CCI_REG8(0x3b46), 0xcd },
++      { CCI_REG8(0x3b47), 0xcd }, { CCI_REG8(0x3b48), 0xcd },
++      { CCI_REG8(0x3b49), 0xcd }, { CCI_REG8(0x3b4a), 0xcd },
++      { CCI_REG8(0x3b4b), 0xcd }, { CCI_REG8(0x3b4c), 0xcd },
++      { CCI_REG8(0x3b4d), 0xcd }, { CCI_REG8(0x3b4e), 0xcd },
++      { CCI_REG8(0x3b4f), 0xcd }, { CCI_REG8(0x3b50), 0xcd },
++      { CCI_REG8(0x3b51), 0xcd }, { CCI_REG8(0x3b52), 0xcd },
++      { CCI_REG8(0x3b53), 0xcd }, { CCI_REG8(0x3b54), 0xcd },
++      { CCI_REG8(0x3b55), 0xcd }, { CCI_REG8(0x3b56), 0xcd },
++      { CCI_REG8(0x3b57), 0xcd }, { CCI_REG8(0x3b58), 0xcd },
++      { CCI_REG8(0x3b59), 0xcd }, { CCI_REG8(0x3b5a), 0xcd },
++      { CCI_REG8(0x3b5b), 0xcd }, { CCI_REG8(0x3b5c), 0xcd },
++      { CCI_REG8(0x3b5d), 0xcd }, { CCI_REG8(0x3b5e), 0xcd },
++      { CCI_REG8(0x3b5f), 0xcd }, { CCI_REG8(0x3b60), 0xcd },
++      { CCI_REG8(0x3b61), 0xcd }, { CCI_REG8(0x3b62), 0xcd },
++      { CCI_REG8(0x3b63), 0xcd }, { CCI_REG8(0x3b64), 0xcd },
++      { CCI_REG8(0x3b65), 0xcd }, { CCI_REG8(0x3b66), 0xcd },
++      { CCI_REG8(0x3b67), 0xcd }, { CCI_REG8(0x3b68), 0xcd },
++      { CCI_REG8(0x3b69), 0xcd }, { CCI_REG8(0x3b6a), 0xcd },
++      { CCI_REG8(0x3b6b), 0xcd }, { CCI_REG8(0x3b6c), 0xcd },
++      { CCI_REG8(0x3b6d), 0xcd }, { CCI_REG8(0x3b6e), 0xcd },
++      { CCI_REG8(0x3b6f), 0xcd }, { CCI_REG8(0x3b70), 0xcd },
++      { CCI_REG8(0x3b71), 0xcd }, { CCI_REG8(0x3b72), 0xcd },
++      { CCI_REG8(0x3b73), 0xcd }, { CCI_REG8(0x3b74), 0xcd },
++      { CCI_REG8(0x3b75), 0xcd }, { CCI_REG8(0x3b76), 0xcd },
++      { CCI_REG8(0x3b77), 0xcd }, { CCI_REG8(0x3b78), 0xcd },
++      { CCI_REG8(0x3b79), 0xcd }, { CCI_REG8(0x3b7a), 0xcd },
++      { CCI_REG8(0x3b7b), 0xcd }, { CCI_REG8(0x3b7c), 0xcd },
++      { CCI_REG8(0x3b7d), 0xcd }, { CCI_REG8(0x3b7e), 0xcd },
++      { CCI_REG8(0x3b7f), 0xcd }, { CCI_REG8(0x3b80), 0xcd },
++      { CCI_REG8(0x3b81), 0xcd }, { CCI_REG8(0x3b82), 0xcd },
++      { CCI_REG8(0x3b83), 0xcd }, { CCI_REG8(0x3b84), 0xcd },
++      { CCI_REG8(0x3b85), 0xcd }, { CCI_REG8(0x3b86), 0xcd },
++      { CCI_REG8(0x3b87), 0xcd }, { CCI_REG8(0x3b88), 0xcd },
++      { CCI_REG8(0x3b89), 0xcd }, { CCI_REG8(0x3b8a), 0xcd },
++      { CCI_REG8(0x3b8b), 0xcd }, { CCI_REG8(0x3b8c), 0xcd },
++      { CCI_REG8(0x3b8d), 0xcd }, { CCI_REG8(0x3b8e), 0xcd },
++      { CCI_REG8(0x3b8f), 0xcd }, { CCI_REG8(0x3b90), 0xcd },
++      { CCI_REG8(0x3b91), 0xcd }, { CCI_REG8(0x3b92), 0xcd },
++      { CCI_REG8(0x3b93), 0xcd }, { CCI_REG8(0x3b94), 0xcd },
++      { CCI_REG8(0x3b95), 0xcd }, { CCI_REG8(0x3b96), 0xcd },
++      { CCI_REG8(0x3b97), 0xcd }, { CCI_REG8(0x3b98), 0xcd },
++      { CCI_REG8(0x3b99), 0xcd }, { CCI_REG8(0x3b9a), 0xcd },
++      { CCI_REG8(0x3b9b), 0xcd }, { CCI_REG8(0x3b9c), 0xcd },
++      { CCI_REG8(0x3b9d), 0xcd }, { CCI_REG8(0x3b9e), 0xcd },
++      { CCI_REG8(0x3b9f), 0xcd }, { CCI_REG8(0x3ba0), 0xcd },
++      { CCI_REG8(0x3ba1), 0xcd }, { CCI_REG8(0x3ba2), 0xcd },
++      { CCI_REG8(0x3ba3), 0xcd }, { CCI_REG8(0x3ba4), 0xcd },
++      { CCI_REG8(0x3ba5), 0xcd }, { CCI_REG8(0x3ba6), 0xcd },
++      { CCI_REG8(0x3ba7), 0xcd }, { CCI_REG8(0x3ba8), 0xcd },
++      { CCI_REG8(0x3ba9), 0xcd }, { CCI_REG8(0x3baa), 0xcd },
++      { CCI_REG8(0x3bab), 0xcd }, { CCI_REG8(0x3bac), 0xcd },
++      { CCI_REG8(0x3bad), 0xcd }, { CCI_REG8(0x3bae), 0xcd },
++      { CCI_REG8(0x3baf), 0xcd }, { CCI_REG8(0x3bb0), 0xcd },
++      { CCI_REG8(0x3bb1), 0xcd }, { CCI_REG8(0x3bb2), 0xcd },
++      { CCI_REG8(0x3bb3), 0xcd }, { CCI_REG8(0x3bb4), 0xcd },
++      { CCI_REG8(0x3bb5), 0xcd }, { CCI_REG8(0x3bb6), 0xcd },
++      { CCI_REG8(0x3bb7), 0xcd }, { CCI_REG8(0x3bb8), 0xcd },
++      { CCI_REG8(0x3bb9), 0xcd }, { CCI_REG8(0x3bba), 0xcd },
++      { CCI_REG8(0x3bbb), 0xcd }, { CCI_REG8(0x3bbc), 0xcd },
++      { CCI_REG8(0x3bbd), 0xcd }, { CCI_REG8(0x3bbe), 0xcd },
++      { CCI_REG8(0x3bbf), 0xcd }, { CCI_REG8(0x3bc0), 0xcd },
++      { CCI_REG8(0x3bc1), 0xcd }, { CCI_REG8(0x3bc2), 0xcd },
++      { CCI_REG8(0x3bc3), 0xcd }, { CCI_REG8(0x3bc4), 0xcd },
++      { CCI_REG8(0x3bc5), 0xcd }, { CCI_REG8(0x3bc6), 0xcd },
++      { CCI_REG8(0x3bc7), 0xcd }, { CCI_REG8(0x3bc8), 0xcd },
++      { CCI_REG8(0x3bc9), 0xcd }, { CCI_REG8(0x3bca), 0xcd },
++      { CCI_REG8(0x3bcb), 0xcd }, { CCI_REG8(0x3bcc), 0xcd },
++      { CCI_REG8(0x3bcd), 0xcd }, { CCI_REG8(0x3bce), 0xcd },
++      { CCI_REG8(0x3bcf), 0xcd }, { CCI_REG8(0x3bd0), 0xcd },
++      { CCI_REG8(0x3bd1), 0xcd }, { CCI_REG8(0x3bd2), 0xcd },
++      { CCI_REG8(0x3bd3), 0xcd }, { CCI_REG8(0x3bd4), 0xcd },
++      { CCI_REG8(0x3bd5), 0xcd }, { CCI_REG8(0x3bd6), 0xcd },
++      { CCI_REG8(0x3bd7), 0xcd }, { CCI_REG8(0x3bd8), 0xcd },
++      { CCI_REG8(0x3bd9), 0xcd }, { CCI_REG8(0x3bda), 0xcd },
++      { CCI_REG8(0x3bdb), 0xcd }, { CCI_REG8(0x3bdc), 0xcd },
++      { CCI_REG8(0x3bdd), 0xcd }, { CCI_REG8(0x3bde), 0xcd },
++      { CCI_REG8(0x3bdf), 0xcd }, { CCI_REG8(0x3be0), 0xcd },
++      { CCI_REG8(0x3be1), 0xcd }, { CCI_REG8(0x3be2), 0xcd },
++      { CCI_REG8(0x3be3), 0xcd }, { CCI_REG8(0x3be4), 0xcd },
++      { CCI_REG8(0x3be5), 0xcd }, { CCI_REG8(0x3be6), 0xcd },
++      { CCI_REG8(0x3be7), 0xcd }, { CCI_REG8(0x3be8), 0xcd },
++      { CCI_REG8(0x3be9), 0xcd }, { CCI_REG8(0x3bea), 0xcd },
++      { CCI_REG8(0x3beb), 0xcd }, { CCI_REG8(0x3bec), 0xcd },
++      { CCI_REG8(0x3bed), 0xcd }, { CCI_REG8(0x3bee), 0xcd },
++      { CCI_REG8(0x3bef), 0xcd }, { CCI_REG8(0x3bf0), 0xcd },
++      { CCI_REG8(0x3bf1), 0xcd }, { CCI_REG8(0x3bf2), 0xcd },
++      { CCI_REG8(0x3bf3), 0xcd }, { CCI_REG8(0x3bf4), 0xcd },
++      { CCI_REG8(0x3bf5), 0xcd }, { CCI_REG8(0x3bf6), 0xcd },
++      { CCI_REG8(0x3bf7), 0xcd }, { CCI_REG8(0x3bf8), 0xcd },
++      { CCI_REG8(0x3bf9), 0xcd }, { CCI_REG8(0x3bfa), 0xcd },
++      { CCI_REG8(0x3bfb), 0xcd }, { CCI_REG8(0x3bfc), 0xcd },
++      { CCI_REG8(0x3bfd), 0xcd }, { CCI_REG8(0x3bfe), 0xcd },
++      { CCI_REG8(0x3bff), 0xcd }, { CCI_REG8(0x3c00), 0xcd },
++      { CCI_REG8(0x3c01), 0xcd }, { CCI_REG8(0x3c02), 0xcd },
++      { CCI_REG8(0x3c03), 0xcd }, { CCI_REG8(0x3c04), 0xcd },
++      { CCI_REG8(0x3c05), 0xcd }, { CCI_REG8(0x3c06), 0xcd },
++      { CCI_REG8(0x3c07), 0xcd }, { CCI_REG8(0x3c08), 0xcd },
++      { CCI_REG8(0x3c09), 0xcd }, { CCI_REG8(0x3c0a), 0xcd },
++      { CCI_REG8(0x3c0b), 0xcd }, { CCI_REG8(0x3c0c), 0xcd },
++      { CCI_REG8(0x3c0d), 0xcd }, { CCI_REG8(0x3c0e), 0xcd },
++      { CCI_REG8(0x3c0f), 0xcd }, { CCI_REG8(0x3c10), 0xcd },
++      { CCI_REG8(0x3c11), 0xcd }, { CCI_REG8(0x3c12), 0xcd },
++      { CCI_REG8(0x3c13), 0xcd }, { CCI_REG8(0x3c14), 0xcd },
++      { CCI_REG8(0x3c15), 0xcd }, { CCI_REG8(0x3c16), 0xcd },
++      { CCI_REG8(0x3c17), 0xcd }, { CCI_REG8(0x3c18), 0xcd },
++      { CCI_REG8(0x3c19), 0xcd }, { CCI_REG8(0x3c1a), 0xcd },
++      { CCI_REG8(0x3c1b), 0xcd }, { CCI_REG8(0x3c1c), 0xcd },
++      { CCI_REG8(0x3c1d), 0xcd }, { CCI_REG8(0x3c1e), 0xcd },
++      { CCI_REG8(0x3c1f), 0xcd }, { CCI_REG8(0x3c20), 0xcd },
++      { CCI_REG8(0x3c21), 0xcd }, { CCI_REG8(0x3c22), 0xcd },
++      { CCI_REG8(0x3c23), 0xcd }, { CCI_REG8(0x3c24), 0xcd },
++      { CCI_REG8(0x3c25), 0xcd }, { CCI_REG8(0x3c26), 0xcd },
++      { CCI_REG8(0x3c27), 0xcd }, { CCI_REG8(0x3c28), 0xcd },
++      { CCI_REG8(0x3c29), 0xcd }, { CCI_REG8(0x3c2a), 0xcd },
++      { CCI_REG8(0x3c2b), 0xcd }, { CCI_REG8(0x3c2c), 0xcd },
++      { CCI_REG8(0x3c2d), 0xcd }, { CCI_REG8(0x3c2e), 0xcd },
++      { CCI_REG8(0x3c2f), 0xcd }, { CCI_REG8(0x3c30), 0xcd },
++      { CCI_REG8(0x3c31), 0xcd }, { CCI_REG8(0x3c32), 0xcd },
++      { CCI_REG8(0x3c33), 0xcd }, { CCI_REG8(0x3c34), 0xcd },
++      { CCI_REG8(0x3c35), 0xcd }, { CCI_REG8(0x3c36), 0xcd },
++      { CCI_REG8(0x3c37), 0xcd }, { CCI_REG8(0x3c38), 0xcd },
++      { CCI_REG8(0x3c39), 0xcd }, { CCI_REG8(0x3c3a), 0xcd },
++      { CCI_REG8(0x3c3b), 0xcd }, { CCI_REG8(0x3c3c), 0xcd },
++      { CCI_REG8(0x3c3d), 0xcd }, { CCI_REG8(0x3c3e), 0xcd },
++      { CCI_REG8(0x3c3f), 0xcd }, { CCI_REG8(0x3c40), 0xcd },
++      { CCI_REG8(0x3c41), 0xcd }, { CCI_REG8(0x3c42), 0xcd },
++      { CCI_REG8(0x3c43), 0xcd }, { CCI_REG8(0x3c44), 0xcd },
++      { CCI_REG8(0x3c45), 0xcd }, { CCI_REG8(0x3c46), 0xcd },
++      { CCI_REG8(0x3c47), 0xcd }, { CCI_REG8(0x3c48), 0xcd },
++      { CCI_REG8(0x3c49), 0xcd }, { CCI_REG8(0x3c4a), 0xcd },
++      { CCI_REG8(0x3c4b), 0xcd }, { CCI_REG8(0x3c4c), 0xcd },
++      { CCI_REG8(0x3c4d), 0xcd }, { CCI_REG8(0x3c4e), 0xcd },
++      { CCI_REG8(0x3c4f), 0xcd }, { CCI_REG8(0x3c50), 0xcd },
++      { CCI_REG8(0x3c51), 0xcd }, { CCI_REG8(0x3c52), 0xcd },
++      { CCI_REG8(0x3c53), 0xcd }, { CCI_REG8(0x3c54), 0xcd },
++      { CCI_REG8(0x3c55), 0xcd }, { CCI_REG8(0x3c56), 0xcd },
++      { CCI_REG8(0x3c57), 0xcd }, { CCI_REG8(0x3c58), 0xcd },
++      { CCI_REG8(0x3c59), 0xcd }, { CCI_REG8(0x3c5a), 0xcd },
++      { CCI_REG8(0x3c5b), 0xcd }, { CCI_REG8(0x3c5c), 0xcd },
++      { CCI_REG8(0x3c5d), 0xcd }, { CCI_REG8(0x3c5e), 0xcd },
++      { CCI_REG8(0x3c5f), 0xcd }, { CCI_REG8(0x3c60), 0xcd },
++      { CCI_REG8(0x3c61), 0xcd }, { CCI_REG8(0x3c62), 0xcd },
++      { CCI_REG8(0x3c63), 0xcd }, { CCI_REG8(0x3c64), 0xcd },
++      { CCI_REG8(0x3c65), 0xcd }, { CCI_REG8(0x3c66), 0xcd },
++      { CCI_REG8(0x3c67), 0xcd }, { CCI_REG8(0x3c68), 0xcd },
++      { CCI_REG8(0x3c69), 0xcd }, { CCI_REG8(0x3c6a), 0xcd },
++      { CCI_REG8(0x3c6b), 0xcd }, { CCI_REG8(0x3c6c), 0xcd },
++      { CCI_REG8(0x3c6d), 0xcd }, { CCI_REG8(0x3c6e), 0xcd },
++      { CCI_REG8(0x3c6f), 0xcd }, { CCI_REG8(0x3c70), 0xcd },
++      { CCI_REG8(0x3c71), 0xcd }, { CCI_REG8(0x3c72), 0xcd },
++      { CCI_REG8(0x3c73), 0xcd }, { CCI_REG8(0x3c74), 0xcd },
++      { CCI_REG8(0x3c75), 0xcd }, { CCI_REG8(0x3c76), 0xcd },
++      { CCI_REG8(0x3c77), 0xcd }, { CCI_REG8(0x3c78), 0xcd },
++      { CCI_REG8(0x3c79), 0xcd }, { CCI_REG8(0x3c7a), 0xcd },
++      { CCI_REG8(0x3c7b), 0xcd }, { CCI_REG8(0x3c7c), 0xcd },
++      { CCI_REG8(0x3c7d), 0xcd }, { CCI_REG8(0x3c7e), 0xcd },
++      { CCI_REG8(0x3c7f), 0xcd }, { CCI_REG8(0x3c80), 0xcd },
++      { CCI_REG8(0x3c81), 0xcd }, { CCI_REG8(0x3c82), 0xcd },
++      { CCI_REG8(0x3c83), 0xcd }, { CCI_REG8(0x3c84), 0xcd },
++      { CCI_REG8(0x3c85), 0xcd }, { CCI_REG8(0x3c86), 0xcd },
++      { CCI_REG8(0x3c87), 0xcd }, { CCI_REG8(0x3c88), 0xcd },
++      { CCI_REG8(0x3c89), 0xcd }, { CCI_REG8(0x3c8a), 0xcd },
++      { CCI_REG8(0x3c8b), 0xcd }, { CCI_REG8(0x3c8c), 0xcd },
++      { CCI_REG8(0x3c8d), 0xcd }, { CCI_REG8(0x3c8e), 0xcd },
++      { CCI_REG8(0x3c8f), 0xcd }, { CCI_REG8(0x3c90), 0xcd },
++      { CCI_REG8(0x3c91), 0xcd }, { CCI_REG8(0x3c92), 0xcd },
++      { CCI_REG8(0x3c93), 0xcd }, { CCI_REG8(0x3c94), 0xcd },
++      { CCI_REG8(0x3c95), 0xcd }, { CCI_REG8(0x3c96), 0xcd },
++      { CCI_REG8(0x3c97), 0xcd }, { CCI_REG8(0x3c98), 0xcd },
++      { CCI_REG8(0x3c99), 0xcd }, { CCI_REG8(0x3c9a), 0xcd },
++      { CCI_REG8(0x3c9b), 0xcd }, { CCI_REG8(0x3c9c), 0xcd },
++      { CCI_REG8(0x3c9d), 0xcd }, { CCI_REG8(0x3c9e), 0xcd },
++      { CCI_REG8(0x3c9f), 0xcd }, { CCI_REG8(0x3ca0), 0xcd },
++      { CCI_REG8(0x3ca1), 0xcd }, { CCI_REG8(0x3ca2), 0xcd },
++      { CCI_REG8(0x3ca3), 0xcd }, { CCI_REG8(0x3ca4), 0xcd },
++      { CCI_REG8(0x3ca5), 0xcd }, { CCI_REG8(0x3ca6), 0xcd },
++      { CCI_REG8(0x3ca7), 0xcd }, { CCI_REG8(0x3ca8), 0xcd },
++      { CCI_REG8(0x3ca9), 0xcd }, { CCI_REG8(0x3caa), 0xcd },
++      { CCI_REG8(0x3cab), 0xcd }, { CCI_REG8(0x3cac), 0xcd },
++      { CCI_REG8(0x3cad), 0xcd }, { CCI_REG8(0x3cae), 0xcd },
++      { CCI_REG8(0x3caf), 0xcd }, { CCI_REG8(0x3cb0), 0xcd },
++      { CCI_REG8(0x3cb1), 0x40 }, { CCI_REG8(0x3cb2), 0x40 },
++      { CCI_REG8(0x3cb3), 0x40 }, { CCI_REG8(0x3cb4), 0x40 },
++      { CCI_REG8(0x3cb5), 0x40 }, { CCI_REG8(0x3cb6), 0x40 },
++      { CCI_REG8(0x3cb7), 0x40 }, { CCI_REG8(0x3cb8), 0x40 },
++      { CCI_REG8(0x3cb9), 0x40 }, { CCI_REG8(0x3cba), 0x40 },
++      { CCI_REG8(0x3cbb), 0x40 }, { CCI_REG8(0x3cbc), 0x40 },
++      { CCI_REG8(0x3cbd), 0x40 }, { CCI_REG8(0x3cbe), 0x40 },
++      { CCI_REG8(0x3cbf), 0x40 }, { CCI_REG8(0x3cc0), 0x40 },
++      { CCI_REG8(0x3cc1), 0x40 }, { CCI_REG8(0x3cc2), 0x40 },
++      { CCI_REG8(0x3cc3), 0x40 }, { CCI_REG8(0x3cc4), 0x40 },
++      { CCI_REG8(0x3cc5), 0x40 }, { CCI_REG8(0x3cc6), 0x40 },
++      { CCI_REG8(0x3cc7), 0x40 }, { CCI_REG8(0x3cc8), 0x40 },
++      { CCI_REG8(0x3cc9), 0x40 }, { CCI_REG8(0x3cca), 0x40 },
++      { CCI_REG8(0x3ccb), 0x40 }, { CCI_REG8(0x3ccc), 0x40 },
++      { CCI_REG8(0x3ccd), 0x40 }, { CCI_REG8(0x3cce), 0x40 },
++      { CCI_REG8(0x3ccf), 0x40 }, { CCI_REG8(0x3cd0), 0x40 },
++      { CCI_REG8(0x3cd1), 0x40 }, { CCI_REG8(0x3cd2), 0x40 },
++      { CCI_REG8(0x3cd3), 0x40 }, { CCI_REG8(0x3cd4), 0x40 },
++      { CCI_REG8(0x3cd5), 0x40 }, { CCI_REG8(0x3cd6), 0x40 },
++      { CCI_REG8(0x3cd7), 0x40 }, { CCI_REG8(0x3cd8), 0x40 },
++      { CCI_REG8(0x3cd9), 0x40 }, { CCI_REG8(0x3cda), 0x40 },
++      { CCI_REG8(0x3cdb), 0x40 }, { CCI_REG8(0x3cdc), 0x40 },
++      { CCI_REG8(0x3cdd), 0x40 }, { CCI_REG8(0x3cde), 0x40 },
++      { CCI_REG8(0x3cdf), 0x40 }, { CCI_REG8(0x3ce0), 0x40 },
++      { CCI_REG8(0x3ce1), 0x40 }, { CCI_REG8(0x3ce2), 0x40 },
++      { CCI_REG8(0x3ce3), 0x40 }, { CCI_REG8(0x3ce4), 0x40 },
++      { CCI_REG8(0x3ce5), 0x40 }, { CCI_REG8(0x3ce6), 0x40 },
++      { CCI_REG8(0x3ce7), 0x40 }, { CCI_REG8(0x3ce8), 0x40 },
++      { CCI_REG8(0x3ce9), 0x40 }, { CCI_REG8(0x3cea), 0x40 },
++      { CCI_REG8(0x3ceb), 0x40 }, { CCI_REG8(0x3cec), 0x40 },
++      { CCI_REG8(0x3ced), 0x40 }, { CCI_REG8(0x3cee), 0x40 },
++      { CCI_REG8(0x3cef), 0x40 }, { CCI_REG8(0x3cf0), 0x40 },
++      { CCI_REG8(0x3cf1), 0x40 }, { CCI_REG8(0x3cf2), 0x40 },
++      { CCI_REG8(0x3cf3), 0x40 }, { CCI_REG8(0x3cf4), 0x40 },
++      { CCI_REG8(0x3cf5), 0x40 }, { CCI_REG8(0x3cf6), 0x40 },
++      { CCI_REG8(0x3cf7), 0x40 }, { CCI_REG8(0x3cf8), 0x40 },
++      { CCI_REG8(0x3cf9), 0x40 }, { CCI_REG8(0x3cfa), 0x40 },
++      { CCI_REG8(0x3cfb), 0x40 }, { CCI_REG8(0x3cfc), 0x40 },
++      { CCI_REG8(0x3cfd), 0x40 }, { CCI_REG8(0x3cfe), 0x40 },
++      { CCI_REG8(0x3cff), 0x40 }, { CCI_REG8(0x3d00), 0x40 },
++      { CCI_REG8(0x3d01), 0x40 }, { CCI_REG8(0x3d02), 0x40 },
++      { CCI_REG8(0x3d03), 0x40 }, { CCI_REG8(0x3d04), 0x40 },
++      { CCI_REG8(0x3d05), 0x40 }, { CCI_REG8(0x3d06), 0x40 },
++      { CCI_REG8(0x3d07), 0x40 }, { CCI_REG8(0x3d08), 0x40 },
++      { CCI_REG8(0x3d09), 0x40 }, { CCI_REG8(0x3d0a), 0x40 },
++      { CCI_REG8(0x3d0b), 0xcd }, { CCI_REG8(0x3d0c), 0xcd },
++      { CCI_REG8(0x3d0d), 0xcd }, { CCI_REG8(0x3d0e), 0xcd },
++      { CCI_REG8(0x3d0f), 0xcd }, { CCI_REG8(0x3d10), 0xcd },
++      { CCI_REG8(0x3d11), 0xcd }, { CCI_REG8(0x3d12), 0xcd },
++      { CCI_REG8(0x3d13), 0xcd }, { CCI_REG8(0x3d14), 0xcd },
++      { CCI_REG8(0x3d15), 0xcd }, { CCI_REG8(0x3d16), 0xcd },
++      { CCI_REG8(0x3d17), 0xcd }, { CCI_REG8(0x3d18), 0xcd },
++      { CCI_REG8(0x3d19), 0xcd }, { CCI_REG8(0x3d1a), 0xcd },
++      { CCI_REG8(0x3d1b), 0xcd }, { CCI_REG8(0x3d1c), 0xcd },
++      { CCI_REG8(0x3d1d), 0xcd }, { CCI_REG8(0x3d1e), 0xcd },
++      { CCI_REG8(0x3d1f), 0xcd }, { CCI_REG8(0x3d20), 0xcd },
++      { CCI_REG8(0x3d21), 0xcd }, { CCI_REG8(0x3d22), 0xcd },
++      { CCI_REG8(0x3d23), 0xcd }, { CCI_REG8(0x3d24), 0xcd },
++      { CCI_REG8(0x3d25), 0xcd }, { CCI_REG8(0x3d26), 0xcd },
++      { CCI_REG8(0x3d27), 0xcd }, { CCI_REG8(0x3d28), 0xcd },
++      { CCI_REG8(0x3d29), 0xcd }, { CCI_REG8(0x3d2a), 0xcd },
++      { CCI_REG8(0x3d2b), 0xcd }, { CCI_REG8(0x3d2c), 0xcd },
++      { CCI_REG8(0x3d2d), 0xcd }, { CCI_REG8(0x3d2e), 0xcd },
++      { CCI_REG8(0x3d2f), 0xcd }, { CCI_REG8(0x3d30), 0xcd },
++      { CCI_REG8(0x3d31), 0xcd }, { CCI_REG8(0x3d32), 0xcd },
++      { CCI_REG8(0x3d33), 0xcd }, { CCI_REG8(0x3d34), 0xcd },
++      { CCI_REG8(0x3d35), 0xcd }, { CCI_REG8(0x3d36), 0xcd },
++      { CCI_REG8(0x3d37), 0xcd }, { CCI_REG8(0x3d38), 0xcd },
++      { CCI_REG8(0x3d39), 0xcd }, { CCI_REG8(0x3d3a), 0xcd },
++      { CCI_REG8(0x3d3b), 0xcd }, { CCI_REG8(0x3d3c), 0xcd },
++      { CCI_REG8(0x3d3d), 0xcd }, { CCI_REG8(0x3d3e), 0xcd },
++      { CCI_REG8(0x3d3f), 0xcd }, { CCI_REG8(0x3d40), 0xcd },
++      { CCI_REG8(0x3d41), 0xcd }, { CCI_REG8(0x3d42), 0xcd },
++      { CCI_REG8(0x3d43), 0xcd }, { CCI_REG8(0x3d44), 0xcd },
++      { CCI_REG8(0x3d45), 0xcd }, { CCI_REG8(0x3d46), 0xcd },
++      { CCI_REG8(0x3d47), 0xcd }, { CCI_REG8(0x3d48), 0xcd },
++      { CCI_REG8(0x3d49), 0xcd }, { CCI_REG8(0x3d4a), 0xcd },
++      { CCI_REG8(0x3d4b), 0xcd }, { CCI_REG8(0x3d4c), 0xcd },
++      { CCI_REG8(0x3d4d), 0xcd }, { CCI_REG8(0x3d4e), 0xcd },
++      { CCI_REG8(0x3d4f), 0xcd }, { CCI_REG8(0x3d50), 0xcd },
++      { CCI_REG8(0x3d51), 0xcd }, { CCI_REG8(0x3d52), 0xcd },
++      { CCI_REG8(0x3d53), 0xcd }, { CCI_REG8(0x3d54), 0xcd },
++      { CCI_REG8(0x3d55), 0xcd }, { CCI_REG8(0x3d56), 0xcd },
++      { CCI_REG8(0x3d57), 0xcd }, { CCI_REG8(0x3d58), 0xcd },
++      { CCI_REG8(0x3d59), 0xcd }, { CCI_REG8(0x3d5a), 0xcd },
++      { CCI_REG8(0x3d5b), 0xcd }, { CCI_REG8(0x3d5c), 0xcd },
++      { CCI_REG8(0x3d5d), 0xcd }, { CCI_REG8(0x3d5e), 0xcd },
++      { CCI_REG8(0x3d5f), 0xcd }, { CCI_REG8(0x3d60), 0xcd },
++      { CCI_REG8(0x3d61), 0xcd }, { CCI_REG8(0x3d62), 0xcd },
++      { CCI_REG8(0x3d63), 0xcd }, { CCI_REG8(0x3d64), 0xcd },
++      { CCI_REG8(0x3d65), 0x40 }, { CCI_REG8(0x3d66), 0x40 },
++      { CCI_REG8(0x3d67), 0x40 }, { CCI_REG8(0x3d68), 0x40 },
++      { CCI_REG8(0x3d69), 0x40 }, { CCI_REG8(0x3d6a), 0x40 },
++      { CCI_REG8(0x3d6b), 0x40 }, { CCI_REG8(0x3d6c), 0x40 },
++      { CCI_REG8(0x3d6d), 0x40 }, { CCI_REG8(0x3d6e), 0x40 },
++      { CCI_REG8(0x3d6f), 0x40 }, { CCI_REG8(0x3d70), 0x40 },
++      { CCI_REG8(0x3d71), 0x40 }, { CCI_REG8(0x3d72), 0x40 },
++      { CCI_REG8(0x3d73), 0x40 }, { CCI_REG8(0x3d74), 0x40 },
++      { CCI_REG8(0x3d75), 0x40 }, { CCI_REG8(0x3d76), 0x40 },
++      { CCI_REG8(0x3d77), 0x40 }, { CCI_REG8(0x3d78), 0x40 },
++      { CCI_REG8(0x3d79), 0x40 }, { CCI_REG8(0x3d7a), 0x40 },
++      { CCI_REG8(0x3d7b), 0x40 }, { CCI_REG8(0x3d7c), 0x40 },
++      { CCI_REG8(0x3d7d), 0x40 }, { CCI_REG8(0x3d7e), 0x40 },
++      { CCI_REG8(0x3d7f), 0x40 }, { CCI_REG8(0x3d80), 0x40 },
++      { CCI_REG8(0x3d81), 0x40 }, { CCI_REG8(0x3d82), 0x40 },
++      { CCI_REG8(0x3d83), 0x40 }, { CCI_REG8(0x3d84), 0x40 },
++      { CCI_REG8(0x3d85), 0x40 }, { CCI_REG8(0x3d86), 0x40 },
++      { CCI_REG8(0x3d87), 0x40 }, { CCI_REG8(0x3d88), 0x40 },
++      { CCI_REG8(0x3d89), 0x40 }, { CCI_REG8(0x3d8a), 0x40 },
++      { CCI_REG8(0x3d8b), 0x40 }, { CCI_REG8(0x3d8c), 0x40 },
++      { CCI_REG8(0x3d8d), 0x40 }, { CCI_REG8(0x3d8e), 0x40 },
++      { CCI_REG8(0x3d8f), 0x40 }, { CCI_REG8(0x3d90), 0x40 },
++      { CCI_REG8(0x3d91), 0x40 }, { CCI_REG8(0x3d92), 0x40 },
++      { CCI_REG8(0x3d93), 0x40 }, { CCI_REG8(0x3d94), 0x40 },
++      { CCI_REG8(0x3d95), 0x40 }, { CCI_REG8(0x3d96), 0x40 },
++      { CCI_REG8(0x3d97), 0x40 }, { CCI_REG8(0x3d98), 0x40 },
++      { CCI_REG8(0x3d99), 0x40 }, { CCI_REG8(0x3d9a), 0x40 },
++      { CCI_REG8(0x3d9b), 0x40 }, { CCI_REG8(0x3d9c), 0x40 },
++      { CCI_REG8(0x3d9d), 0x40 }, { CCI_REG8(0x3d9e), 0x40 },
++      { CCI_REG8(0x3d9f), 0x40 }, { CCI_REG8(0x3da0), 0x40 },
++      { CCI_REG8(0x3da1), 0x40 }, { CCI_REG8(0x3da2), 0x40 },
++      { CCI_REG8(0x3da3), 0x40 }, { CCI_REG8(0x3da4), 0x40 },
++      { CCI_REG8(0x3da5), 0x40 }, { CCI_REG8(0x3da6), 0x40 },
++      { CCI_REG8(0x3da7), 0x40 }, { CCI_REG8(0x3da8), 0x40 },
++      { CCI_REG8(0x3da9), 0x40 }, { CCI_REG8(0x3daa), 0x40 },
++      { CCI_REG8(0x3dab), 0x40 }, { CCI_REG8(0x3dac), 0x40 },
++      { CCI_REG8(0x3dad), 0x40 }, { CCI_REG8(0x3dae), 0x40 },
++      { CCI_REG8(0x3daf), 0x40 }, { CCI_REG8(0x3db0), 0x40 },
++      { CCI_REG8(0x3db1), 0x40 }, { CCI_REG8(0x3db2), 0x40 },
++      { CCI_REG8(0x3db3), 0x40 }, { CCI_REG8(0x3db4), 0x40 },
++      { CCI_REG8(0x3db5), 0x40 }, { CCI_REG8(0x3db6), 0x40 },
++      { CCI_REG8(0x3db7), 0x40 }, { CCI_REG8(0x3db8), 0x40 },
++      { CCI_REG8(0x3db9), 0x40 }, { CCI_REG8(0x3dba), 0x40 },
++      { CCI_REG8(0x3dbb), 0x40 }, { CCI_REG8(0x3dbc), 0x40 },
++      { CCI_REG8(0x3dbd), 0x40 }, { CCI_REG8(0x3dbe), 0x40 },
++      { CCI_REG8(0x3dbf), 0xcd }, { CCI_REG8(0x3dc0), 0xcd },
++      { CCI_REG8(0x3dc1), 0xcd }, { CCI_REG8(0x3dc2), 0xcd },
++      { CCI_REG8(0x3dc3), 0xcd }, { CCI_REG8(0x3dc4), 0xcd },
++      { CCI_REG8(0x3dc5), 0xcd }, { CCI_REG8(0x3dc6), 0xcd },
++      { CCI_REG8(0x3dc7), 0xcd }, { CCI_REG8(0x3dc8), 0xcd },
++      { CCI_REG8(0x3dc9), 0xcd }, { CCI_REG8(0x3dca), 0xcd },
++      { CCI_REG8(0x3dcb), 0xcd }, { CCI_REG8(0x3dcc), 0xcd },
++      { CCI_REG8(0x3dcd), 0xcd }, { CCI_REG8(0x3dce), 0xcd },
++      { CCI_REG8(0x3dcf), 0xcd }, { CCI_REG8(0x3dd0), 0xcd },
++      { CCI_REG8(0x3dd1), 0xcd }, { CCI_REG8(0x3dd2), 0xcd },
++      { CCI_REG8(0x3dd3), 0xcd }, { CCI_REG8(0x3dd4), 0xcd },
++      { CCI_REG8(0x3dd5), 0xcd }, { CCI_REG8(0x3dd6), 0xcd },
++      { CCI_REG8(0x3dd7), 0xcd }, { CCI_REG8(0x3dd8), 0xcd },
++      { CCI_REG8(0x3dd9), 0xcd }, { CCI_REG8(0x3dda), 0xcd },
++      { CCI_REG8(0x3ddb), 0xcd }, { CCI_REG8(0x3ddc), 0xcd },
++      { CCI_REG8(0x3ddd), 0xcd }, { CCI_REG8(0x3dde), 0xcd },
++      { CCI_REG8(0x3ddf), 0xcd }, { CCI_REG8(0x3de0), 0xcd },
++      { CCI_REG8(0x3de1), 0xcd }, { CCI_REG8(0x3de2), 0xcd },
++      { CCI_REG8(0x3de3), 0xcd }, { CCI_REG8(0x3de4), 0xcd },
++      { CCI_REG8(0x3de5), 0xcd }, { CCI_REG8(0x3de6), 0xcd },
++      { CCI_REG8(0x3de7), 0xcd }, { CCI_REG8(0x3de8), 0xcd },
++      { CCI_REG8(0x3de9), 0xcd }, { CCI_REG8(0x3dea), 0xcd },
++      { CCI_REG8(0x3deb), 0xcd }, { CCI_REG8(0x3dec), 0xcd },
++      { CCI_REG8(0x3ded), 0xcd }, { CCI_REG8(0x3dee), 0xcd },
++      { CCI_REG8(0x3def), 0xcd }, { CCI_REG8(0x3df0), 0xcd },
++      { CCI_REG8(0x3df1), 0xcd }, { CCI_REG8(0x3df2), 0xcd },
++      { CCI_REG8(0x3df3), 0xcd }, { CCI_REG8(0x3df4), 0xcd },
++      { CCI_REG8(0x3df5), 0xcd }, { CCI_REG8(0x3df6), 0xcd },
++      { CCI_REG8(0x3df7), 0xcd }, { CCI_REG8(0x3df8), 0xcd },
++      { CCI_REG8(0x3df9), 0xcd }, { CCI_REG8(0x3dfa), 0xcd },
++      { CCI_REG8(0x3dfb), 0xcd }, { CCI_REG8(0x3dfc), 0xcd },
++      { CCI_REG8(0x3dfd), 0xcd }, { CCI_REG8(0x3dfe), 0xcd },
++      { CCI_REG8(0x3dff), 0xcd }, { CCI_REG8(0x3e00), 0xcd },
++      { CCI_REG8(0x3e01), 0xcd }, { CCI_REG8(0x3e02), 0xcd },
++      { CCI_REG8(0x3e03), 0xcd }, { CCI_REG8(0x3e04), 0xcd },
++      { CCI_REG8(0x3e05), 0xcd }, { CCI_REG8(0x3e06), 0xcd },
++      { CCI_REG8(0x3e07), 0xcd }, { CCI_REG8(0x3e08), 0xcd },
++      { CCI_REG8(0x3e09), 0xcd }, { CCI_REG8(0x3e0a), 0xcd },
++      { CCI_REG8(0x3e0b), 0xcd }, { CCI_REG8(0x3e0c), 0xcd },
++      { CCI_REG8(0x3e0d), 0xcd }, { CCI_REG8(0x3e0e), 0xcd },
++      { CCI_REG8(0x3e0f), 0xcd }, { CCI_REG8(0x3e10), 0xcd },
++      { CCI_REG8(0x3e11), 0xcd }, { CCI_REG8(0x3e12), 0xcd },
++      { CCI_REG8(0x3e13), 0xcd }, { CCI_REG8(0x3e14), 0xcd },
++      { CCI_REG8(0x3e15), 0xcd }, { CCI_REG8(0x3e16), 0xcd },
++      { CCI_REG8(0x3e17), 0xcd }, { CCI_REG8(0x3e18), 0xcd },
++      { CCI_REG8(0x3e19), 0xcd }, { CCI_REG8(0x3e1a), 0xcd },
++      { CCI_REG8(0x3e1b), 0xcd }, { CCI_REG8(0x3e1c), 0xcd },
++      { CCI_REG8(0x3e1d), 0xcd }, { CCI_REG8(0x3e1e), 0xcd },
++      { CCI_REG8(0x3e1f), 0xcd }, { CCI_REG8(0x3e20), 0xcd },
++      { CCI_REG8(0x3e21), 0xcd }, { CCI_REG8(0x3e22), 0xcd },
++      { CCI_REG8(0x3e23), 0xcd }, { CCI_REG8(0x3e24), 0xcd },
++      { CCI_REG8(0x3e25), 0xcd }, { CCI_REG8(0x3e26), 0xcd },
++      { CCI_REG8(0x3e27), 0xcd }, { CCI_REG8(0x3e28), 0xcd },
++      { CCI_REG8(0x3e29), 0xcd }, { CCI_REG8(0x3e2a), 0xcd },
++      { CCI_REG8(0x3e2b), 0xcd }, { CCI_REG8(0x3e2c), 0xcd },
++      { CCI_REG8(0x3e2d), 0xcd }, { CCI_REG8(0x3e2e), 0xcd },
++      { CCI_REG8(0x3e2f), 0xcd }, { CCI_REG8(0x3e30), 0xcd },
++      { CCI_REG8(0x3e31), 0xcd }, { CCI_REG8(0x3e32), 0xcd },
++      { CCI_REG8(0x3e33), 0xcd }, { CCI_REG8(0x3e34), 0xcd },
++      { CCI_REG8(0x3e35), 0xcd }, { CCI_REG8(0x3e36), 0xcd },
++      { CCI_REG8(0x3e37), 0xcd }, { CCI_REG8(0x3e38), 0xcd },
++      { CCI_REG8(0x3e39), 0xcd }, { CCI_REG8(0x3e3a), 0xcd },
++      { CCI_REG8(0x3e3b), 0xcd }, { CCI_REG8(0x3e3c), 0xcd },
++      { CCI_REG8(0x3e3d), 0xcd }, { CCI_REG8(0x3e3e), 0xcd },
++      { CCI_REG8(0x3e3f), 0xcd }, { CCI_REG8(0x3e40), 0xcd },
++      { CCI_REG8(0x3e41), 0xcd }, { CCI_REG8(0x3e42), 0xcd },
++      { CCI_REG8(0x3e43), 0xcd }, { CCI_REG8(0x3e44), 0xcd },
++      { CCI_REG8(0x3e45), 0xcd }, { CCI_REG8(0x3e46), 0xcd },
++      { CCI_REG8(0x3e47), 0xcd }, { CCI_REG8(0x3e48), 0xcd },
++      { CCI_REG8(0x3e49), 0xcd }, { CCI_REG8(0x3e4a), 0xcd },
++      { CCI_REG8(0x3e4b), 0xcd }, { CCI_REG8(0x3e4c), 0xcd },
++      { CCI_REG8(0x3e4d), 0xcd }, { CCI_REG8(0x3e4e), 0xcd },
++      { CCI_REG8(0x3e4f), 0xcd }, { CCI_REG8(0x3e50), 0xcd },
++      { CCI_REG8(0x3e51), 0xcd }, { CCI_REG8(0x3e52), 0xcd },
++      { CCI_REG8(0x3e53), 0xcd }, { CCI_REG8(0x3e54), 0xcd },
++      { CCI_REG8(0x3e55), 0xcd }, { CCI_REG8(0x3e56), 0xcd },
++      { CCI_REG8(0x3e57), 0xcd }, { CCI_REG8(0x3e58), 0xcd },
++      { CCI_REG8(0x3e59), 0xcd }, { CCI_REG8(0x3e5a), 0xcd },
++      { CCI_REG8(0x3e5b), 0xcd }, { CCI_REG8(0x3e5c), 0xcd },
++      { CCI_REG8(0x3e5d), 0xcd }, { CCI_REG8(0x3e5e), 0xcd },
++      { CCI_REG8(0x3e5f), 0xcd }, { CCI_REG8(0x3e60), 0xcd },
++      { CCI_REG8(0x3e61), 0xcd }, { CCI_REG8(0x3e62), 0xcd },
++      { CCI_REG8(0x3e63), 0xcd }, { CCI_REG8(0x3e64), 0xcd },
++      { CCI_REG8(0x3e65), 0xcd }, { CCI_REG8(0x3e66), 0xcd },
++      { CCI_REG8(0x3e67), 0xcd }, { CCI_REG8(0x3e68), 0xcd },
++      { CCI_REG8(0x3e69), 0xcd }, { CCI_REG8(0x3e6a), 0xcd },
++      { CCI_REG8(0x3e6b), 0xcd }, { CCI_REG8(0x3e6c), 0xcd },
++      { CCI_REG8(0x3e6d), 0xcd }, { CCI_REG8(0x3e6e), 0xcd },
++      { CCI_REG8(0x3e6f), 0xcd }, { CCI_REG8(0x3e70), 0xcd },
++      { CCI_REG8(0x3e71), 0xcd }, { CCI_REG8(0x3e72), 0xcd },
++      { CCI_REG8(0x3e73), 0xcd }, { CCI_REG8(0x3e74), 0xcd },
++      { CCI_REG8(0x3e75), 0xcd }, { CCI_REG8(0x3e76), 0xcd },
++      { CCI_REG8(0x3e77), 0xcd }, { CCI_REG8(0x3e78), 0xcd },
++      { CCI_REG8(0x3e79), 0xcd }, { CCI_REG8(0x3e7a), 0xcd },
++      { CCI_REG8(0x3e7b), 0xcd }, { CCI_REG8(0x3e7c), 0xcd },
++      { CCI_REG8(0x3e7d), 0xcd }, { CCI_REG8(0x3e7e), 0xcd },
++      { CCI_REG8(0x3e7f), 0xcd }, { CCI_REG8(0x3e80), 0xcd },
++      { CCI_REG8(0x3e81), 0xcd }, { CCI_REG8(0x3e82), 0xcd },
++      { CCI_REG8(0x3e83), 0xcd }, { CCI_REG8(0x3e84), 0xcd },
++      { CCI_REG8(0x3e85), 0xcd }, { CCI_REG8(0x3e86), 0xcd },
++      { CCI_REG8(0x3e87), 0xcd }, { CCI_REG8(0x3e88), 0xcd },
++      { CCI_REG8(0x3e89), 0xcd }, { CCI_REG8(0x3e8a), 0xcd },
++      { CCI_REG8(0x3e8b), 0xcd }, { CCI_REG8(0x3e8c), 0xcd },
++      { CCI_REG8(0x3e8d), 0xcd }, { CCI_REG8(0x3e8e), 0xcd },
++      { CCI_REG8(0x3e8f), 0xcd }, { CCI_REG8(0x3e90), 0xcd },
++      { CCI_REG8(0x3e91), 0xcd }, { CCI_REG8(0x3e92), 0xcd },
++      { CCI_REG8(0x3e93), 0xcd }, { CCI_REG8(0x3e94), 0xcd },
++      { CCI_REG8(0x3e95), 0xcd }, { CCI_REG8(0x3e96), 0xcd },
++      { CCI_REG8(0x3e97), 0xcd }, { CCI_REG8(0x3e98), 0xcd },
++      { CCI_REG8(0x3e99), 0xcd }, { CCI_REG8(0x3e9a), 0xcd },
++      { CCI_REG8(0x3e9b), 0xcd }, { CCI_REG8(0x3e9c), 0xcd },
++      { CCI_REG8(0x3e9d), 0xcd }, { CCI_REG8(0x3e9e), 0xcd },
++      { CCI_REG8(0x3e9f), 0xcd }, { CCI_REG8(0xfff9), 0x06 },
++      { CCI_REG8(0xc03f), 0x01 }, { CCI_REG8(0xc03e), 0x08 },
++      { CCI_REG8(0xc02c), 0xff }, { CCI_REG8(0xc005), 0x06 },
++      { CCI_REG8(0xc006), 0x30 }, { CCI_REG8(0xc007), 0xc0 },
++      { CCI_REG8(0xc027), 0x01 }, { CCI_REG8(0x30c0), 0x05 },
++      { CCI_REG8(0x30c1), 0x9f }, { CCI_REG8(0x30c2), 0x06 },
++      { CCI_REG8(0x30c3), 0x5f }, { CCI_REG8(0x30c4), 0x80 },
++      { CCI_REG8(0x30c5), 0x08 }, { CCI_REG8(0x30c6), 0x39 },
++      { CCI_REG8(0x30c7), 0x00 }, { CCI_REG8(0xc046), 0x20 },
++      { CCI_REG8(0xc043), 0x01 }, { CCI_REG8(0xc04b), 0x01 },
++      { CCI_REG8(0x0102), 0x01 }, { CCI_REG8(0x0100), 0x00 },
++      { CCI_REG8(0x0102), 0x00 }, { CCI_REG8(0x3015), 0xf0 },
++      { CCI_REG8(0x3018), 0xf0 }, { CCI_REG8(0x301c), 0xf0 },
++      { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 }
++};
++
++static const struct cci_reg_sequence ov64a40_9248x6944[] = {
++      { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++      { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a },
++      { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 },
++      { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 },
++      { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_8000x6000[] = {
++      { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++      { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a },
++      { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 },
++      { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 },
++      { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_4624_3472[] = {
++      { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++      { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++      { CCI_REG8(0x3712), 0x50 }, { CCI_REG8(0x3822), 0x00 },
++      { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x08 },
++      { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x02 },
++      { CCI_REG8(0x384d), 0xba }, { CCI_REG8(0x3852), 0x00 },
++      { CCI_REG8(0x3856), 0x08 }, { CCI_REG8(0x3857), 0x08 },
++      { CCI_REG8(0x3858), 0x10 }, { CCI_REG8(0x3859), 0x10 },
++      { CCI_REG8(0x4016), 0x0f }, { CCI_REG8(0x4018), 0x03 },
++      { CCI_REG8(0x4504), 0x1e }, { CCI_REG8(0x4523), 0x41 },
++      { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x12 },
++      { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4915), 0x02 },
++      { CCI_REG8(0x4916), 0x1d }, { CCI_REG8(0x4a15), 0x02 },
++      { CCI_REG8(0x4a16), 0x1d }, { CCI_REG8(0x3703), 0x72 },
++      { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a60), 0x68 },
++      { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc },
++      { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3721), 0xc9 },
++      { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++      { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++      { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++      { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++      { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++      { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++      { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++      { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++      { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x481b), 0x35 },
++      { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x3400), 0x00 },
++      { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc },
++      { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 },
++      { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 },
++      { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 },
++      { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 },
++      { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x0305), 0x98 },
++      { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++      { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++      { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++      { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_3840x2160[] = {
++      { CCI_REG8(0x034a), 0x05 }, { CCI_REG8(0x034b), 0x05 },
++      { CCI_REG8(0x3504), 0x08 }, { CCI_REG8(0x360d), 0x82 },
++      { CCI_REG8(0x368a), 0x2e }, { CCI_REG8(0x3712), 0x50 },
++      { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3827), 0x40 },
++      { CCI_REG8(0x383d), 0x08 }, { CCI_REG8(0x383f), 0x00 },
++      { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0xba },
++      { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x08 },
++      { CCI_REG8(0x3857), 0x08 }, { CCI_REG8(0x3858), 0x10 },
++      { CCI_REG8(0x3859), 0x10 }, { CCI_REG8(0x4016), 0x0f },
++      { CCI_REG8(0x4018), 0x03 }, { CCI_REG8(0x4504), 0x1e },
++      { CCI_REG8(0x4523), 0x41 }, { CCI_REG8(0x45c0), 0x01 },
++      { CCI_REG8(0x4641), 0x12 }, { CCI_REG8(0x4643), 0x0c },
++      { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++      { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++      { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3709), 0xe6 },
++      { CCI_REG8(0x3a60), 0x68 }, { CCI_REG8(0x3a6f), 0x68 },
++      { CCI_REG8(0x3a5e), 0xdc }, { CCI_REG8(0x3a6d), 0xdc },
++      { CCI_REG8(0x3721), 0xc9 }, { CCI_REG8(0x5250), 0x06 },
++      { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x65 },
++      { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x82 },
++      { CCI_REG8(0x5280), 0x24 }, { CCI_REG8(0x5281), 0x40 },
++      { CCI_REG8(0x5282), 0x1b }, { CCI_REG8(0x5283), 0x40 },
++      { CCI_REG8(0x5284), 0x24 }, { CCI_REG8(0x5285), 0x40 },
++      { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 },
++      { CCI_REG8(0x5200), 0x24 }, { CCI_REG8(0x5201), 0x40 },
++      { CCI_REG8(0x5202), 0x1b }, { CCI_REG8(0x5203), 0x40 },
++      { CCI_REG8(0x481b), 0x35 }, { CCI_REG8(0x4862), 0x25 },
++      { CCI_REG8(0x3400), 0x00 }, { CCI_REG8(0x3421), 0x23 },
++      { CCI_REG8(0x3422), 0xfc }, { CCI_REG8(0x3423), 0x07 },
++      { CCI_REG8(0x3424), 0x01 }, { CCI_REG8(0x3425), 0x04 },
++      { CCI_REG8(0x3426), 0x50 }, { CCI_REG8(0x3427), 0x55 },
++      { CCI_REG8(0x3428), 0x15 }, { CCI_REG8(0x3429), 0x00 },
++      { CCI_REG8(0x3025), 0x03 }, { CCI_REG8(0x5250), 0x06 },
++      { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++      { CCI_REG8(0x0345), 0x90 }, { CCI_REG8(0x0307), 0x01 },
++      { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++      { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++      { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 },
++      { CCI_REG8(0x5000), 0x01 }
++};
++
++static const struct cci_reg_sequence ov64a40_2312_1736[] = {
++      { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++      { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++      { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 },
++      { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 },
++      { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 },
++      { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 },
++      { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 },
++      { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 },
++      { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 },
++      { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++      { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 },
++      { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b },
++      { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++      { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++      { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 },
++      { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 },
++      { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a },
++      { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 },
++      { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 },
++      { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 },
++      { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++      { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++      { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++      { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++      { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++      { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++      { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++      { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++      { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 },
++      { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 },
++      { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 },
++      { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++      { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 },
++      { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++      { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 },
++      { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 },
++      { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 },
++      { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 },
++      { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 },
++      { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e },
++      { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 },
++      { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++      { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 },
++      { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 },
++      { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++      { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++      { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++      { CCI_REG8(0x480C), 0x92 }
++};
++
++static const struct cci_reg_sequence ov64a40_1920x1080[] = {
++      { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++      { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++      { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 },
++      { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 },
++      { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 },
++      { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 },
++      { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 },
++      { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 },
++      { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 },
++      { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++      { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 },
++      { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b },
++      { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++      { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++      { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 },
++      { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 },
++      { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a },
++      { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 },
++      { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 },
++      { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 },
++      { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++      { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++      { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++      { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++      { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++      { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++      { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++      { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++      { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 },
++      { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 },
++      { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 },
++      { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++      { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 },
++      { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++      { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 },
++      { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 },
++      { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 },
++      { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 },
++      { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 },
++      { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e },
++      { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 },
++      { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++      { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 },
++      { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 },
++      { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++      { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++      { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++      { CCI_REG8(0x480C), 0x92 }
++};
++
++/* 456MHz MIPI link frequency with 24MHz input clock. */
++static const struct cci_reg_sequence ov64a40_pll_config[] = {
++      { OV64A40_PLL1_PRE_DIV0, 0x88 },
++      { OV64A40_PLL1_PRE_DIV, 0x02 },
++      { OV64A40_PLL1_MULTIPLIER, 0x0098 },
++      { OV64A40_PLL1_M_DIV, 0x01 },
++      { OV64A40_PLL2_SEL_BAK_SA1, 0x00 },
++      { OV64A40_PLL2_PRE_DIV, 0x12 },
++      { OV64A40_PLL2_MULTIPLIER, 0x0190 },
++      { OV64A40_PLL2_PRE_DIV0, 0xd7 },
++      { OV64A40_PLL2_DIVSP, 0x00 },
++      { OV64A40_PLL2_DIVDAC, 0x00 },
++      { OV64A40_PLL2_DACPREDIV, 0x00 }
++};
++
++struct ov64a40_reglist {
++      unsigned int num_regs;
++      const struct cci_reg_sequence *regvals;
++};
++
++struct ov64a40_subsampling {
++      unsigned int x_odd_inc;
++      unsigned int x_even_inc;
++      unsigned int y_odd_inc;
++      unsigned int y_even_inc;
++      bool vbin;
++      bool hbin;
++};
++
++static struct ov64a40_mode {
++      unsigned int width;
++      unsigned int height;
++      struct ov64a40_timings {
++              unsigned int vts;
++              unsigned int ppl;
++      } timings_default[OV64A40_NUM_LINK_FREQ];
++      const struct ov64a40_reglist reglist;
++      struct v4l2_rect analogue_crop;
++      struct v4l2_rect digital_crop;
++      struct ov64a40_subsampling subsampling;
++} ov64a40_modes[] = {
++      /* Full resolution */
++      {
++              .width = 9248,
++              .height = 6944,
++              .timings_default = {
++                      /* 2.6 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 7072,
++                              .ppl = 4072,
++                      },
++                      /* 2 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 7072,
++                              .ppl = 5248,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_9248x6944),
++                      .regvals = ov64a40_9248x6944,
++              },
++              .analogue_crop = {
++                      .left = 0,
++                      .top = 0,
++                      .width = 9279,
++                      .height = 6975,
++              },
++              .digital_crop = {
++                      .left = 17,
++                      .top = 16,
++                      .width = 9248,
++                      .height = 6944,
++              },
++              .subsampling = {
++                      .x_odd_inc = 1,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 1,
++                      .y_even_inc = 1,
++                      .vbin = false,
++                      .hbin = false,
++              },
++      },
++      /* Analogue crop + digital crop */
++      {
++              .width = 8000,
++              .height = 6000,
++              .timings_default = {
++                      /* 3.0 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 6400,
++                              .ppl = 3848,
++                      },
++                      /* 2.5 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 6304,
++                              .ppl = 4736,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_8000x6000),
++                      .regvals = ov64a40_8000x6000,
++              },
++              .analogue_crop = {
++                      .left = 624,
++                      .top = 472,
++                      .width = 8047,
++                      .height = 6031,
++              },
++              .digital_crop = {
++                      .left = 17,
++                      .top = 16,
++                      .width = 8000,
++                      .height = 6000,
++              },
++              .subsampling = {
++                      .x_odd_inc = 1,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 1,
++                      .y_even_inc = 1,
++                      .vbin = false,
++                      .hbin = false,
++              },
++      },
++      /* 2x2 downscaled */
++      {
++              .width = 4624,
++              .height = 3472,
++              .timings_default = {
++                      /* 10 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 3533,
++                              .ppl = 2112,
++                      },
++                      /* 7 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 3939,
++                              .ppl = 2720,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_4624_3472),
++                      .regvals = ov64a40_4624_3472,
++              },
++              .analogue_crop = {
++                      .left = 0,
++                      .top = 0,
++                      .width = 9279,
++                      .height = 6975,
++              },
++              .digital_crop = {
++                      .left = 9,
++                      .top = 8,
++                      .width = 4624,
++                      .height = 3472,
++              },
++              .subsampling = {
++                      .x_odd_inc = 3,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 1,
++                      .y_even_inc = 1,
++                      .vbin = true,
++                      .hbin = false,
++              },
++      },
++      /* Analogue crop + 2x2 downscale + digital crop */
++      {
++              .width = 3840,
++              .height = 2160,
++              .timings_default = {
++                      /* 20 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 2218,
++                              .ppl = 1690,
++                      },
++                      /* 15 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 2270,
++                              .ppl = 2202,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_3840x2160),
++                      .regvals = ov64a40_3840x2160,
++              },
++              .analogue_crop = {
++                      .left = 784,
++                      .top = 1312,
++                      .width = 7711,
++                      .height = 4351,
++              },
++              .digital_crop = {
++                      .left = 9,
++                      .top = 8,
++                      .width = 3840,
++                      .height = 2160,
++              },
++              .subsampling = {
++                      .x_odd_inc = 3,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 1,
++                      .y_even_inc = 1,
++                      .vbin = true,
++                      .hbin = false,
++              },
++      },
++      /* 4x4 downscaled */
++      {
++              .width = 2312,
++              .height = 1736,
++              .timings_default = {
++                      /* 30 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 1998,
++                              .ppl = 1248,
++                      },
++                      /* 25 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 1994,
++                              .ppl = 1504,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_2312_1736),
++                      .regvals = ov64a40_2312_1736,
++              },
++              .analogue_crop = {
++                      .left = 0,
++                      .top = 0,
++                      .width = 9279,
++                      .height = 6975,
++              },
++              .digital_crop = {
++                      .left = 5,
++                      .top = 4,
++                      .width = 2312,
++                      .height = 1736,
++              },
++              .subsampling = {
++                      .x_odd_inc = 3,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 3,
++                      .y_even_inc = 1,
++                      .vbin = true,
++                      .hbin = true,
++              },
++      },
++      /* Analogue crop + 4x4 downscale + digital crop */
++      {
++              .width = 1920,
++              .height = 1080,
++              .timings_default = {
++                      /* 60 FPS */
++                      [OV64A40_LINK_FREQ_456M_ID] = {
++                              .vts = 1397,
++                              .ppl = 880,
++                      },
++                      /* 45 FPS */
++                      [OV64A40_LINK_FREQ_360M_ID] = {
++                              .vts = 1216,
++                              .ppl = 1360,
++                      },
++              },
++              .reglist = {
++                      .num_regs = ARRAY_SIZE(ov64a40_1920x1080),
++                      .regvals = ov64a40_1920x1080,
++              },
++              .analogue_crop = {
++                      .left = 784,
++                      .top = 1312,
++                      .width = 7711,
++                      .height = 4351,
++              },
++              .digital_crop = {
++                      .left = 7,
++                      .top = 6,
++                      .width = 1920,
++                      .height = 1080,
++              },
++              .subsampling = {
++                      .x_odd_inc = 3,
++                      .x_even_inc = 1,
++                      .y_odd_inc = 3,
++                      .y_even_inc = 1,
++                      .vbin = true,
++                      .hbin = true,
++              },
++      },
++};
++
++struct ov64a40 {
++      struct device *dev;
++
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++
++      struct regmap *cci;
++
++      struct ov64a40_mode *mode;
++
++      struct clk *xclk;
++
++      struct gpio_desc *reset_gpio;
++      struct regulator_bulk_data supplies[ARRAY_SIZE(ov64a40_supply_names)];
++
++      s64 *link_frequencies;
++      unsigned int num_link_frequencies;
++
++      struct v4l2_ctrl_handler ctrl_handler;
++      struct v4l2_ctrl *exposure;
++      struct v4l2_ctrl *link_freq;
++      struct v4l2_ctrl *vblank;
++      struct v4l2_ctrl *hblank;
++      struct v4l2_ctrl *vflip;
++      struct v4l2_ctrl *hflip;
++};
++
++static inline struct ov64a40 *sd_to_ov64a40(struct v4l2_subdev *sd)
++{
++      return container_of(sd, struct ov64a40, sd);
++}
++
++static const struct ov64a40_timings *
++ov64a40_get_timings(struct ov64a40 *ov64a40, unsigned int link_freq_index)
++{
++      s64 link_freq = ov64a40->link_frequencies[link_freq_index];
++      unsigned int timings_index = link_freq == OV64A40_LINK_FREQ_360M
++                                 ? OV64A40_LINK_FREQ_360M_ID
++                                 : OV64A40_LINK_FREQ_456M_ID;
++
++      return &ov64a40->mode->timings_default[timings_index];
++}
++
++static int ov64a40_program_geometry(struct ov64a40 *ov64a40)
++{
++      struct ov64a40_mode *mode = ov64a40->mode;
++      struct v4l2_rect *anacrop = &mode->analogue_crop;
++      struct v4l2_rect *digicrop = &mode->digital_crop;
++      const struct ov64a40_timings *timings;
++      int ret = 0;
++
++      /* Analogue crop. */
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL0,
++                anacrop->left, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL2,
++                anacrop->top, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL4,
++                anacrop->width + anacrop->left, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL6,
++                anacrop->height + anacrop->top, &ret);
++
++      /* ISP windowing. */
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL10,
++                digicrop->left, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL12,
++                digicrop->top, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL8,
++                digicrop->width, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLA,
++                digicrop->height, &ret);
++
++      /* Total timings. */
++      timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLC, timings->ppl, &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLE, timings->vts, &ret);
++
++      return ret;
++}
++
++static int ov64a40_program_subsampling(struct ov64a40 *ov64a40)
++{
++      struct ov64a40_subsampling *subsampling = &ov64a40->mode->subsampling;
++      int ret = 0;
++
++      /* Skipping configuration */
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL14,
++                OV64A40_SKIPPING_CONFIG(subsampling->x_odd_inc,
++                                        subsampling->x_even_inc), &ret);
++      cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL15,
++                OV64A40_SKIPPING_CONFIG(subsampling->y_odd_inc,
++                                        subsampling->y_even_inc), &ret);
++
++      /* Binning configuration */
++      cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20,
++                      OV64A40_TIMING_CTRL_20_VBIN,
++                      subsampling->vbin ? OV64A40_TIMING_CTRL_20_VBIN : 0,
++                      &ret);
++      cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21,
++                      OV64A40_TIMING_CTRL_21_HBIN_CONF,
++                      subsampling->hbin ?
++                      OV64A40_TIMING_CTRL_21_HBIN_CONF : 0, &ret);
++
++      return ret;
++}
++
++static int ov64a40_start_streaming(struct ov64a40 *ov64a40,
++                                 struct v4l2_subdev_state *state)
++{
++      const struct ov64a40_reglist *reglist = &ov64a40->mode->reglist;
++      const struct ov64a40_timings *timings;
++      unsigned long delay;
++      int ret;
++
++      ret = pm_runtime_resume_and_get(ov64a40->dev);
++      if (ret < 0)
++              return ret;
++
++      ret = cci_multi_reg_write(ov64a40->cci, ov64a40_init,
++                                ARRAY_SIZE(ov64a40_init), NULL);
++      if (ret)
++              goto error_power_off;
++
++      ret = cci_multi_reg_write(ov64a40->cci, reglist->regvals,
++                                reglist->num_regs, NULL);
++      if (ret)
++              goto error_power_off;
++
++      ret = ov64a40_program_geometry(ov64a40);
++      if (ret)
++              goto error_power_off;
++
++      ret = ov64a40_program_subsampling(ov64a40);
++      if (ret)
++              goto error_power_off;
++
++      ret =  __v4l2_ctrl_handler_setup(&ov64a40->ctrl_handler);
++      if (ret)
++              goto error_power_off;
++
++      ret = cci_write(ov64a40->cci, OV64A40_REG_SMIA,
++                      OV64A40_REG_SMIA_STREAMING, NULL);
++      if (ret)
++              goto error_power_off;
++
++      /* Link frequency and flips cannot change while streaming. */
++      __v4l2_ctrl_grab(ov64a40->link_freq, true);
++      __v4l2_ctrl_grab(ov64a40->vflip, true);
++      __v4l2_ctrl_grab(ov64a40->hflip, true);
++
++      /* delay: max(4096 xclk pulses, 150usec) + exposure time */
++      timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val);
++      delay = DIV_ROUND_UP(4096, OV64A40_XCLK_FREQ / 1000 / 1000);
++      delay = max(delay, 150ul);
++
++      /* The sensor has an internal x4 multiplier on the line length. */
++      delay += DIV_ROUND_UP(timings->ppl * 4 * ov64a40->exposure->cur.val,
++                            OV64A40_PIXEL_RATE / 1000 / 1000);
++      fsleep(delay);
++
++      return 0;
++
++error_power_off:
++      pm_runtime_mark_last_busy(ov64a40->dev);
++      pm_runtime_put_autosuspend(ov64a40->dev);
++
++      return ret;
++}
++
++static int ov64a40_stop_streaming(struct ov64a40 *ov64a40,
++                                struct v4l2_subdev_state *state)
++{
++      cci_update_bits(ov64a40->cci, OV64A40_REG_SMIA, BIT(0), 0, NULL);
++      pm_runtime_mark_last_busy(ov64a40->dev);
++      pm_runtime_put_autosuspend(ov64a40->dev);
++
++      __v4l2_ctrl_grab(ov64a40->link_freq, false);
++      __v4l2_ctrl_grab(ov64a40->vflip, false);
++      __v4l2_ctrl_grab(ov64a40->hflip, false);
++
++      return 0;
++}
++
++static int ov64a40_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++      struct v4l2_subdev_state *state;
++      int ret;
++
++      state = v4l2_subdev_lock_and_get_active_state(sd);
++      if (enable)
++              ret = ov64a40_start_streaming(ov64a40, state);
++      else
++              ret = ov64a40_stop_streaming(ov64a40, state);
++      v4l2_subdev_unlock_state(state);
++
++      return ret;
++}
++
++static const struct v4l2_subdev_video_ops ov64a40_video_ops = {
++      .s_stream = ov64a40_set_stream,
++};
++
++static u32 ov64a40_mbus_code(struct ov64a40 *ov64a40)
++{
++      unsigned int index = ov64a40->hflip->val << 1 | ov64a40->vflip->val;
++
++      return ov64a40_mbus_codes[index];
++}
++
++static void ov64a40_update_pad_fmt(struct ov64a40 *ov64a40,
++                                 struct ov64a40_mode *mode,
++                                 struct v4l2_mbus_framefmt *fmt)
++{
++      fmt->code = ov64a40_mbus_code(ov64a40);
++      fmt->width = mode->width;
++      fmt->height = mode->height;
++      fmt->field = V4L2_FIELD_NONE;
++      fmt->colorspace = V4L2_COLORSPACE_RAW;
++      fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++      fmt->xfer_func = V4L2_XFER_FUNC_NONE;
++      fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
++}
++
++static int ov64a40_init_cfg(struct v4l2_subdev *sd,
++                          struct v4l2_subdev_state *state)
++{
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++      struct v4l2_mbus_framefmt *format;
++      struct v4l2_rect *crop;
++
++      format = v4l2_subdev_get_pad_format(sd, state, 0);
++      ov64a40_update_pad_fmt(ov64a40, &ov64a40_modes[0], format);
++
++      crop = v4l2_subdev_get_pad_crop(sd, state, 0);
++      crop->top = OV64A40_PIXEL_ARRAY_TOP;
++      crop->left = OV64A40_PIXEL_ARRAY_LEFT;
++      crop->width = OV64A40_PIXEL_ARRAY_WIDTH;
++      crop->height = OV64A40_PIXEL_ARRAY_HEIGHT;
++
++      return 0;
++}
++
++static int ov64a40_enum_mbus_code(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_state *sd_state,
++                                struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++      if (code->index)
++              return -EINVAL;
++
++      code->code = ov64a40_mbus_code(ov64a40);
++
++      return 0;
++}
++
++static int ov64a40_enum_frame_size(struct v4l2_subdev *sd,
++                                 struct v4l2_subdev_state *sd_state,
++                                 struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++      struct ov64a40_mode *mode;
++      u32 code;
++
++      if (fse->index >= ARRAY_SIZE(ov64a40_modes))
++              return -EINVAL;
++
++      code = ov64a40_mbus_code(ov64a40);
++      if (fse->code != code)
++              return -EINVAL;
++
++      mode = &ov64a40_modes[fse->index];
++      fse->min_width = mode->width;
++      fse->max_width = mode->width;
++      fse->min_height = mode->height;
++      fse->max_height = mode->height;
++
++      return 0;
++}
++
++static int ov64a40_get_selection(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_state *sd_state,
++                               struct v4l2_subdev_selection *sel)
++{
++      switch (sel->target) {
++      case V4L2_SEL_TGT_CROP:
++              sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0);
++
++              return 0;
++
++      case V4L2_SEL_TGT_NATIVE_SIZE:
++              sel->r.top = 0;
++              sel->r.left = 0;
++              sel->r.width = OV64A40_NATIVE_WIDTH;
++              sel->r.height = OV64A40_NATIVE_HEIGHT;
++
++              return 0;
++
++      case V4L2_SEL_TGT_CROP_DEFAULT:
++      case V4L2_SEL_TGT_CROP_BOUNDS:
++              sel->r.top = OV64A40_PIXEL_ARRAY_TOP;
++              sel->r.left = OV64A40_PIXEL_ARRAY_LEFT;
++              sel->r.width = OV64A40_PIXEL_ARRAY_WIDTH;
++              sel->r.height = OV64A40_PIXEL_ARRAY_HEIGHT;
++
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++static int ov64a40_set_format(struct v4l2_subdev *sd,
++                            struct v4l2_subdev_state *sd_state,
++                            struct v4l2_subdev_format *fmt)
++{
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++      struct v4l2_mbus_framefmt *format;
++      struct ov64a40_mode *mode;
++
++      mode = v4l2_find_nearest_size(ov64a40_modes,
++                                    ARRAY_SIZE(ov64a40_modes),
++                                    width, height,
++                                    fmt->format.width, fmt->format.height);
++
++      ov64a40_update_pad_fmt(ov64a40, mode, &fmt->format);
++
++      format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
++      if (ov64a40->mode == mode && format->code == fmt->format.code)
++              return 0;
++
++      if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
++              const struct ov64a40_timings *timings;
++              int vblank_max, vblank_def;
++              int hblank_val;
++              int exp_max;
++
++              ov64a40->mode = mode;
++              *v4l2_subdev_get_pad_crop(sd, sd_state, 0) = mode->analogue_crop;
++
++              /* Update control limits according to the new mode. */
++              timings = ov64a40_get_timings(ov64a40,
++                                            ov64a40->link_freq->cur.val);
++              vblank_max = OV64A40_VTS_MAX - mode->height;
++              vblank_def = timings->vts - mode->height;
++              __v4l2_ctrl_modify_range(ov64a40->vblank, OV64A40_VBLANK_MIN,
++                                       vblank_max, 1, vblank_def);
++              __v4l2_ctrl_s_ctrl(ov64a40->vblank, vblank_def);
++
++              exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN;
++              __v4l2_ctrl_modify_range(ov64a40->exposure,
++                                       OV64A40_EXPOSURE_MIN, exp_max,
++                                       1, OV64A40_EXPOSURE_MIN);
++
++              hblank_val = timings->ppl * 4 - mode->width;
++              __v4l2_ctrl_modify_range(ov64a40->hblank,
++                                       hblank_val, hblank_val, 1, hblank_val);
++      }
++
++      *format = fmt->format;
++
++      return 0;
++}
++
++static const struct v4l2_subdev_pad_ops ov64a40_pad_ops = {
++      .init_cfg = ov64a40_init_cfg,
++      .enum_mbus_code = ov64a40_enum_mbus_code,
++      .enum_frame_size = ov64a40_enum_frame_size,
++      .get_fmt = v4l2_subdev_get_fmt,
++      .set_fmt = ov64a40_set_format,
++      .get_selection = ov64a40_get_selection,
++};
++
++static const struct v4l2_subdev_core_ops ov64a40_core_ops = {
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_ops ov64a40_subdev_ops = {
++      .core = &ov64a40_core_ops,
++      .video = &ov64a40_video_ops,
++      .pad = &ov64a40_pad_ops,
++};
++
++static int ov64a40_power_on(struct device *dev)
++{
++      struct v4l2_subdev *sd = dev_get_drvdata(dev);
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++      int ret;
++
++      ret = clk_prepare_enable(ov64a40->xclk);
++      if (ret)
++              return ret;
++
++      ret = regulator_bulk_enable(ARRAY_SIZE(ov64a40_supply_names),
++                                  ov64a40->supplies);
++      if (ret) {
++              clk_disable_unprepare(ov64a40->xclk);
++              dev_err(dev, "Failed to enable regulators: %d\n", ret);
++              return ret;
++      }
++
++      gpiod_set_value_cansleep(ov64a40->reset_gpio, 0);
++
++      fsleep(5000);
++
++      return 0;
++}
++
++static int ov64a40_power_off(struct device *dev)
++{
++      struct v4l2_subdev *sd = dev_get_drvdata(dev);
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++      gpiod_set_value_cansleep(ov64a40->reset_gpio, 1);
++      regulator_bulk_disable(ARRAY_SIZE(ov64a40_supply_names),
++                             ov64a40->supplies);
++      clk_disable_unprepare(ov64a40->xclk);
++
++      return 0;
++}
++
++static int ov64a40_link_freq_config(struct ov64a40 *ov64a40, int link_freq_id)
++{
++      s64 link_frequency;
++      int ret = 0;
++
++      /* Default 456MHz with 24MHz input clock. */
++      cci_multi_reg_write(ov64a40->cci, ov64a40_pll_config,
++                          ARRAY_SIZE(ov64a40_pll_config), &ret);
++
++      /* Decrease the PLL1 multiplier to obtain 360MHz mipi link frequency. */
++      link_frequency = ov64a40->link_frequencies[link_freq_id];
++      if (link_frequency == OV64A40_LINK_FREQ_360M)
++              cci_write(ov64a40->cci, OV64A40_PLL1_MULTIPLIER, 0x0078, &ret);
++
++      return ret;
++}
++
++static int ov64a40_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct ov64a40 *ov64a40 = container_of(ctrl->handler, struct ov64a40,
++                                             ctrl_handler);
++      int pm_status;
++      int ret = 0;
++
++      if (ctrl->id == V4L2_CID_VBLANK) {
++              int exp_max = ov64a40->mode->height + ctrl->val
++                          - OV64A40_EXPOSURE_MARGIN;
++              int exp_val = min(ov64a40->exposure->cur.val, exp_max);
++
++              __v4l2_ctrl_modify_range(ov64a40->exposure,
++                                       ov64a40->exposure->minimum,
++                                       exp_max, 1, exp_val);
++      }
++
++      pm_status = pm_runtime_get_if_active(ov64a40->dev, true);
++      if (!pm_status)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_EXPOSURE:
++              ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_EXPO,
++                              ctrl->val, NULL);
++              break;
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_GAIN,
++                              ctrl->val << 1, NULL);
++              break;
++      case V4L2_CID_VBLANK: {
++              int vts = ctrl->val + ov64a40->mode->height;
++
++              cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_LOW, vts, &ret);
++              cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_MID,
++                        (vts >> 8), &ret);
++              cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_HIGH,
++                        (vts >> 16), &ret);
++              break;
++      }
++      case V4L2_CID_VFLIP:
++              ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20,
++                                    OV64A40_TIMING_CTRL_20_VFLIP,
++                                    ctrl->val << 2,
++                                    NULL);
++              break;
++      case V4L2_CID_HFLIP:
++              ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21,
++                                    OV64A40_TIMING_CTRL_21_HFLIP,
++                                    ctrl->val ? 0
++                                              : OV64A40_TIMING_CTRL_21_HFLIP,
++                                    NULL);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = cci_write(ov64a40->cci, OV64A40_REG_TEST_PATTERN,
++                              ov64a40_test_pattern_val[ctrl->val], NULL);
++              break;
++      case V4L2_CID_LINK_FREQ:
++              ret = ov64a40_link_freq_config(ov64a40, ctrl->val);
++              break;
++      default:
++              dev_err(ov64a40->dev, "Unhandled control: %#x\n", ctrl->id);
++              ret = -EINVAL;
++              break;
++      }
++
++      if (pm_status > 0) {
++              pm_runtime_mark_last_busy(ov64a40->dev);
++              pm_runtime_put_autosuspend(ov64a40->dev);
++      }
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops ov64a40_ctrl_ops = {
++      .s_ctrl = ov64a40_set_ctrl,
++};
++
++static int ov64a40_init_controls(struct ov64a40 *ov64a40)
++{
++      int exp_max, hblank_val, vblank_max, vblank_def;
++      struct v4l2_ctrl_handler *hdlr = &ov64a40->ctrl_handler;
++      struct v4l2_fwnode_device_properties props;
++      const struct ov64a40_timings *timings;
++      int ret;
++
++      ret = v4l2_ctrl_handler_init(hdlr, 11);
++      if (ret)
++              return ret;
++
++      v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_PIXEL_RATE,
++                        OV64A40_PIXEL_RATE, OV64A40_PIXEL_RATE,  1,
++                        OV64A40_PIXEL_RATE);
++
++      ov64a40->link_freq =
++              v4l2_ctrl_new_int_menu(hdlr, &ov64a40_ctrl_ops,
++                                     V4L2_CID_LINK_FREQ,
++                                     ov64a40->num_link_frequencies - 1,
++                                     0, ov64a40->link_frequencies);
++
++      v4l2_ctrl_new_std_menu_items(hdlr, &ov64a40_ctrl_ops,
++                                   V4L2_CID_TEST_PATTERN,
++                                   ARRAY_SIZE(ov64a40_test_pattern_menu) - 1,
++                                   0, 0, ov64a40_test_pattern_menu);
++
++      timings = ov64a40_get_timings(ov64a40, 0);
++      exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN;
++      ov64a40->exposure = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++                                            V4L2_CID_EXPOSURE,
++                                            OV64A40_EXPOSURE_MIN, exp_max, 1,
++                                            OV64A40_EXPOSURE_MIN);
++
++      hblank_val = timings->ppl * 4 - ov64a40->mode->width;
++      ov64a40->hblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++                                          V4L2_CID_HBLANK, hblank_val,
++                                          hblank_val, 1, hblank_val);
++      if (ov64a40->hblank)
++              ov64a40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++      vblank_def = timings->vts - ov64a40->mode->height;
++      vblank_max = OV64A40_VTS_MAX - ov64a40->mode->height;
++      ov64a40->vblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++                                          V4L2_CID_VBLANK, OV64A40_VBLANK_MIN,
++                                          vblank_max, 1, vblank_def);
++
++      v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++                        OV64A40_ANA_GAIN_MIN, OV64A40_ANA_GAIN_MAX, 1,
++                        OV64A40_ANA_GAIN_DEFAULT);
++
++      ov64a40->hflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++                                         V4L2_CID_HFLIP, 0, 1, 1, 0);
++      if (ov64a40->hflip)
++              ov64a40->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      ov64a40->vflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++                                         V4L2_CID_VFLIP, 0, 1, 1, 0);
++      if (ov64a40->vflip)
++              ov64a40->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      if (hdlr->error) {
++              ret = hdlr->error;
++              dev_err(ov64a40->dev, "control init failed: %d\n", ret);
++              goto error_free_hdlr;
++      }
++
++      ret = v4l2_fwnode_device_parse(ov64a40->dev, &props);
++      if (ret)
++              goto error_free_hdlr;
++
++      ret = v4l2_ctrl_new_fwnode_properties(hdlr, &ov64a40_ctrl_ops,
++                                            &props);
++      if (ret)
++              goto error_free_hdlr;
++
++      ov64a40->sd.ctrl_handler = hdlr;
++
++      return 0;
++
++error_free_hdlr:
++      v4l2_ctrl_handler_free(hdlr);
++      return ret;
++}
++
++static int ov64a40_identify(struct ov64a40 *ov64a40)
++{
++      int ret;
++      u64 id;
++
++      ret = cci_read(ov64a40->cci, OV64A40_REG_CHIP_ID, &id, NULL);
++      if (ret) {
++              dev_err(ov64a40->dev, "Failed to read chip id: %d\n", ret);
++              return ret;
++      }
++
++      if (id != OV64A40_CHIP_ID) {
++              dev_err(ov64a40->dev, "chip id mismatch: %#llx\n", id);
++              return -ENODEV;
++      }
++
++      dev_dbg(ov64a40->dev, "OV64A40 chip identified: %#llx\n", id);
++
++      return 0;
++}
++
++static int ov64a40_parse_dt(struct ov64a40 *ov64a40)
++{
++      struct v4l2_fwnode_endpoint v4l2_fwnode = {
++              .bus_type = V4L2_MBUS_CSI2_DPHY
++      };
++      struct fwnode_handle *endpoint;
++      int ret = -EINVAL;
++      unsigned int i;
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(ov64a40->dev),
++                                                NULL);
++      if (!endpoint) {
++              dev_err(ov64a40->dev, "Failed to find endpoint\n");
++              return -EINVAL;
++      }
++
++      if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &v4l2_fwnode)) {
++              dev_err(ov64a40->dev, "Failed to parse endpoint\n");
++              goto error_put_fwnode;
++
++      }
++
++      if (v4l2_fwnode.bus.mipi_csi2.num_data_lanes != 2) {
++              dev_err(ov64a40->dev, "Unsupported number of data lanes: %u\n",
++                      v4l2_fwnode.bus.mipi_csi2.num_data_lanes);
++              goto error_free_fwnode;
++      }
++
++      if (!v4l2_fwnode.nr_of_link_frequencies) {
++              dev_warn(ov64a40->dev, "no link frequencies defined\n");
++              goto error_free_fwnode;
++      }
++
++      if (v4l2_fwnode.nr_of_link_frequencies > 2) {
++              dev_warn(ov64a40->dev,
++                       "Unsupported number of link frequencies\n");
++              goto error_free_fwnode;
++      }
++
++      ov64a40->link_frequencies =
++              devm_kcalloc(ov64a40->dev, v4l2_fwnode.nr_of_link_frequencies,
++                           sizeof(v4l2_fwnode.link_frequencies[0]),
++                           GFP_KERNEL);
++      if (!ov64a40->link_frequencies)  {
++              ret = -ENOMEM;
++              goto error_free_fwnode;
++      }
++      ov64a40->num_link_frequencies = v4l2_fwnode.nr_of_link_frequencies;
++
++      for (i = 0; i < v4l2_fwnode.nr_of_link_frequencies; ++i) {
++              if (v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_360M &&
++                  v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_456M) {
++                      dev_err(ov64a40->dev,
++                              "Unsupported link frequency %lld\n",
++                              v4l2_fwnode.link_frequencies[i]);
++                      goto error_free_fwnode;
++              }
++
++              ov64a40->link_frequencies[i] = v4l2_fwnode.link_frequencies[i];
++      }
++
++      v4l2_fwnode_endpoint_free(&v4l2_fwnode);
++
++      /* Register the subdev on the endpoint, so don't put it yet. */
++      ov64a40->sd.fwnode = endpoint;
++
++      return 0;
++
++error_free_fwnode:
++      v4l2_fwnode_endpoint_free(&v4l2_fwnode);
++error_put_fwnode:
++      fwnode_handle_put(endpoint);
++      return ret;
++}
++
++static int ov64a40_get_regulators(struct ov64a40 *ov64a40)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&ov64a40->sd);
++      unsigned int i;
++
++      for (i = 0; i < ARRAY_SIZE(ov64a40_supply_names); i++)
++              ov64a40->supplies[i].supply = ov64a40_supply_names[i];
++
++      return devm_regulator_bulk_get(&client->dev,
++                                     ARRAY_SIZE(ov64a40_supply_names),
++                                     ov64a40->supplies);
++}
++
++static int ov64a40_probe(struct i2c_client *client)
++{
++      struct ov64a40 *ov64a40;
++      u32 xclk_freq;
++      int ret;
++
++      ov64a40 = devm_kzalloc(&client->dev, sizeof(*ov64a40), GFP_KERNEL);
++      if (!ov64a40)
++              return -ENOMEM;
++
++      ov64a40->dev = &client->dev;
++      v4l2_i2c_subdev_init(&ov64a40->sd, client, &ov64a40_subdev_ops);
++
++      ov64a40->cci = devm_cci_regmap_init_i2c(client, 16);
++      if (IS_ERR(ov64a40->cci)) {
++              dev_err(&client->dev, "Failed to initialize CCI\n");
++              return PTR_ERR(ov64a40->cci);
++      }
++
++      ov64a40->xclk = devm_clk_get(&client->dev, NULL);
++      if (!ov64a40->xclk)
++              return dev_err_probe(&client->dev, PTR_ERR(ov64a40->xclk),
++                                   "Failed to get clock\n");
++
++      xclk_freq = clk_get_rate(ov64a40->xclk);
++      if (xclk_freq != OV64A40_XCLK_FREQ) {
++              dev_err(&client->dev, "Unsupported xclk frequency %u\n",
++                      xclk_freq);
++              return -EINVAL;
++      }
++
++      ret = ov64a40_get_regulators(ov64a40);
++      if (ret)
++              return ret;
++
++      ov64a40->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
++                                                    GPIOD_OUT_LOW);
++      if (IS_ERR(ov64a40->reset_gpio))
++              return dev_err_probe(&client->dev, PTR_ERR(ov64a40->reset_gpio),
++                                   "Failed to get reset gpio\n");
++
++      ret = ov64a40_parse_dt(ov64a40);
++      if (ret)
++              return ret;
++
++      ret = ov64a40_power_on(&client->dev);
++      if (ret)
++              goto error_put_fwnode;
++
++      ret = ov64a40_identify(ov64a40);
++      if (ret)
++              goto error_poweroff;
++
++      ov64a40->mode = &ov64a40_modes[0];
++
++      pm_runtime_set_active(&client->dev);
++      pm_runtime_get_noresume(&client->dev);
++      pm_runtime_enable(&client->dev);
++      pm_runtime_set_autosuspend_delay(&client->dev, 1000);
++      pm_runtime_use_autosuspend(&client->dev);
++
++      ret = ov64a40_init_controls(ov64a40);
++      if (ret)
++              goto error_poweroff;
++
++      /* Initialize subdev */
++      ov64a40->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE
++                        | V4L2_SUBDEV_FL_HAS_EVENTS;
++      ov64a40->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++      ov64a40->pad.flags = MEDIA_PAD_FL_SOURCE;
++      ret = media_entity_pads_init(&ov64a40->sd.entity, 1, &ov64a40->pad);
++      if (ret) {
++              dev_err(&client->dev, "failed to init entity pads: %d\n", ret);
++              goto error_handler_free;
++      }
++
++      ov64a40->sd.state_lock = ov64a40->ctrl_handler.lock;
++      ret = v4l2_subdev_init_finalize(&ov64a40->sd);
++      if (ret < 0) {
++              dev_err(&client->dev, "subdev init error: %d\n", ret);
++              goto error_media_entity;
++      }
++
++      ret = v4l2_async_register_subdev_sensor(&ov64a40->sd);
++      if (ret < 0) {
++              dev_err(&client->dev,
++                      "failed to register sensor sub-device: %d\n", ret);
++              goto error_subdev_cleanup;
++      }
++
++      pm_runtime_mark_last_busy(&client->dev);
++      pm_runtime_put_autosuspend(&client->dev);
++
++      return 0;
++
++error_subdev_cleanup:
++      v4l2_subdev_cleanup(&ov64a40->sd);
++error_media_entity:
++      media_entity_cleanup(&ov64a40->sd.entity);
++error_handler_free:
++      v4l2_ctrl_handler_free(ov64a40->sd.ctrl_handler);
++error_poweroff:
++      ov64a40_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++error_put_fwnode:
++      fwnode_handle_put(ov64a40->sd.fwnode);
++
++      return ret;
++}
++
++static void ov64a40_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++      v4l2_async_unregister_subdev(sd);
++      fwnode_handle_put(ov64a40->sd.fwnode);
++      v4l2_subdev_cleanup(sd);
++      media_entity_cleanup(&sd->entity);
++      v4l2_ctrl_handler_free(sd->ctrl_handler);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              ov64a40_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id ov64a40_of_ids[] = {
++      { .compatible = "ovti,ov64a40" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov64a40_of_ids);
++
++static const struct dev_pm_ops ov64a40_pm_ops = {
++      SET_RUNTIME_PM_OPS(ov64a40_power_off, ov64a40_power_on, NULL)
++};
++
++static struct i2c_driver ov64a40_i2c_driver = {
++      .driver = {
++              .name = "ov64a40",
++              .of_match_table = ov64a40_of_ids,
++              .pm = &ov64a40_pm_ops,
++      },
++      .probe_new = ov64a40_probe,
++      .remove = ov64a40_remove,
++};
++
++module_i2c_driver(ov64a40_i2c_driver);
++
++MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com>");
++MODULE_DESCRIPTION("OmniVision OV64A40 sensor driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch b/target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch
new file mode 100644 (file)
index 0000000..a50617e
--- /dev/null
@@ -0,0 +1,368 @@
+From 97ec6aeb265df0bfe7193f00c249b38873fb0fb7 Mon Sep 17 00:00:00 2001
+From: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Date: Wed, 13 Sep 2023 17:53:54 +0100
+Subject: [PATCH] media: i2c: Add ROHM BU64754 Camera Autofocus Actuator
+
+Add support for the ROHM BU64754 Motor Driver for Camera Autofocus. A
+V4L2 Subdevice is registered and provides a single
+V4L2_CID_FOCUS_ABSOLUTE control.
+
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ drivers/media/i2c/Kconfig   |  13 ++
+ drivers/media/i2c/Makefile  |   1 +
+ drivers/media/i2c/bu64754.c | 315 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 329 insertions(+)
+ create mode 100644 drivers/media/i2c/bu64754.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -917,6 +917,19 @@ config VIDEO_AK7375
+         capability. This is designed for linear control of
+         voice coil motors, controlled via I2C serial interface.
++config VIDEO_BU64754
++      tristate "BU64754 Motor Driver for Camera Autofocus"
++      depends on I2C && VIDEO_DEV
++      select MEDIA_CONTROLLER
++      select VIDEO_V4L2_SUBDEV_API
++      select V4L2_ASYNC
++      select V4L2_CCI_I2C
++      help
++        This is a driver for the BU64754 Motor Driver for Camera
++        Autofocus. The BU64754GWZ is an actuator driver IC which
++        can be controlled the actuator position precisely using
++        with internal Hall Sensor.
++
+ config VIDEO_DW9714
+       tristate "DW9714 lens voice coil support"
+       depends on I2C && VIDEO_DEV
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -26,6 +26,7 @@ obj-$(CONFIG_VIDEO_ARDUCAM_PIVARIETY) +=
+ obj-$(CONFIG_VIDEO_BT819) += bt819.o
+ obj-$(CONFIG_VIDEO_BT856) += bt856.o
+ obj-$(CONFIG_VIDEO_BT866) += bt866.o
++obj-$(CONFIG_VIDEO_BU64754) += bu64754.o
+ obj-$(CONFIG_VIDEO_CCS) += ccs/
+ obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o
+ obj-$(CONFIG_VIDEO_CS3308) += cs3308.o
+--- /dev/null
++++ b/drivers/media/i2c/bu64754.c
+@@ -0,0 +1,315 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * The BU64754GWZ is an actuator driver IC which can control the
++ * actuator position precisely using an internal Hall Sensor.
++ */
++
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++
++#include <media/v4l2-cci.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++
++#define BU64754_REG_ACTIVE    CCI_REG16(0x07)
++#define BU64754_ACTIVE_MODE   0x8080
++
++#define BU64754_REG_SERVE     CCI_REG16(0xd9)
++#define BU64754_SERVE_ON      0x0404
++
++#define BU64754_REG_POSITION  CCI_REG16(0x45)
++#define BU64753_POSITION_MAX  1023 /* 0x3ff */
++#define BU64753_POSITION_STEPS        1
++
++#define BU64754_POWER_ON_DELAY        800 /* uS : t1, t3 */
++
++struct bu64754 {
++      struct device *dev;
++
++      struct v4l2_ctrl_handler ctrls_vcm;
++      struct v4l2_subdev sd;
++      struct regmap *cci;
++
++      u16 current_val;
++      struct regulator *vdd;
++      struct notifier_block notifier;
++};
++
++static inline struct bu64754 *sd_to_bu64754(struct v4l2_subdev *subdev)
++{
++      return container_of(subdev, struct bu64754, sd);
++}
++
++static int bu64754_set(struct bu64754 *bu64754, u16 position)
++{
++      int ret;
++
++      position &= 0x3ff; /* BU64753_POSITION_MAX */
++      ret = cci_write(bu64754->cci, BU64754_REG_POSITION, position, NULL);
++      if (ret) {
++              dev_err(bu64754->dev, "Set position failed ret=%d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int bu64754_active(struct bu64754 *bu64754)
++{
++      int ret;
++
++      /* Power on */
++      ret = cci_write(bu64754->cci, BU64754_REG_ACTIVE, BU64754_ACTIVE_MODE, NULL);
++      if (ret < 0) {
++              dev_err(bu64754->dev, "Failed to set active mode ret = %d\n",
++                      ret);
++              return ret;
++      }
++
++      /* Serve on */
++      ret = cci_write(bu64754->cci, BU64754_REG_SERVE, BU64754_SERVE_ON, NULL);
++      if (ret < 0) {
++              dev_err(bu64754->dev, "Failed to enable serve ret = %d\n",
++                      ret);
++              return ret;
++      }
++
++      return bu64754_set(bu64754, bu64754->current_val);
++}
++
++static int bu64754_standby(struct bu64754 *bu64754)
++{
++      int ret;
++
++      ret = cci_write(bu64754->cci, BU64754_REG_ACTIVE, 0, NULL);
++      if (ret < 0)
++              dev_err(bu64754->dev, "Failed to enter standby mode ret = %d\n",
++                      ret);
++
++      return ret;
++}
++
++static int bu64754_regulator_event(struct notifier_block *nb,
++                                 unsigned long action, void *data)
++{
++      struct bu64754 *bu64754 = container_of(nb, struct bu64754, notifier);
++
++      if (action & REGULATOR_EVENT_ENABLE) {
++              /*
++               * Initialisation delay between VDD low->high and availability
++               * i2c operation.
++               */
++              usleep_range(BU64754_POWER_ON_DELAY,
++                           BU64754_POWER_ON_DELAY + 100);
++
++              bu64754_active(bu64754);
++      } else if (action & REGULATOR_EVENT_PRE_DISABLE) {
++              bu64754_standby(bu64754);
++      }
++
++      return 0;
++}
++
++static int bu64754_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct bu64754 *bu64754 = container_of(ctrl->handler,
++              struct bu64754, ctrls_vcm);
++
++      if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
++              bu64754->current_val = ctrl->val;
++              return bu64754_set(bu64754, ctrl->val);
++      }
++
++      return -EINVAL;
++}
++
++static const struct v4l2_ctrl_ops bu64754_vcm_ctrl_ops = {
++      .s_ctrl = bu64754_set_ctrl,
++};
++
++static int bu64754_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      return pm_runtime_resume_and_get(sd->dev);
++}
++
++static int bu64754_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      pm_runtime_put(sd->dev);
++      return 0;
++}
++
++static const struct v4l2_subdev_internal_ops bu64754_int_ops = {
++      .open = bu64754_open,
++      .close = bu64754_close,
++};
++
++static const struct v4l2_subdev_ops bu64754_ops = { };
++
++static void bu64754_subdev_cleanup(struct bu64754 *bu64754)
++{
++      v4l2_async_unregister_subdev(&bu64754->sd);
++      v4l2_ctrl_handler_free(&bu64754->ctrls_vcm);
++      media_entity_cleanup(&bu64754->sd.entity);
++}
++
++static int bu64754_init_controls(struct bu64754 *bu64754)
++{
++      struct v4l2_ctrl_handler *hdl = &bu64754->ctrls_vcm;
++      const struct v4l2_ctrl_ops *ops = &bu64754_vcm_ctrl_ops;
++
++      v4l2_ctrl_handler_init(hdl, 1);
++
++      v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
++                        0, BU64753_POSITION_MAX, BU64753_POSITION_STEPS,
++                        0);
++
++      bu64754->current_val = 0;
++
++      bu64754->sd.ctrl_handler = hdl;
++      if (hdl->error) {
++              dev_err(bu64754->dev, "%s fail error: 0x%x\n",
++                      __func__, hdl->error);
++              return hdl->error;
++      }
++
++      return 0;
++}
++
++static int bu64754_probe(struct i2c_client *client)
++{
++      struct bu64754 *bu64754;
++      int ret;
++
++      bu64754 = devm_kzalloc(&client->dev, sizeof(*bu64754), GFP_KERNEL);
++      if (!bu64754)
++              return -ENOMEM;
++
++      bu64754->dev = &client->dev;
++
++      bu64754->cci = devm_cci_regmap_init_i2c(client, 8);
++      if (IS_ERR(bu64754->cci)) {
++              dev_err(bu64754->dev, "Failed to initialize CCI\n");
++              return PTR_ERR(bu64754->cci);
++      }
++
++      bu64754->vdd = devm_regulator_get_optional(&client->dev, "vdd");
++      if (IS_ERR(bu64754->vdd)) {
++              if (PTR_ERR(bu64754->vdd) != -ENODEV)
++                      return PTR_ERR(bu64754->vdd);
++
++              bu64754->vdd = NULL;
++      } else {
++              bu64754->notifier.notifier_call = bu64754_regulator_event;
++
++              ret = regulator_register_notifier(bu64754->vdd,
++                                                &bu64754->notifier);
++              if (ret) {
++                      dev_err(bu64754->dev,
++                              "could not register regulator notifier\n");
++                      return ret;
++              }
++      }
++
++      v4l2_i2c_subdev_init(&bu64754->sd, client, &bu64754_ops);
++      bu64754->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++      bu64754->sd.internal_ops = &bu64754_int_ops;
++      bu64754->sd.entity.function = MEDIA_ENT_F_LENS;
++
++      ret = bu64754_init_controls(bu64754);
++      if (ret)
++              goto err_cleanup;
++
++      ret = media_entity_pads_init(&bu64754->sd.entity, 0, NULL);
++      if (ret < 0)
++              goto err_cleanup;
++
++      bu64754->sd.entity.function = MEDIA_ENT_F_LENS;
++
++      ret = v4l2_async_register_subdev(&bu64754->sd);
++      if (ret < 0)
++              goto err_cleanup;
++
++      if (!bu64754->vdd)
++              pm_runtime_set_active(&client->dev);
++
++      pm_runtime_enable(&client->dev);
++      pm_runtime_idle(&client->dev);
++
++      return 0;
++
++err_cleanup:
++      v4l2_ctrl_handler_free(&bu64754->ctrls_vcm);
++      media_entity_cleanup(&bu64754->sd.entity);
++
++      return ret;
++}
++
++static void bu64754_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++      if (bu64754->vdd)
++              regulator_unregister_notifier(bu64754->vdd,
++                                            &bu64754->notifier);
++
++      pm_runtime_disable(&client->dev);
++
++      bu64754_subdev_cleanup(bu64754);
++}
++
++static int __maybe_unused bu64754_vcm_suspend(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++      if (bu64754->vdd)
++              return regulator_disable(bu64754->vdd);
++
++      return bu64754_standby(bu64754);
++}
++
++static int  __maybe_unused bu64754_vcm_resume(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++      if (bu64754->vdd)
++              return regulator_enable(bu64754->vdd);
++
++      return bu64754_active(bu64754);
++}
++
++static const struct of_device_id bu64754_of_table[] = {
++      { .compatible = "rohm,bu64754", },
++      { /* sentinel */ }
++};
++
++MODULE_DEVICE_TABLE(of, bu64754_of_table);
++
++static const struct dev_pm_ops bu64754_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(bu64754_vcm_suspend, bu64754_vcm_resume)
++      SET_RUNTIME_PM_OPS(bu64754_vcm_suspend, bu64754_vcm_resume, NULL)
++};
++
++static struct i2c_driver bu64754_i2c_driver = {
++      .driver = {
++              .name = "bu64754",
++              .pm = &bu64754_pm_ops,
++              .of_match_table = bu64754_of_table,
++      },
++      .probe_new = bu64754_probe,
++      .remove = bu64754_remove,
++};
++
++module_i2c_driver(bu64754_i2c_driver);
++
++MODULE_AUTHOR("Kieran Bingham");
++MODULE_DESCRIPTION("BU64754 VCM driver");
++MODULE_LICENSE("GPL");
++
diff --git a/target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch b/target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch
new file mode 100644 (file)
index 0000000..d3ee0dc
--- /dev/null
@@ -0,0 +1,426 @@
+From 7f67a45ee7c008c3d8e45fde6fa9c4287fb3bc9e Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Thu, 20 Jul 2023 13:18:34 +0200
+Subject: [PATCH] overlays: Add overlay for the OV64A40 Arducam Camera Module
+
+Arducam have integrated an Omnivision OV64A40 with a ROHM BU64754 VCM
+with a Raspberry Pi compatible cable pinout.
+
+Provide an overlay to support the module.
+
+Also add support to the camera mux overlays.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             | 25 +++++
+ .../dts/overlays/camera-mux-2port-overlay.dts | 32 +++++++
+ .../dts/overlays/camera-mux-4port-overlay.dts | 64 +++++++++++++
+ .../arm/boot/dts/overlays/ov64a40-overlay.dts | 91 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/ov64a40.dtsi       | 34 +++++++
+ 6 files changed, 247 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/ov64a40-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/ov64a40.dtsi
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -175,6 +175,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+       mz61581.dtbo \
+       ov2311.dtbo \
+       ov5647.dtbo \
++      ov64a40.dtbo \
+       ov7251.dtbo \
+       ov9281.dtbo \
+       papirus.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -817,6 +817,7 @@ Params: cam0-arducam-64mp       Select A
+         cam0-imx708             Select IMX708 for camera on port 0
+         cam0-ov2311             Select OV2311 for camera on port 0
+         cam0-ov5647             Select OV5647 for camera on port 0
++        cam0-ov64a40            Select OV64A40 for camera on port 0
+         cam0-ov7251             Select OV7251 for camera on port 0
+         cam0-ov9281             Select OV9281 for camera on port 0
+         cam0-imx290-clk-freq    Set clock frequency for an IMX290 on port 0
+@@ -829,6 +830,7 @@ Params: cam0-arducam-64mp       Select A
+         cam1-imx708             Select IMX708 for camera on port 1
+         cam1-ov2311             Select OV2311 for camera on port 1
+         cam1-ov5647             Select OV5647 for camera on port 1
++        cam1-ov64a40            Select OV64A40 for camera on port 1
+         cam1-ov7251             Select OV7251 for camera on port 1
+         cam1-ov9281             Select OV9281 for camera on port 1
+         cam1-imx290-clk-freq    Set clock frequency for an IMX290 on port 1
+@@ -850,6 +852,7 @@ Params: cam0-arducam-64mp       Select A
+         cam0-imx708             Select IMX708 for camera on port 0
+         cam0-ov2311             Select OV2311 for camera on port 0
+         cam0-ov5647             Select OV5647 for camera on port 0
++        cam0-ov64a40            Select OV64A40 for camera on port 0
+         cam0-ov7251             Select OV7251 for camera on port 0
+         cam0-ov9281             Select OV9281 for camera on port 0
+         cam0-imx290-clk-freq    Set clock frequency for an IMX290 on port 0
+@@ -862,6 +865,7 @@ Params: cam0-arducam-64mp       Select A
+         cam1-imx708             Select IMX708 for camera on port 1
+         cam1-ov2311             Select OV2311 for camera on port 1
+         cam1-ov5647             Select OV5647 for camera on port 1
++        cam1-ov64a40            Select OV64A40 for camera on port 1
+         cam1-ov7251             Select OV7251 for camera on port 1
+         cam1-ov9281             Select OV9281 for camera on port 1
+         cam1-imx290-clk-freq    Set clock frequency for an IMX290 on port 1
+@@ -874,6 +878,7 @@ Params: cam0-arducam-64mp       Select A
+         cam2-imx708             Select IMX708 for camera on port 2
+         cam2-ov2311             Select OV2311 for camera on port 2
+         cam2-ov5647             Select OV5647 for camera on port 2
++        cam2-ov64a40            Select OV64A40 for camera on port 2
+         cam2-ov7251             Select OV7251 for camera on port 2
+         cam2-ov9281             Select OV9281 for camera on port 2
+         cam2-imx290-clk-freq    Set clock frequency for an IMX290 on port 2
+@@ -886,6 +891,7 @@ Params: cam0-arducam-64mp       Select A
+         cam3-imx708             Select IMX708 for camera on port 3
+         cam3-ov2311             Select OV2311 for camera on port 3
+         cam3-ov5647             Select OV5647 for camera on port 3
++        cam3-ov64a40            Select OV64A40 for camera on port 3
+         cam3-ov7251             Select OV7251 for camera on port 3
+         cam3-ov9281             Select OV9281 for camera on port 3
+         cam3-imx290-clk-freq    Set clock frequency for an IMX290 on port 3
+@@ -3222,6 +3228,25 @@ Params: rotation                Mounting
+         vcm                     Configure a VCM focus drive on the sensor.
++Name:   ov64a40
++Info:   Arducam OV64A40 camera module.
++        Uses Unicam 1, which is the standard camera connector on most Pi
++        variants.
++Load:   dtoverlay=ov64a40,<param>=<val>
++Params: rotation                Mounting rotation of the camera sensor (0 or
++                                180, default 0)
++        orientation             Sensor orientation (0 = front, 1 = rear,
++                                2 = external, default external)
++        media-controller        Configure use of Media Controller API for
++                                configuring the sensor (default on)
++        cam0                    Adopt the default configuration for CAM0 on a
++                                Compute Module (CSI0, i2c_vc, and cam0_reg).
++        vcm                     Select lens driver state. Default is enabled,
++                                but vcm=off will disable.
++        link-frequency          Allowable link frequency values to use in Hz:
++                                456000000 (default), 360000000
++
++
+ Name:   ov7251
+ Info:   Omnivision OV7251 camera module.
+         Uses Unicam 1, which is the standard camera connector on most Pi
+--- a/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
+@@ -189,6 +189,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_0
++                                      #define cam_endpoint ov64a40_0_ep
++                                      #define vcm_node ov64a40_0_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                               i2c@1 {
+@@ -289,6 +299,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_1
++                                      #define cam_endpoint ov64a40_1_ep
++                                      #define vcm_node ov64a40_1_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                       };
+               };
+@@ -450,6 +470,12 @@
+               cam0-ov2311 = <&mux_in0>, "remote-endpoint:0=",<&ov2311_0_ep>,
+                             <&ov2311_0_ep>, "remote-endpoint:0=",<&mux_in0>,
+                             <&ov2311_0>, "status=okay";
++              cam0-ov64a40 = <&mux_in0>, "remote-endpoint:0=",<&ov64a40_0_ep>,
++                            <&ov64a40_0_ep>, "remote-endpoint:0=",<&mux_in0>,
++                            <&mux_in0>, "clock-noncontinuous?",
++                            <&ov64a40_0>, "status=okay",
++                            <&ov64a40_0_vcm>, "status=okay",
++                            <&ov64a40_0>,"lens-focus:0=", <&ov64a40_0_vcm>;
+               cam1-arducam-64mp = <&mux_in1>, "remote-endpoint:0=",<&arducam_64mp_1_ep>,
+                                   <&arducam_64mp_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+@@ -496,6 +522,12 @@
+               cam1-ov2311 = <&mux_in1>, "remote-endpoint:0=",<&ov2311_1_ep>,
+                             <&ov2311_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+                             <&ov2311_1>, "status=okay";
++              cam1-ov64a40 = <&mux_in1>, "remote-endpoint:0=",<&ov64a40_1_ep>,
++                            <&ov64a40_1_ep>, "remote-endpoint:0=",<&mux_in1>,
++                            <&mux_in1>, "clock-noncontinuous?",
++                            <&ov64a40_1>, "status=okay",
++                            <&ov64a40_1_vcm>, "status=okay",
++                            <&ov64a40_1>,"lens-focus:0=", <&ov64a40_1_vcm>;
+               cam0-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+                                      <&imx290_0>,"clock-frequency:0";
+--- a/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
+@@ -247,6 +247,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_0
++                                      #define cam_endpoint ov64a40_0_ep
++                                      #define vcm_node ov64a40_0_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                               i2c@1 {
+@@ -347,6 +357,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_1
++                                      #define cam_endpoint ov64a40_1_ep
++                                      #define vcm_node ov64a40_1_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                               i2c@2 {
+@@ -447,6 +467,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_2
++                                      #define cam_endpoint ov64a40_2_ep
++                                      #define vcm_node ov64a40_2_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                               i2c@3 {
+@@ -547,6 +577,16 @@
+                                       #undef cam_node
+                                       #undef cam_endpoint
+                                       #undef cam1_clk
++
++                                      #define cam_node ov64a40_3
++                                      #define cam_endpoint ov64a40_3_ep
++                                      #define vcm_node ov64a40_3_vcm
++                                      #define cam1_clk clk_24mhz
++                                      #include "ov64a40.dtsi"
++                                      #undef cam_node
++                                      #undef cam_endpoint
++                                      #undef vcm_node
++                                      #undef cam1_clk
+                               };
+                       };
+               };
+@@ -725,6 +765,12 @@
+               cam0-ov2311 = <&mux_in0>, "remote-endpoint:0=",<&ov2311_0_ep>,
+                             <&ov2311_0_ep>, "remote-endpoint:0=",<&mux_in0>,
+                             <&ov2311_0>, "status=okay";
++              cam0-ov64a40 = <&mux_in0>, "remote-endpoint:0=",<&ov64a40_0_ep>,
++                            <&ov64a40_0_ep>, "remote-endpoint:0=",<&mux_in0>,
++                            <&mux_in0>, "clock-noncontinuous?",
++                            <&ov64a40_0>, "status=okay",
++                            <&ov64a40_0_vcm>, "status=okay",
++                            <&ov64a40_0>,"lens-focus:0=", <&ov64a40_0_vcm>;
+               cam1-arducam-64mp = <&mux_in1>, "remote-endpoint:0=",<&arducam_64mp_1_ep>,
+                                   <&arducam_64mp_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+@@ -771,6 +817,12 @@
+               cam1-ov2311 = <&mux_in1>, "remote-endpoint:0=",<&ov2311_1_ep>,
+                             <&ov2311_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+                             <&ov2311_1>, "status=okay";
++              cam1-ov64a40 = <&mux_in1>, "remote-endpoint:0=",<&ov64a40_1_ep>,
++                            <&ov64a40_1_ep>, "remote-endpoint:0=",<&mux_in1>,
++                            <&mux_in1>, "clock-noncontinuous?",
++                            <&ov64a40_1>, "status=okay",
++                            <&ov64a40_1_vcm>, "status=okay",
++                            <&ov64a40_1>,"lens-focus:0=", <&ov64a40_1_vcm>;
+               cam2-arducam-64mp = <&mux_in2>, "remote-endpoint:0=",<&arducam_64mp_2_ep>,
+                                   <&arducam_64mp_2_ep>, "remote-endpoint:0=",<&mux_in2>,
+@@ -817,6 +869,12 @@
+               cam2-ov2311 = <&mux_in2>, "remote-endpoint:0=",<&ov2311_2_ep>,
+                             <&ov2311_2_ep>, "remote-endpoint:0=",<&mux_in2>,
+                             <&ov2311_2>, "status=okay";
++              cam2-ov64a40 = <&mux_in2>, "remote-endpoint:0=",<&ov64a40_2_ep>,
++                            <&ov64a40_2_ep>, "remote-endpoint:0=",<&mux_in2>,
++                            <&mux_in2>, "clock-noncontinuous?",
++                            <&ov64a40_2>, "status=okay",
++                            <&ov64a40_2_vcm>, "status=okay",
++                            <&ov64a40_2>,"lens-focus:0=", <&ov64a40_2_vcm>;
+               cam3-arducam-64mp = <&mux_in3>, "remote-endpoint:0=",<&arducam_64mp_3_ep>,
+                                   <&arducam_64mp_3_ep>, "remote-endpoint:0=",<&mux_in3>,
+@@ -863,6 +921,12 @@
+               cam3-ov2311 = <&mux_in3>, "remote-endpoint:0=",<&ov2311_3_ep>,
+                             <&ov2311_3_ep>, "remote-endpoint:0=",<&mux_in3>,
+                             <&ov2311_3>, "status=okay";
++              cam3-ov64a40 = <&mux_in3>, "remote-endpoint:0=",<&ov64a40_3_ep>,
++                            <&ov64a40_3_ep>, "remote-endpoint:0=",<&mux_in3>,
++                            <&mux_in3>, "clock-noncontinuous?",
++                            <&ov64a40_3>, "status=okay",
++                            <&ov64a40_3_vcm>, "status=okay",
++                            <&ov64a40_3>,"lens-focus:0=", <&ov64a40_3_vcm>;
+               cam0-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+                                      <&imx290_0>,"clock-frequency:0";
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/ov64a40-overlay.dts
+@@ -0,0 +1,91 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for OV64A40 camera module on VC I2C bus
++/dts-v1/;
++/plugin/;
++
++/{
++      compatible = "brcm,bcm2835";
++
++      i2c_frag: fragment@0 {
++              target = <&i2c_csi_dsi>;
++              __overlay__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "okay";
++
++                      #include "ov64a40.dtsi"
++              };
++      };
++
++      csi_frag: fragment@1 {
++              target = <&csi1>;
++              csi: __overlay__ {
++                      status = "okay";
++                      brcm,media-controller;
++
++                      port{
++                              csi_ep: endpoint{
++                                      remote-endpoint = <&cam_endpoint>;
++                                      clock-lanes = <0>;
++                                      data-lanes = <1 2>;
++                              };
++                      };
++              };
++      };
++
++      fragment@2 {
++              target = <&i2c0if>;
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      clk_frag: fragment@3 {
++              target = <&cam1_clk>;
++              __overlay__ {
++                      clock-frequency = <24000000>;
++                      status = "okay";
++              };
++      };
++
++      fragment@4 {
++              target = <&i2c0mux>;
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      fragment@5 {
++              target = <&cam_node>;
++              __overlay__ {
++                      lens-focus = <&vcm_node>;
++              };
++      };
++
++      __overrides__ {
++              rotation = <&cam_node>,"rotation:0";
++              orientation = <&cam_node>,"orientation:0";
++              media-controller = <&csi>,"brcm,media-controller?";
++              cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++                     <&csi_frag>, "target:0=",<&csi0>,
++                     <&clk_frag>, "target:0=",<&cam0_clk>,
++                     <&cam_node>, "clocks:0=",<&cam0_clk>,
++                     <&cam_node>, "avdd-supply:0=",<&cam0_reg>,
++                     <&vcm_node>, "vdd-supply:0=",<&cam0_reg>;
++              vcm = <&vcm_node>, "status",
++                    <0>, "=5";
++              link-frequency = <&cam_endpoint>,"link-frequencies#0";
++      };
++};
++
++&cam_node {
++      status = "okay";
++};
++
++&cam_endpoint {
++      remote-endpoint = <&csi_ep>;
++};
++
++&vcm_node {
++      status = "okay";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/ov64a40.dtsi
+@@ -0,0 +1,34 @@
++// Fragment that configures an OV64A40
++
++cam_node: ov64a40@36 {
++      compatible = "ovti,ov64a40";
++      reg = <0x36>;
++      status = "disabled";
++
++      clocks = <&cam1_clk>;
++      clock-names = "xclk";
++
++      avdd-supply = <&cam1_reg>;      /* 2.8v */
++      dovdd-supply = <&cam_dummy_reg>;/* 1.8v */
++      dvdd-supply = <&cam_dummy_reg>; /* 1.1v */
++
++      rotation = <180>;
++      orientation = <2>;
++
++      port {
++              cam_endpoint: endpoint {
++                      bus-type = <4>;
++                      clock-lanes = <0>;
++                      data-lanes = <1 2>;
++                      link-frequencies =
++                              /bits/ 64 <456000000>;
++              };
++      };
++};
++
++vcm_node: bu64754@76 {
++      compatible = "rohm,bu64754";
++      reg = <0x76>;
++      status = "disabled";
++      vdd-supply = <&cam1_reg>;
++};
diff --git a/target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch b/target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch
new file mode 100644 (file)
index 0000000..8ff96cc
--- /dev/null
@@ -0,0 +1,26 @@
+From 8ef68aadaa3aa29bc2661ab44db4ddc50e77cef5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 11:25:16 +0300
+Subject: [PATCH] media: rp1: cfe: Fix verbose debug print
+
+Switch a debug print from cfe_dbg() to cfe_dbg_verbose() as it will be
+printed often while streaming.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -975,8 +975,8 @@ static void cfe_buffer_queue(struct vb2_
+       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_dbg_verbose("Preparing job immediately for channel %u\n",
++                              node->id);
+               cfe_prepare_next_job(cfe);
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch b/target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch
new file mode 100644 (file)
index 0000000..f263648
--- /dev/null
@@ -0,0 +1,33 @@
+From d978e784f433346d3676b5de805b3cea36b835c4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:23:58 +0300
+Subject: [PATCH] media: rp1: cfe: Expose find_format_by_pix()
+
+Make find_format_by_pix() accessible to other files in the driver.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.h | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -461,7 +461,7 @@ const struct cfe_fmt *find_format_by_cod
+       return NULL;
+ }
+-static const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
++const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
+ {
+       unsigned int i;
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -36,5 +36,6 @@ extern const struct v4l2_mbus_framefmt c
+ extern const struct v4l2_mbus_framefmt cfe_default_meta_format;
+ const struct cfe_fmt *find_format_by_code(u32 code);
++const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
+ #endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch b/target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch
new file mode 100644 (file)
index 0000000..d6dadb6
--- /dev/null
@@ -0,0 +1,43 @@
+From cbed711f05a228d0f8f54b1b01f43d4d6489eccc Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:24:14 +0300
+Subject: [PATCH] media: rp1: cfe: Add missing remaps
+
+8-bit bayer formats are missing remap definitions. Add them.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -85,24 +85,28 @@ static const struct cfe_fmt formats[] =
+               .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+               .depth = 8,
+               .csi_dt = 0x2a,
++              .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SGBRG8,
+               .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+               .depth = 8,
+               .csi_dt = 0x2a,
++              .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SGRBG8,
+               .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+               .depth = 8,
+               .csi_dt = 0x2a,
++              .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SRGGB8,
+               .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+               .depth = 8,
+               .csi_dt = 0x2a,
++              .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SBGGR10P,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch b/target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch
new file mode 100644 (file)
index 0000000..92d7db3
--- /dev/null
@@ -0,0 +1,43 @@
+From 93c40564b94367c6ce072d66479af58afa3f08e0 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:14:31 +0300
+Subject: [PATCH] media: rp1: cfe: Add missing compressed remaps
+
+16-bit bayer formats are missing compressed remap definitions. Add them.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -197,24 +197,28 @@ static const struct cfe_fmt formats[] =
+               .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+               .depth = 16,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
++              .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SGBRG16,
+               .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+               .depth = 16,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
++              .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SGRBG16,
+               .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+               .depth = 16,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
++              .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_SRGGB16,
+               .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+               .depth = 16,
+               .flags = CFE_FORMAT_FLAG_FE_OUT,
++              .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+       },
+       /* PiSP Compressed Mode 1 */
+       {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch b/target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch
new file mode 100644 (file)
index 0000000..42c3b6f
--- /dev/null
@@ -0,0 +1,74 @@
+From 2e0e1d7b493dffe7baa763d499e51ba42f0bad19 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:14:11 +0300
+Subject: [PATCH] media: rp1: cfe: Add cfe_find_16bit_code() and
+ cfe_find_compressed_code()
+
+Add helper functions which, given an mbus code, return the 16-bit
+remapped mbus code or the compressed mbus code.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 40 +++++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h  |  2 +
+ 2 files changed, 42 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -473,6 +473,46 @@ const struct cfe_fmt *find_format_by_pix
+       return NULL;
+ }
++/*
++ * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap
++ * possible.
++ */
++u32 cfe_find_16bit_code(u32 code)
++{
++      const struct cfe_fmt *cfe_fmt;
++
++      cfe_fmt = find_format_by_code(code);
++
++      if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT])
++              return 0;
++
++      cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]);
++      if (!cfe_fmt)
++              return 0;
++
++      return cfe_fmt->code;
++}
++
++/*
++ * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap
++ * possible.
++ */
++u32 cfe_find_compressed_code(u32 code)
++{
++      const struct cfe_fmt *cfe_fmt;
++
++      cfe_fmt = find_format_by_code(code);
++
++      if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED])
++              return 0;
++
++      cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]);
++      if (!cfe_fmt)
++              return 0;
++
++      return cfe_fmt->code;
++}
++
+ static int cfe_calc_format_size_bpl(struct cfe_device *cfe,
+                                   const struct cfe_fmt *fmt,
+                                   struct v4l2_format *f)
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -37,5 +37,7 @@ extern const struct v4l2_mbus_framefmt c
+ const struct cfe_fmt *find_format_by_code(u32 code);
+ const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
++u32 cfe_find_16bit_code(u32 code);
++u32 cfe_find_compressed_code(u32 code);
+ #endif
diff --git a/target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch b/target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch
new file mode 100644 (file)
index 0000000..d9b8d26
--- /dev/null
@@ -0,0 +1,95 @@
+From eaa8a0ae14a1ca797c1896e9dafbefa1fa51a617 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:25:10 +0300
+Subject: [PATCH] media: rp1: csi2: Fix csi2_pad_set_fmt()
+
+The CSI-2 subdev's set_fmt currently allows setting the source and sink
+pad formats quite freely. This is not right, as the CSI-2 block can only
+do one of the following when processing the stream: 1) pass through as
+is, 2) expand to 16-bits, 3) compress.
+
+The csi2_pad_set_fmt() should take this into account, and only allow
+changing the source side mbus code, compared to the sink side format.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 61 +++++++++++++++----
+ 1 file changed, 48 insertions(+), 13 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -438,25 +438,60 @@ static int csi2_pad_set_fmt(struct v4l2_
+                           struct v4l2_subdev_state *state,
+                           struct v4l2_subdev_format *format)
+ {
+-      struct v4l2_mbus_framefmt *fmt;
+-      const struct cfe_fmt *cfe_fmt;
+-
+-      /* TODO: format validation */
++      if (format->pad < CSI2_NUM_CHANNELS) {
++              /*
++               * Store the sink pad format and propagate it to the source pad.
++               */
++
++              struct v4l2_mbus_framefmt *fmt;
++
++              fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++              if (!fmt)
++                      return -EINVAL;
+-      cfe_fmt = find_format_by_code(format->format.code);
+-      if (!cfe_fmt)
+-              cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++              *fmt = format->format;
+-      format->format.code = cfe_fmt->code;
++              fmt = v4l2_subdev_get_pad_format(sd, state,
++                      format->pad + CSI2_NUM_CHANNELS);
++              if (!fmt)
++                      return -EINVAL;
+-      fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
+-      *fmt = format->format;
++              format->format.field = V4L2_FIELD_NONE;
+-      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;
++      } else {
++              /*
++               * Only allow changing the source pad mbus code.
++               */
++
++              struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
++              u32 sink_code;
++              u32 code;
++
++              sink_fmt = v4l2_subdev_get_pad_format(sd, state,
++                      format->pad - CSI2_NUM_CHANNELS);
++              if (!sink_fmt)
++                      return -EINVAL;
++
++              source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++              if (!source_fmt)
++                      return -EINVAL;
++
++              sink_code = sink_fmt->code;
++              code = format->format.code;
++
++              /*
++               * If the source code from the user does not match the code in
++               * the sink pad, check that the source code matches either the
++               * 16-bit version or the compressed version of the sink code.
++               */
++
++              if (code != sink_code &&
++                  (code == cfe_find_16bit_code(sink_code) ||
++                   code == cfe_find_compressed_code(sink_code)))
++                      source_fmt->code = code;
++
++              format->format.code = source_fmt->code;
+       }
+       return 0;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch b/target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch
new file mode 100644 (file)
index 0000000..202fe29
--- /dev/null
@@ -0,0 +1,104 @@
+From 214e8134842a338215831f2efa6d730f413c5ec4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:15:20 +0300
+Subject: [PATCH] media: rp1: fe: Fix pisp_fe_pad_set_fmt()
+
+pisp_fe_pad_set_fmt() allows setting the pad formats quite freely. This
+is not correct, and the function should only allow formats as supported
+by the hardware. Fix this by:
+
+Allow no format changes for FE_CONFIG_PAD and FE_STATS_PAD. They should
+always be the hardcoded initial ones.
+
+Allow setting FE_STREAM_PAD freely (but the mbus code must be
+supported), and propagate the format to the FE_OUTPUT0_PAD and
+FE_OUTPUT1_PAD pads.
+
+Allow changing the mbus code for FE_OUTPUT0_PAD and FE_OUTPUT1_PAD pads
+only if the mbus code is the compressed version of the sink side code.
+
+TODO: FE supports scaling and cropping. This should be represented here
+too?
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c    | 59 +++++++++++++++----
+ 1 file changed, 48 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -433,26 +433,63 @@ static int pisp_fe_pad_set_fmt(struct v4
+       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_SRGGB16_1X16);
+               format->format.code = cfe_fmt->code;
++              format->format.field = V4L2_FIELD_NONE;
+-              break;
++              fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++              *fmt = format->format;
+-      case FE_STATS_PAD:
+-      case FE_CONFIG_PAD:
+-              format->format.code = MEDIA_BUS_FMT_FIXED;
+-              break;
+-      }
++              fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
++              *fmt = format->format;
++
++              fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD);
++              *fmt = format->format;
++
++              return 0;
+-      fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
+-      *fmt = format->format;
++      case FE_OUTPUT0_PAD:
++      case FE_OUTPUT1_PAD: {
++              /*
++               * TODO: we should allow scaling and cropping by allowing the
++               * user to set the size here.
++               */
++              struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
++              u32 sink_code;
++              u32 code;
++
++              sink_fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++              if (!sink_fmt)
++                      return -EINVAL;
++
++              source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++              if (!source_fmt)
++                      return -EINVAL;
++
++              sink_code = sink_fmt->code;
++              code = format->format.code;
++
++              /*
++               * If the source code from the user does not match the code in
++               * the sink pad, check that the source code matches the
++               * compressed version of the sink code.
++               */
++
++              if (code != sink_code &&
++                  code == cfe_find_compressed_code(sink_code))
++                      source_fmt->code = code;
++
++              return 0;
++      }
+-      return 0;
++      case FE_CONFIG_PAD:
++      case FE_STATS_PAD:
++      default:
++              return v4l2_subdev_get_fmt(sd, state, format);
++      }
+ }
+ static int pisp_fe_link_validate(struct v4l2_subdev *sd,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch b/target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch
new file mode 100644 (file)
index 0000000..bd5789a
--- /dev/null
@@ -0,0 +1,146 @@
+From 425a6b752c38b50c97220db37a67b18b281f56e5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 15:28:20 +0300
+Subject: [PATCH] media: rp1: csi2: Use get_frame_desc to get CSI-2 VC and DT
+
+Use get_frame_desc pad op for asking the CSI-2 VC and DT from the source
+device driver, instead of hardcoding to VC 0, and getting the DT from a
+formats table. To keep backward compatibility with sources that do not
+implement get_frame_desc, implement a fallback mechanism that always
+uses VC 0, and gets the DT from the formats table, based on the CSI2's
+sink pad's format.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  |  4 +-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 75 ++++++++++++++++++-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h |  2 +-
+ 3 files changed, 77 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -838,7 +838,7 @@ static void cfe_start_channel(struct cfe
+                * 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,
++                                 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);
+@@ -872,7 +872,7 @@ static void cfe_start_channel(struct cfe
+                       }
+               }
+               /* Unconditionally start this CSI2 channel. */
+-              csi2_start_channel(&cfe->csi2, node->id, fmt->csi_dt,
++              csi2_start_channel(&cfe->csi2, node->id,
+                                  mode,
+                                  /* Auto arm */
+                                  false,
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -324,12 +324,84 @@ void csi2_set_compression(struct csi2_de
+       csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
+ }
++static int csi2_get_vc_dt_fallback(struct csi2_device *csi2,
++                                 unsigned int channel, u8 *vc, u8 *dt)
++{
++      struct v4l2_subdev *sd = &csi2->sd;
++      struct v4l2_subdev_state *state;
++      struct v4l2_mbus_framefmt *fmt;
++      const struct cfe_fmt *cfe_fmt;
++
++      state = v4l2_subdev_get_locked_active_state(sd);
++
++      /* Without Streams API, the channel number matches the sink pad */
++      fmt = v4l2_subdev_get_pad_format(sd, state, channel);
++      if (!fmt)
++              return -EINVAL;
++
++      cfe_fmt = find_format_by_code(fmt->code);
++      if (!cfe_fmt)
++              return -EINVAL;
++
++      *vc = 0;
++      *dt = cfe_fmt->csi_dt;
++
++      return 0;
++}
++
++static int csi2_get_vc_dt(struct csi2_device *csi2, unsigned int channel,
++                        u8 *vc, u8 *dt)
++{
++      struct v4l2_mbus_frame_desc remote_desc;
++      const struct media_pad *remote_pad;
++      struct v4l2_subdev *source_sd;
++      int ret;
++
++      /* Without Streams API, the channel number matches the sink pad */
++      remote_pad = media_pad_remote_pad_first(&csi2->pad[channel]);
++      if (!remote_pad)
++              return -EPIPE;
++
++      source_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++
++      ret = v4l2_subdev_call(source_sd, pad, get_frame_desc,
++                             remote_pad->index, &remote_desc);
++      if (ret == -ENOIOCTLCMD) {
++              csi2_dbg("source does not support get_frame_desc, use fallback\n");
++              return csi2_get_vc_dt_fallback(csi2, channel, vc, dt);
++      } else if (ret) {
++              csi2_err("Failed to get frame descriptor\n");
++              return ret;
++      }
++
++      if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
++              csi2_err("Frame descriptor does not describe CSI-2 link");
++              return -EINVAL;
++      }
++
++      if (remote_desc.num_entries != 1) {
++              csi2_err("Frame descriptor does not have a single entry");
++              return -EINVAL;
++      }
++
++      *vc = remote_desc.entry[0].bus.csi2.vc;
++      *dt = remote_desc.entry[0].bus.csi2.dt;
++
++      return 0;
++}
++
+ void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
+-                      u16 dt, enum csi2_mode mode, bool auto_arm,
++                      enum csi2_mode mode, bool auto_arm,
+                       bool pack_bytes, unsigned int width,
+                       unsigned int height)
+ {
+       u32 ctrl;
++      int ret;
++      u8 vc, dt;
++
++      ret = csi2_get_vc_dt(csi2, channel, &vc, &dt);
++      if (ret)
++              return;
+       csi2_dbg("%s [%u]\n", __func__, channel);
+@@ -369,6 +441,7 @@ void csi2_start_channel(struct csi2_devi
+               csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+       }
++      set_field(&ctrl, vc, VC_MASK);
+       set_field(&ctrl, dt, DT_MASK);
+       csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
+       csi2->num_lines[channel] = height;
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -79,7 +79,7 @@ void csi2_set_compression(struct csi2_de
+                         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,
++                      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);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch b/target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch
new file mode 100644 (file)
index 0000000..ad589d7
--- /dev/null
@@ -0,0 +1,91 @@
+From 2b6570e66f2769110311593f52f88dba3271a278 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 13:47:10 +0300
+Subject: [PATCH] media: rp1: cfe: Add is_image_node()
+
+The hardware supports streaming from memory (in addition to streaming
+from the CSI-2 RX), but the driver does not support this at the moment.
+
+There are multiple places in the driver which uses
+is_image_output_node(), even if the "output" part is not relevant. Thus,
+in a minor preparation for the possible support for streaming from
+memory, and to make it more obvious that the pieces of code are not
+about the "output", add is_image_node() which will return true for both
+input and output video nodes.
+
+While at it, reformat also the metadata related macros to fit inside 80
+columns.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 28 +++++++++++--------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -199,13 +199,20 @@ static const struct node_description nod
+ #define is_fe_node(node) (((node)->id) >= FE_OUT0)
+ #define is_csi2_node(node) (!is_fe_node(node))
+-#define is_image_output_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)                                                \
++#define is_image_input_node(node) \
++      (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
++#define is_image_node(node) \
++      (is_image_output_node(node) || is_image_input_node(node))
++
++#define is_meta_output_node(node) \
+       (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
+-#define is_meta_input_node(node)                                                 \
++#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))
++#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
+@@ -426,7 +433,7 @@ static int format_show(struct seq_file *
+               seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
+                          node_desc[i].name, state);
+-              if (is_image_output_node(node))
++              if (is_image_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),
+@@ -940,9 +947,8 @@ static int cfe_queue_setup(struct vb2_qu
+ {
+       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;
++      unsigned int size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
++                                                node->fmt.fmt.meta.buffersize;
+       cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+@@ -973,8 +979,8 @@ static int cfe_buffer_prepare(struct vb2
+       cfe_dbg_verbose("%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;
++      size = is_image_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);
+@@ -1757,7 +1763,7 @@ static int cfe_register_node(struct cfe_
+       node->cfe = cfe;
+       node->id = id;
+-      if (is_image_output_node(node)) {
++      if (is_image_node(node)) {
+               fmt = find_format_by_code(cfe_default_format.code);
+               if (!fmt) {
+                       cfe_err("Failed to find format code\n");
diff --git a/target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch b/target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch
new file mode 100644 (file)
index 0000000..f68aa39
--- /dev/null
@@ -0,0 +1,621 @@
+From c54b8d2fc79c684deacc81a94f6baa1cb56c62be Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 27 Sep 2023 17:18:09 +0300
+Subject: [PATCH] media: rp1: cfe: Dual purpose video nodes
+
+The RP1 CSI-2 DMA can capture both video and metadata just fine, but at
+the moment the video nodes are only set to support either video or
+metadata.
+
+Make the changes to support both video and metadata. This mostly means
+tracking both video format and metadata format separately for each video
+node, and using vb2_queue_change_type() to change the vb2 queue type
+when needed.
+
+Briefly, this means that the user can get/set both video and meta
+formats to a single video node. The vb2 queue buffer type will be
+changed when the user calls REQBUFS or CREATE_BUFS ioctls. This buffer
+type will be then used as the "mode" for the video node when the user
+starts the streaming, and based on that either the video or the meta
+format will be used.
+
+A bunch of macros are added (node_supports_xxx()), which tell if a node
+can support a particular mode, whereas the existing macros
+(is_xxx_node()) will tell if the node is currently in a particular mode.
+Note that the latter will only work correctly between the start of the
+streaming and the end of the streaming, and thus should be only used in
+those code paths.
+
+However, as the userspace (libcamera) does not support dual purpose
+video nodes, for the time being let's keep the second video node as
+V4L2_CAP_META_CAPTURE only to keep the userspace working.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 271 ++++++++++++------
+ 1 file changed, 182 insertions(+), 89 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -116,7 +116,7 @@ const struct v4l2_mbus_framefmt cfe_defa
+ enum node_ids {
+       /* CSI2 HW output nodes first. */
+       CSI2_CH0,
+-      CSI2_CH1_EMBEDDED,
++      CSI2_CH1,
+       CSI2_CH2,
+       CSI2_CH3,
+       /* FE only nodes from here on. */
+@@ -130,8 +130,7 @@ enum node_ids {
+ struct node_description {
+       unsigned int id;
+       const char *name;
+-      enum v4l2_buf_type buf_type;
+-      unsigned int cap;
++      unsigned int caps;
+       unsigned int pad_flags;
+       unsigned int link_pad;
+ };
+@@ -140,58 +139,55 @@ struct node_description {
+ 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,
++              .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_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] = {
++      /*
++       * TODO: This node should be named "csi2_ch1" and the caps should be set
++       * to both video and meta capture. However, to keep compatibility with
++       * the current libcamera, keep the name as "embedded" and support
++       * only meta capture.
++       */
++      [CSI2_CH1] = {
+               .name = "embedded",
+-              .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+-              .cap = V4L2_CAP_META_CAPTURE,
++              .caps = 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,
++              .caps = V4L2_CAP_VIDEO_CAPTURE | 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,
++              .caps = V4L2_CAP_VIDEO_CAPTURE | 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,
++              .caps = 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,
++              .caps = 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,
++              .caps = 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,
++              .caps = V4L2_CAP_META_OUTPUT,
+               .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
+               .link_pad = FE_CONFIG_PAD
+       },
+@@ -200,17 +196,29 @@ static const struct node_description nod
+ #define is_fe_node(node) (((node)->id) >= FE_OUT0)
+ #define is_csi2_node(node) (!is_fe_node(node))
++#define node_supports_image_output(node) \
++      (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE))
++#define node_supports_meta_output(node) \
++      (!!(node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE))
++#define node_supports_image_input(node) \
++      (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT))
++#define node_supports_meta_input(node) \
++      (!!(node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT))
++#define node_supports_image(node) \
++      (node_supports_image_output(node) || node_supports_image_input(node))
++#define node_supports_meta(node) \
++      (node_supports_meta_output(node) || node_supports_meta_input(node))
++
+ #define is_image_output_node(node) \
+-      (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++      ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ #define is_image_input_node(node) \
+-      (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
++      ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ #define is_image_node(node) \
+       (is_image_output_node(node) || is_image_input_node(node))
+-
+ #define is_meta_output_node(node) \
+-      (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
++      ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE)
+ #define is_meta_input_node(node) \
+-      (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_OUTPUT)
++      ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT)
+ #define is_meta_node(node) \
+       (is_meta_output_node(node) || is_meta_input_node(node))
+@@ -250,7 +258,9 @@ struct cfe_node {
+       /* Pointer pointing to next v4l2_buffer */
+       struct cfe_buffer *next_frm;
+       /* Used to store current pixel format */
+-      struct v4l2_format fmt;
++      struct v4l2_format vid_fmt;
++      /* Used to store current meta format */
++      struct v4l2_format meta_fmt;
+       /* Buffer queue used in video-buf */
+       struct vb2_queue buffer_queue;
+       /* Queue of filled frames */
+@@ -433,20 +443,21 @@ static int format_show(struct seq_file *
+               seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
+                          node_desc[i].name, state);
+-              if (is_image_node(node))
++              if (node_supports_image(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
++                                 V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat),
++                                 node->vid_fmt.fmt.pix.pixelformat,
++                                 node->vid_fmt.fmt.pix.width,
++                                 node->vid_fmt.fmt.pix.height,
++                                 node->vid_fmt.fmt.pix.bytesperline,
++                                 node->vid_fmt.fmt.pix.sizeimage);
++
++              if (node_supports_meta(node))
+                       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);
++                                 V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat),
++                                 node->meta_fmt.fmt.meta.dataformat,
++                                 node->meta_fmt.fmt.meta.buffersize);
+       }
+       return 0;
+@@ -571,11 +582,11 @@ static void cfe_schedule_next_csi2_job(s
+                               node_desc[node->id].name, &buf->vb.vb2_buf);
+               if (is_meta_node(node)) {
+-                      size = node->fmt.fmt.meta.buffersize;
++                      size = node->meta_fmt.fmt.meta.buffersize;
+                       stride = 0;
+               } else {
+-                      size = node->fmt.fmt.pix.sizeimage;
+-                      stride = node->fmt.fmt.pix.bytesperline;
++                      size = node->vid_fmt.fmt.pix.sizeimage;
++                      stride = node->vid_fmt.fmt.pix.bytesperline;
+               }
+               addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+@@ -867,10 +878,10 @@ static void cfe_start_channel(struct cfe
+                       width = source_fmt->width;
+                       height = source_fmt->height;
+-                      if (node->fmt.fmt.pix.pixelformat ==
++                      if (node->vid_fmt.fmt.pix.pixelformat ==
+                                       fmt->remap[CFE_REMAP_16BIT])
+                               mode = CSI2_MODE_REMAP;
+-                      else if (node->fmt.fmt.pix.pixelformat ==
++                      else if (node->vid_fmt.fmt.pix.pixelformat ==
+                                       fmt->remap[CFE_REMAP_COMPRESSED]) {
+                               mode = CSI2_MODE_COMPRESSED;
+                               csi2_set_compression(&cfe->csi2, node->id,
+@@ -884,7 +895,7 @@ static void cfe_start_channel(struct cfe
+                                  /* Auto arm */
+                                  false,
+                                  /* Pack bytes */
+-                                 node->id == CSI2_CH1_EMBEDDED ? true : false,
++                                 is_meta_node(node) ? true : false,
+                                  width, height);
+       }
+@@ -947,10 +958,11 @@ static int cfe_queue_setup(struct vb2_qu
+ {
+       struct cfe_node *node = vb2_get_drv_priv(vq);
+       struct cfe_device *cfe = node->cfe;
+-      unsigned int size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
+-                                                node->fmt.fmt.meta.buffersize;
++      unsigned int size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
++                                                node->meta_fmt.fmt.meta.buffersize;
+-      cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++      cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++              node->buffer_queue.type);
+       if (vq->num_buffers + *nbuffers < 3)
+               *nbuffers = 3 - vq->num_buffers;
+@@ -979,8 +991,8 @@ static int cfe_buffer_prepare(struct vb2
+       cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
+                       node_desc[node->id].name, vb);
+-      size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
+-                                   node->fmt.fmt.meta.buffersize;
++      size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
++                                   node->meta_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);
+@@ -995,8 +1007,8 @@ static int cfe_buffer_prepare(struct vb2
+               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);
++                                             &cfe->node[FE_OUT0].vid_fmt,
++                                             &cfe->node[FE_OUT1].vid_fmt);
+       }
+       return 0;
+@@ -1256,7 +1268,7 @@ static int cfe_enum_fmt_vid_cap(struct f
+       struct cfe_device *cfe = node->cfe;
+       unsigned int i, j;
+-      if (!is_image_output_node(node))
++      if (!node_supports_image_output(node))
+               return -EINVAL;
+       cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+@@ -1292,10 +1304,10 @@ static int cfe_g_fmt(struct file *file,
+       cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+-      if (f->type != node->buffer_queue.type)
++      if (!node_supports_image(node))
+               return -EINVAL;
+-      *f = node->fmt;
++      *f = node->vid_fmt;
+       return 0;
+ }
+@@ -1310,7 +1322,7 @@ static int try_fmt_vid_cap(struct cfe_no
+               f->fmt.pix.width, f->fmt.pix.height,
+               V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat));
+-      if (!is_image_output_node(node))
++      if (!node_supports_image_output(node))
+               return -EINVAL;
+       /*
+@@ -1351,11 +1363,11 @@ static int cfe_s_fmt_vid_cap(struct file
+       if (ret)
+               return ret;
+-      node->fmt = *f;
++      node->vid_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));
++              node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height,
++              V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat));
+       return 0;
+ }
+@@ -1379,11 +1391,11 @@ static int cfe_enum_fmt_meta(struct file
+       cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+-      if (!is_meta_node(node) || f->index != 0)
++      if (!node_supports_meta(node) || f->index != 0)
+               return -EINVAL;
+       switch (node->id) {
+-      case CSI2_CH1_EMBEDDED:
++      case CSI2_CH0...CSI2_CH3:
+               f->pixelformat = V4L2_META_FMT_SENSOR_DATA;
+               return 0;
+       case FE_STATS:
+@@ -1399,8 +1411,11 @@ static int cfe_enum_fmt_meta(struct file
+ static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
+ {
++      if (!node_supports_meta(node))
++              return -EINVAL;
++
+       switch (node->id) {
+-      case CSI2_CH1_EMBEDDED:
++      case CSI2_CH0...CSI2_CH3:
+               f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
+               if (!f->fmt.meta.buffersize)
+                       f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE;
+@@ -1422,6 +1437,21 @@ static int try_fmt_meta(struct cfe_node
+       return -EINVAL;
+ }
++static int cfe_g_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);
++
++      if (!node_supports_meta(node))
++              return -EINVAL;
++
++      *f = node->meta_fmt;
++
++      return 0;
++}
++
+ static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
+ {
+       struct cfe_node *node = video_drvdata(file);
+@@ -1434,17 +1464,17 @@ static int cfe_s_fmt_meta(struct file *f
+       if (vb2_is_busy(q))
+               return -EBUSY;
+-      if (f->type != node->buffer_queue.type)
++      if (!node_supports_meta(node))
+               return -EINVAL;
+       ret = try_fmt_meta(node, f);
+       if (ret)
+               return ret;
+-      node->fmt = *f;
++      node->meta_fmt = *f;
+       cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__,
+-              V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat));
++              V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat));
+       return 0;
+ }
+@@ -1491,6 +1521,52 @@ static int cfe_enum_framesizes(struct fi
+       return 0;
+ }
++static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv,
++                               struct v4l2_requestbuffers *p)
++{
++      struct video_device *vdev = video_devdata(file);
++      struct cfe_node *node = video_get_drvdata(vdev);
++      struct cfe_device *cfe = node->cfe;
++      int ret;
++
++      cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++              p->type);
++
++      if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
++          p->type != V4L2_BUF_TYPE_META_CAPTURE &&
++          p->type != V4L2_BUF_TYPE_META_OUTPUT)
++              return -EINVAL;
++
++      ret = vb2_queue_change_type(vdev->queue, p->type);
++      if (ret)
++              return ret;
++
++      return vb2_ioctl_reqbufs(file, priv, p);
++}
++
++static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv,
++                                   struct v4l2_create_buffers *p)
++{
++      struct video_device *vdev = video_devdata(file);
++      struct cfe_node *node = video_get_drvdata(vdev);
++      struct cfe_device *cfe = node->cfe;
++      int ret;
++
++      cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++              p->format.type);
++
++      if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
++          p->format.type != V4L2_BUF_TYPE_META_CAPTURE &&
++          p->format.type != V4L2_BUF_TYPE_META_OUTPUT)
++              return -EINVAL;
++
++      ret = vb2_queue_change_type(vdev->queue, p->format.type);
++      if (ret)
++              return ret;
++
++      return vb2_ioctl_create_bufs(file, priv, p);
++}
++
+ static int cfe_subscribe_event(struct v4l2_fh *fh,
+                              const struct v4l2_event_subscription *sub)
+ {
+@@ -1498,12 +1574,13 @@ static int cfe_subscribe_event(struct v4
+       switch (sub->type) {
+       case V4L2_EVENT_FRAME_SYNC:
+-              if (!is_image_output_node(node))
++              if (!node_supports_image_output(node))
+                       break;
+               return v4l2_event_subscribe(fh, sub, 2, NULL);
+       case V4L2_EVENT_SOURCE_CHANGE:
+-              if (is_meta_input_node(node))
++              if (!node_supports_image_output(node) &&
++                  !node_supports_meta_output(node))
+                       break;
+               return v4l2_event_subscribe(fh, sub, 4, NULL);
+@@ -1520,19 +1597,19 @@ static const struct v4l2_ioctl_ops cfe_i
+       .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_g_fmt_meta_cap = cfe_g_fmt_meta,
+       .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_g_fmt_meta_out = cfe_g_fmt_meta,
+       .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_reqbufs = cfe_vb2_ioctl_reqbufs,
++      .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vb2_ioctl_qbuf,
+@@ -1610,7 +1687,7 @@ static int cfe_video_link_validate(struc
+       }
+       if (is_image_output_node(node)) {
+-              struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
++              struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix;
+               const struct cfe_fmt *fmt = NULL;
+               unsigned int i;
+@@ -1636,8 +1713,8 @@ static int cfe_video_link_validate(struc
+                       ret = -EINVAL;
+                       goto out;
+               }
+-      } else if (node->id == CSI2_CH1_EMBEDDED) {
+-              struct v4l2_meta_format *meta_fmt = &node->fmt.fmt.meta;
++      } else if (is_csi2_node(node) && is_meta_output_node(node)) {
++              struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
+               if (source_fmt->width * source_fmt->height !=
+                                                       meta_fmt->buffersize ||
+@@ -1698,15 +1775,17 @@ static int cfe_video_link_notify(struct
+       if (link->source->entity != csi2)
+               return 0;
+-      if (link->sink->index != 0)
++      if (link->sink->entity != fe)
+               return 0;
+-      if (link->source->index == node_desc[CSI2_CH1_EMBEDDED].link_pad)
++      if (link->sink->index != 0)
+               return 0;
+       cfe->fe_csi2_channel = -1;
+-      if (link->sink->entity == fe && (link->flags & MEDIA_LNK_FL_ENABLED)) {
++      if (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_CH1].link_pad)
++                      cfe->fe_csi2_channel = CSI2_CH1;
+               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)
+@@ -1763,30 +1842,42 @@ static int cfe_register_node(struct cfe_
+       node->cfe = cfe;
+       node->id = id;
+-      if (is_image_node(node)) {
++      if (node_supports_image(node)) {
++              if (node_supports_image_output(node))
++                      node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++              else
++                      node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++
+               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);
++              node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc;
++              v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, &cfe_default_format);
+-              ret = try_fmt_vid_cap(node, &node->fmt);
++              ret = try_fmt_vid_cap(node, &node->vid_fmt);
+               if (ret)
+                       return ret;
+-      } else {
+-              ret = try_fmt_meta(node, &node->fmt);
++      }
++
++      if (node_supports_meta(node)) {
++              if (node_supports_meta_output(node))
++                      node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
++              else
++                      node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT;
++
++              ret = try_fmt_meta(node, &node->meta_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->type = node_supports_image(node) ? node->vid_fmt.type :
++                                            node->meta_fmt.type;
+       q->io_modes = VB2_MMAP | VB2_DMABUF;
+       q->drv_priv = node;
+       q->ops = &cfe_video_qops;
+@@ -1812,11 +1903,13 @@ static int cfe_register_node(struct cfe_
+       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->vfl_dir = (node_supports_image_output(node) ||
++                       node_supports_meta_output(node)) ?
++                              VFL_DIR_RX :
++                              VFL_DIR_TX;
+       vdev->queue = q;
+       vdev->lock = &node->lock;
+-      vdev->device_caps = node_desc[id].cap;
++      vdev->device_caps = node_desc[id].caps;
+       vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+       /* Define the device names */
+@@ -1829,7 +1922,7 @@ static int cfe_register_node(struct cfe_
+       node->pad.flags = node_desc[id].pad_flags;
+       media_entity_pads_init(&vdev->entity, 1, &node->pad);
+-      if (is_meta_node(node)) {
++      if (!node_supports_image(node)) {
+               v4l2_disable_ioctl(&node->video_dev,
+                                  VIDIOC_ENUM_FRAMEINTERVALS);
+               v4l2_disable_ioctl(&node->video_dev,
+@@ -1907,7 +2000,7 @@ static int cfe_link_node_pads(struct cfe
+               if (ret)
+                       return ret;
+-              if (node->id != CSI2_CH1_EMBEDDED) {
++              if (node_supports_image(node)) {
+                       /* CSI2 channel # -> FE Input */
+                       ret = media_create_pad_link(&cfe->csi2.sd.entity,
+                                                   node_desc[i].link_pad,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch b/target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch
new file mode 100644 (file)
index 0000000..1d6e396
--- /dev/null
@@ -0,0 +1,127 @@
+From dad296088dffbaf55c1e61cbdc3f7cb1eb504ca6 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 16 May 2023 15:51:54 +0300
+Subject: [PATCH] media: rp1: Drop LE handling
+
+The driver registers for line-end interrupts, but never uses them. This
+just causes extra interrupt load, with more complexity in the driver.
+
+Drop the LE handling. It can easily be added back if later needed.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  |  6 ++--
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 28 ++++---------------
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h |  2 +-
+ 3 files changed, 10 insertions(+), 26 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -734,13 +734,13 @@ static irqreturn_t cfe_isr(int irq, void
+ {
+       struct cfe_device *cfe = dev;
+       unsigned int i;
+-      bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}, lci[NUM_NODES] = {0};
++      bool sof[NUM_NODES] = {0}, eof[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);
++              csi2_isr(&cfe->csi2, sof, eof);
+       if (sts & MIPICFG_INT_PISP_FE)
+               pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
+@@ -757,7 +757,7 @@ static irqreturn_t cfe_isr(int irq, void
+                * generate interrupts even though the node is not streaming.
+                */
+               if (!check_state(cfe, NODE_STREAMING, i) ||
+-                  !(sof[i] || eof[i] || lci[i]))
++                  !(sof[i] || eof[i]))
+                       continue;
+               /*
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -258,7 +258,7 @@ static void csi2_isr_handle_errors(struc
+       spin_unlock(&csi2->errors_lock);
+ }
+-void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof)
+ {
+       unsigned int i;
+       u32 status;
+@@ -290,7 +290,6 @@ void csi2_isr(struct csi2_device *csi2,
+               sof[i] = !!(status & IRQ_FS(i));
+               eof[i] = !!(status & IRQ_FE_ACK(i));
+-              lci[i] = !!(status & IRQ_LE_ACK(i));
+       }
+       if (csi2_track_errors)
+@@ -405,16 +404,12 @@ void csi2_start_channel(struct csi2_devi
+       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_CTRL(channel), 0);
+       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;
++      /* Enable channel and FS/FE interrupts. */
++      ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | PACK_LINE;
+       /* PACK_BYTES ensures no striding for embedded data. */
+       if (pack_bytes)
+               ctrl |= PACK_BYTES;
+@@ -423,21 +418,11 @@ void csi2_start_channel(struct csi2_devi
+               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, 0x0, CH_MODE_MASK);
+               csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+       }
+@@ -452,8 +437,7 @@ void csi2_stop_channel(struct csi2_devic
+       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);
++      csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 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! */
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -71,7 +71,7 @@ struct csi2_device {
+       u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+ };
+-void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof);
+ void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+                    dma_addr_t dmaaddr, unsigned int stride,
+                    unsigned int size);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch b/target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch
new file mode 100644 (file)
index 0000000..a383615
--- /dev/null
@@ -0,0 +1,75 @@
+From b6316a8450d3cb99b7599175d59b1b7f710770f5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 13:59:02 +0300
+Subject: [PATCH] media: rp1: csi2: Use standard link_validate
+
+The current csi2_link_validate() skips some important checks. Let's
+rather use the standard v4l2_subdev_link_validate_default() as the
+link_validate hook.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 41 +------------------
+ 1 file changed, 1 insertion(+), 40 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -462,11 +462,6 @@ void csi2_close_rx(struct csi2_device *c
+       csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
+ }
+-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)
+ {
+@@ -554,45 +549,11 @@ static int csi2_pad_set_fmt(struct v4l2_
+       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,
++      .link_validate = v4l2_subdev_link_validate_default,
+ };
+ static const struct media_entity_operations csi2_entity_ops = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch b/target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch
new file mode 100644 (file)
index 0000000..d36ea5b
--- /dev/null
@@ -0,0 +1,70 @@
+From 0eeb351222adbc5b534c86f7815ee787babc3485 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 14:34:43 +0300
+Subject: [PATCH] media: rp1: fe: Use standard link_validate
+
+The current pisp_fe_link_validate() skips some important checks. Let's
+rather use the standard v4l2_subdev_link_validate_default() as the
+link_validate hook.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c    | 36 +------------------
+ 1 file changed, 1 insertion(+), 35 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -388,11 +388,6 @@ void pisp_fe_stop(struct pisp_fe_device
+       pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ }
+-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)
+ {
+@@ -492,40 +487,11 @@ static int pisp_fe_pad_set_fmt(struct v4
+       }
+ }
+-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,
++      .link_validate = v4l2_subdev_link_validate_default,
+ };
+ static const struct media_entity_operations pisp_fe_entity_ops = {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch b/target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch
new file mode 100644 (file)
index 0000000..d85a90a
--- /dev/null
@@ -0,0 +1,61 @@
+From e0f52ccfe1e383622fb30708acd38921e84fbff4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 14:29:44 +0300
+Subject: [PATCH] media: rp1: cfe: Improve link validation for metadata
+
+Improve the link validation for metadata by:
+- Allowing capture buffers that are larger than the incoming frame
+  (instead of requiring exact match).
+
+- Instead of assuming that a metadata unit ("pixel") is 8 bits, use
+  find_format_by_code() to get the format and use the bit depth from
+  there. E.g. bit depth for RAW10 metadata will be 10 bits, when we
+  move to the upstream metadata formats.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 32 +++++++++++++------
+ 1 file changed, 22 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1715,17 +1715,29 @@ static int cfe_video_link_validate(struc
+               }
+       } else if (is_csi2_node(node) && is_meta_output_node(node)) {
+               struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
++              const struct cfe_fmt *fmt;
++              u32 source_size;
+-              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 */
++              fmt = find_format_by_code(source_fmt->code);
++              if (!fmt || fmt->fourcc != meta_fmt->dataformat) {
++                      cfe_err("Metadata format mismatch!\n");
++                      ret = -EINVAL;
++                      goto out;
++              }
++
++              source_size = DIV_ROUND_UP(source_fmt->width * source_fmt->height * fmt->depth, 8);
++
++              if (source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) {
++                      cfe_err("Bad metadata mbus format\n");
++                      ret = -EINVAL;
++                      goto out;
++              }
++
++              if (source_size > meta_fmt->buffersize) {
++                      cfe_err("Metadata buffer too small: %u < %u\n",
++                              meta_fmt->buffersize, source_size);
++                      ret = -EINVAL;
++                      goto out;
+               }
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch b/target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch
new file mode 100644 (file)
index 0000000..c3e4140
--- /dev/null
@@ -0,0 +1,24 @@
+From 046d03c87ecce5db074eae6ffa2d5298c5f7d5a7 Mon Sep 17 00:00:00 2001
+From: Leon Anavi <leon.anavi@konsulko.com>
+Date: Tue, 12 Dec 2023 13:53:37 +0200
+Subject: [PATCH] drivers/pinctrl/bcm/Kconfig: Fix BCM2712 help
+
+Replace "Broadcom BCM2835 GPIO" with "Broadcom BCM2712 PINCONF"
+in the help message. This work was sponsored by GOVCERT.LU.
+
+Signed-off-by: Leon Anavi <leon.anavi@konsulko.com>
+---
+ drivers/pinctrl/bcm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pinctrl/bcm/Kconfig
++++ b/drivers/pinctrl/bcm/Kconfig
+@@ -10,7 +10,7 @@ config PINCTRL_BCM2712
+       select PINCONF
+       select GENERIC_PINCONF
+       help
+-         Say Y here to enable the Broadcom BCM2835 GPIO driver.
++         Say Y here to enable the Broadcom BCM2712 PINCONF driver.
+ config PINCTRL_BCM281XX
+       bool "Broadcom BCM281xx pinctrl driver"
diff --git a/target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch b/target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch
new file mode 100644 (file)
index 0000000..aeb6358
--- /dev/null
@@ -0,0 +1,41 @@
+From a6872b25f18fe46ef9979f9d4a3635a9c3966afd Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Mon, 11 Dec 2023 15:06:45 +0800
+Subject: [PATCH] drivers/gpu/drm/panel:fix waveshare panel software
+ restart/shutdown display is abnormal Fixed the screen stays white when the
+ user restarts or shuts down
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -362,9 +362,18 @@ static void ws_panel_remove(struct i2c_c
+ {
+       struct ws_panel *ts = i2c_get_clientdata(i2c);
++      ws_panel_disable(&ts->base);
++
+       drm_panel_remove(&ts->base);
+ }
++static void ws_panel_shutdown(struct i2c_client *i2c)
++{
++      struct ws_panel *ts = i2c_get_clientdata(i2c);
++
++      ws_panel_disable(&ts->base);
++}
++
+ static const struct of_device_id ws_panel_of_ids[] = {
+       {
+               .compatible = "waveshare,2.8inch-panel",
+@@ -403,6 +412,7 @@ static struct i2c_driver ws_panel_driver
+       },
+       .probe = ws_panel_probe,
+       .remove = ws_panel_remove,
++      .shutdown = ws_panel_shutdown,
+ };
+ module_i2c_driver(ws_panel_driver);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch b/target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch
new file mode 100644 (file)
index 0000000..7feeb22
--- /dev/null
@@ -0,0 +1,33 @@
+From 6988ae7c909dc342322a82daaa3a95b78d038305 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 Dec 2023 16:58:07 +0000
+Subject: [PATCH] firmware/psci: Pass given partition number through
+
+Pi 5 uses BL31 as its armstub file, so the reset goes via PSCI. Parse
+any "reboot" parameter as a partition number to reboot into.
+N.B. This code path is only used if reboot mode has been set to warm
+or soft.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/firmware/psci/psci.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/firmware/psci/psci.c
++++ b/drivers/firmware/psci/psci.c
+@@ -314,7 +314,14 @@ static int psci_sys_reset(struct notifie
+                * reset_type[30:0] = 0 (SYSTEM_WARM_RESET)
+                * cookie = 0 (ignored by the implementation)
+                */
+-              invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0);
++              // Allow extra arguments separated by spaces after
++              // the partition number.
++              unsigned long val;
++              u8 partition = 0;
++
++              if (data && sscanf(data, "%lu", &val) == 1 && val < 63)
++                      partition = val;
++              invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, partition, 0);
+       } else {
+               invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+       }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch
new file mode 100644 (file)
index 0000000..8bcf587
--- /dev/null
@@ -0,0 +1,23 @@
+From 5d87d7f91cb4f1d0f391f6fe9dd0524b363b78e3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 Dec 2023 17:00:56 +0000
+Subject: [PATCH] dts: bcm2712-rpi-5-b: Enable warm reboot mode
+
+Switch to warm reboot mode so that the partition number is preserved.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -428,7 +428,7 @@ dpi_16bit_gpio2:        &rp1_dpi_16bit_g
+ / {
+       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";
++              bootargs = "reboot=w 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";
+       };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch b/target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch
new file mode 100644 (file)
index 0000000..be1eb56
--- /dev/null
@@ -0,0 +1,85 @@
+From 083f39e40d980b47ab12b451d40b9f935bb22a5b Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 6 Dec 2023 14:27:57 +0000
+Subject: [PATCH] drivers: media: i2c: imx296,imx477: Configure tigger_mode
+ every time
+
+Don't assume the camera has been reset each time we start streaming,
+but always write registers relating to trigger_mode, even in mode 0.
+
+IMX477: Stop driving XVS on stop streaming, to avoid spurious pulses.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/media/i2c/imx296.c |  9 +++++----
+ drivers/media/i2c/imx477.c | 29 ++++++++++++++---------------
+ 2 files changed, 19 insertions(+), 19 deletions(-)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -650,10 +650,11 @@ 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);
+-      }
++      /* external trigger mode: 0=normal, 1=triggered */
++      imx296_write(sensor, IMX296_CTRL0B,
++                   (trigger_mode == 1) ? IMX296_CTRL0B_TRIGEN : 0, &ret);
++      imx296_write(sensor, IMX296_LOWLAGTRG,
++                   (trigger_mode == 1) ? IMX296_LOWLAGTRG_FAST : 0, &ret);
+       imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -1742,26 +1742,21 @@ static int imx477_start_streaming(struct
+       imx477_write_reg(imx477, 0x0b05, IMX477_REG_VALUE_08BIT, !!dpc_enable);
+       imx477_write_reg(imx477, 0x0b06, IMX477_REG_VALUE_08BIT, !!dpc_enable);
+-      /* Set vsync trigger mode */
+-      if (trigger_mode != 0) {
+-              /* trigger_mode == 1 for source, 2 for sink */
+-              const u32 val = (trigger_mode == 1) ? 1 : 0;
+-
+-              imx477_write_reg(imx477, IMX477_REG_MC_MODE,
+-                               IMX477_REG_VALUE_08BIT, 1);
+-              imx477_write_reg(imx477, IMX477_REG_MS_SEL,
+-                               IMX477_REG_VALUE_08BIT, val);
+-              imx477_write_reg(imx477, IMX477_REG_XVS_IO_CTRL,
+-                               IMX477_REG_VALUE_08BIT, val);
+-              imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
+-                               IMX477_REG_VALUE_08BIT, val);
+-      }
+-
+       /* Apply customized values from user */
+       ret =  __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler);
+       if (ret)
+               return ret;
++      /* Set vsync trigger mode: 0=standalone, 1=source, 2=sink */
++      imx477_write_reg(imx477, IMX477_REG_MC_MODE,
++                       IMX477_REG_VALUE_08BIT, (trigger_mode > 0) ? 1 : 0);
++      imx477_write_reg(imx477, IMX477_REG_MS_SEL,
++                       IMX477_REG_VALUE_08BIT, (trigger_mode <= 1) ? 1 : 0);
++      imx477_write_reg(imx477, IMX477_REG_XVS_IO_CTRL,
++                       IMX477_REG_VALUE_08BIT, (trigger_mode == 1) ? 1 : 0);
++      imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
++                       IMX477_REG_VALUE_08BIT, (trigger_mode == 1) ? 1 : 0);
++
+       /* set stream on register */
+       return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
+                               IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING);
+@@ -1778,6 +1773,10 @@ static void imx477_stop_streaming(struct
+                              IMX477_REG_VALUE_08BIT, IMX477_MODE_STANDBY);
+       if (ret)
+               dev_err(&client->dev, "%s failed to set stream\n", __func__);
++
++      /* Stop driving XVS out (there is still a weak pull-up) */
++      imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
++                       IMX477_REG_VALUE_08BIT, 0);
+ }
+ static int imx477_set_stream(struct v4l2_subdev *sd, int enable)
diff --git a/target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch b/target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch
new file mode 100644 (file)
index 0000000..d705df6
--- /dev/null
@@ -0,0 +1,99 @@
+From 3ed57f25f3074f6abfe570fc11aa7d370fdc499a Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 6 Dec 2023 14:38:03 +0000
+Subject: [PATCH] overlays: Add "always-on" parameter to imx477 and imx296
+
+Leave the camera's power supplies up, to prevent the camera
+clamping its 1.8V digital I/Os to ground. This may be useful
+when synchronizing multiple camera systems using XVS or XTRIG.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README                  | 6 ++++++
+ arch/arm/boot/dts/overlays/imx296-overlay.dts      | 9 +++++++++
+ arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 9 +++++++++
+ 3 files changed, 24 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2520,6 +2520,8 @@ Params: rotation                Mounting
+         clock-frequency         Sets the clock frequency to match that used on
+                                 the board, which should be one of 54000000
+                                 (the default), 37125000 or 74250000.
++        always-on               Leave the regulator powered up, to stop the
++                                camera clamping I/Os such as XTRIG to 0V.
+ Name:   imx327
+@@ -2558,6 +2560,8 @@ Params: rotation                Mounting
+                                 configuring the sensor (default on)
+         cam0                    Adopt the default configuration for CAM0 on a
+                                 Compute Module (CSI0, i2c_vc, and cam0_reg).
++        always-on               Leave the regulator powered up, to stop the
++                                camera clamping I/Os such as XVS to 0V.
+ Name:   imx462
+@@ -2596,6 +2600,8 @@ Params: rotation                Mounting
+                                 configuring the sensor (default on)
+         cam0                    Adopt the default configuration for CAM0 on a
+                                 Compute Module (CSI0, i2c_vc, and cam0_reg).
++        always-on               Leave the regulator powered up, to stop the
++                                camera clamping I/Os such as XVS to 0V.
+ Name:   imx519
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -37,6 +37,13 @@
+               };
+       };
++      reg_alwayson_frag: fragment@99 {
++              target = <&cam1_reg>;
++              __dormant__ {
++                      regulator-always-on;
++              };
++      };
++
+       i2c_frag: fragment@100 {
+               target = <&i2c_csi_dsi>;
+               __overlay__ {
+@@ -98,8 +105,10 @@
+                      <&csi_frag>, "target:0=",<&csi0>,
+                      <&clk_frag>, "target:0=",<&cam0_clk>,
+                      <&reg_frag>, "target:0=",<&cam0_reg>,
++                     <&reg_alwayson_frag>, "target:0=",<&cam0_reg>,
+                      <&imx296>, "clocks:0=",<&cam0_clk>,
+                      <&imx296>, "avdd-supply:0=",<&cam0_reg>;
+               clock-frequency = <&clk_over>, "clock-frequency:0";
++              always-on = <0>, "+99";
+       };
+ };
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -33,6 +33,13 @@
+               };
+       };
++      reg_alwayson_frag: fragment@99 {
++              target = <&cam1_reg>;
++              __dormant__ {
++                      regulator-always-on;
++              };
++      };
++
+       i2c_frag: fragment@100 {
+               target = <&i2c_csi_dsi>;
+               __overlay__ {
+@@ -69,8 +76,10 @@
+                      <&csi_frag>, "target:0=",<&csi0>,
+                      <&clk_frag>, "target:0=",<&cam0_clk>,
+                      <&reg_frag>, "target:0=",<&cam0_reg>,
++                     <&reg_alwayson_frag>, "target:0=",<&cam0_reg>,
+                      <&cam_node>, "clocks:0=",<&cam0_clk>,
+                      <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++              always-on = <0>, "+99";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch b/target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch
new file mode 100644 (file)
index 0000000..6fb8fc0
--- /dev/null
@@ -0,0 +1,29 @@
+From e0ecfaf1abfd5ef63ceec7606352020a80a2742b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 18 Dec 2023 11:49:36 +0000
+Subject: [PATCH] input: edt-ft5x06: Correct prefix length in snprintf
+
+snprintf takes the length of the array that we can print into,
+and has to fit the NULL terminator in there too.
+Printing the prefix is generally "12-3456 " which is 8 desired
+characters (the length of EDT_NAME_PREFIX_LEN) and the NULL.
+The space is therefore being truncated to fit the NULL in.
+
+Increase the length snprintf is allowed to use.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -966,7 +966,7 @@ static int edt_ft5x06_ts_identify(struct
+       char *model_name = tsdata->name;
+       char *fw_version = tsdata->fw_version;
+-      snprintf(model_name, EDT_NAME_PREFIX_LEN, "%s ", dev_name(&client->dev));
++      snprintf(model_name, EDT_NAME_PREFIX_LEN + 1, "%s ", dev_name(&client->dev));
+       model_name += strlen(model_name);
+       /* see what we find if we assume it is a M06 *
diff --git a/target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch b/target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch
new file mode 100644 (file)
index 0000000..5a3e6df
--- /dev/null
@@ -0,0 +1,38 @@
+From da5fd98469edd797ed77d9a8690a608c6b54f9e6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 14:55:21 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Allow RTC to be disabled
+
+Add a dtparam "rtc", so that "dtparam=rtc=off" can be used to disable
+the Pi 5's onboard RTC.
+
+See: https://forums.raspberrypi.com/viewtopic.php?t=361813
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ arch/arm/boot/dts/overlays/README     | 3 +++
+ 2 files changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -819,6 +819,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               pciex1_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+               pcie_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+               random = <&random>, "status";
++              rtc = <&rpi_rtc>, "status";
+               rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
+               spi = <&spi0>, "status";
+               suspend = <&pwr_key>, "linux,code:0=205";
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -307,6 +307,9 @@ Params:
+         random                  Set to "on" to enable the hardware random
+                                 number generator (default "on")
++        rtc                     Set to "off" to disable the onboard Real Time
++                                Clock (2712 only, 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.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch b/target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch
new file mode 100644 (file)
index 0000000..ccba16a
--- /dev/null
@@ -0,0 +1,57 @@
+From 0a09088e24c013ef608b1bb79501ef890cefc767 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 11:16:25 +0000
+Subject: [PATCH] i2c: designware: Look for *CNT values in DT
+
+The i2c-designware driver supports reading precise timing values from
+ACPI, but the Device Tree support relies on a combination of standard
+rise and fall times and hard-coded minimum timings. The result of this
+is that it is difficult to get optimum timings, particularly given that
+the values are bus speed-specific and only one set can be stored in
+DT at a time.
+
+Add support for initialisation from DT that is similar to that for
+ACPI.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/i2c/busses/i2c-designware-platdrv.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-designware-platdrv.c
++++ b/drivers/i2c/busses/i2c-designware-platdrv.c
+@@ -132,9 +132,18 @@ static int mscc_twi_set_sda_hold_time(st
+       return 0;
+ }
++static void dw_i2c_read_of_cnt(struct device_node *np, const char *name, u16 *pval)
++{
++      u32 val;
++
++      if (!of_property_read_u32(np, name, &val))
++              *pval = (u16)val;
++}
++
+ static int dw_i2c_of_configure(struct platform_device *pdev)
+ {
+       struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
++      struct device_node *np = pdev->dev.of_node;
+       switch (dev->flags & MODEL_MASK) {
+       case MODEL_MSCC_OCELOT:
+@@ -146,6 +155,15 @@ static int dw_i2c_of_configure(struct pl
+               break;
+       }
++      dw_i2c_read_of_cnt(np, "snps,ss_hcnt", &dev->ss_hcnt);
++      dw_i2c_read_of_cnt(np, "snps,ss_lcnt", &dev->ss_lcnt);
++      dw_i2c_read_of_cnt(np, "snps,fs_hcnt", &dev->fs_hcnt);
++      dw_i2c_read_of_cnt(np, "snps,fs_lcnt", &dev->fs_lcnt);
++      dw_i2c_read_of_cnt(np, "snps,fp_hcnt", &dev->fp_hcnt);
++      dw_i2c_read_of_cnt(np, "snps,fp_lcnt", &dev->fp_lcnt);
++      dw_i2c_read_of_cnt(np, "snps,hs_hcnt", &dev->hs_hcnt);
++      dw_i2c_read_of_cnt(np, "snps,hs_lcnt", &dev->hs_lcnt);
++
+       return 0;
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch b/target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch
new file mode 100644 (file)
index 0000000..892f31e
--- /dev/null
@@ -0,0 +1,103 @@
+From 660d569b1a623e4b64350e608bbf8bc2cc6332e9 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 11:27:20 +0000
+Subject: [PATCH] dts: rp1: Add I2C timings
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/rp1.dtsi | 42 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -305,6 +305,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C0 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -313,6 +319,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C1 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -321,6 +333,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C2 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -329,6 +347,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C3 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -337,6 +361,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C4 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -345,6 +375,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C5 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
+@@ -353,6 +389,12 @@
+                       compatible = "snps,designware-i2c";
+                       interrupts = <RP1_INT_I2C6 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&rp1_clocks RP1_CLK_SYS>;
++                      snps,ss_hcnt = <978>;
++                      snps,ss_lcnt = <990>;
++                      snps,fs_hcnt = <200>;
++                      snps,fs_lcnt = <268>;
++                      snps,fp_hcnt = <60>;
++                      snps,fp_lcnt = <107>;
+                       status = "disabled";
+               };
diff --git a/target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch b/target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch
new file mode 100644 (file)
index 0000000..fc77e4d
--- /dev/null
@@ -0,0 +1,58 @@
+From 74c6c159c2b499bdf3c39961bd40eb9243624226 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 29 Nov 2023 13:09:05 +0000
+Subject: [PATCH] drivers: media: pisp_be: pisp_fe: Update UAPI header licenses
+
+Update the license tags on the pisp UAPI header files with the
+"Linux-syscall-note" clause.  Also replace the "GPL-2.0" tag with the
+preferred "GPL-2.0-only" tag.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h  | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h     | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h  | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h      | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+  * PiSP Back End configuration definitions.
+  *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+  * RP1 PiSP common definitions.
+  *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+  * RP1 PiSP Front End Driver Configuration structures
+  *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+  * RP1 PiSP Front End statistics definitions
+  *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+  * RP1 PiSP Front End image definitions.
+  *
diff --git a/target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch b/target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch
new file mode 100644 (file)
index 0000000..b79cad4
--- /dev/null
@@ -0,0 +1,207 @@
+From 6fb7a0b4c1dd6cf5b12ec2b2c197dcf8e58cd2b9 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 18 Dec 2023 09:52:45 +0000
+Subject: [PATCH] drivers: media: cfe: Add more robust ISR handlers
+
+Update the ISR logic to be more robust to sensors in problematic states
+where interrupts may start arriving overlapped and/or missing.
+
+1) Test for cur_frame in the FE handler, and if present, dequeue it in
+an error state so that it does not get orphaned.
+
+2) Move the sequence counter and timestamp variables to the node
+structures.  This allows the ISR to track channels running ahead when
+interrupts arrive unordered.
+
+3) Add a test to ensure we don't have a spurios (but harmlesS) call to
+the FE handler in some circumstances.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c  | 92 ++++++++++---------
+ 1 file changed, 49 insertions(+), 43 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -272,6 +272,8 @@ struct cfe_node {
+       /* Pointer to the parent handle */
+       struct cfe_device *cfe;
+       struct media_pad pad;
++      unsigned int fs_count;
++      u64 ts;
+ };
+ struct cfe_device {
+@@ -311,9 +313,6 @@ struct cfe_device {
+       struct pisp_fe_device fe;
+       int fe_csi2_channel;
+-
+-      unsigned int sequence;
+-      u64 ts;
+ };
+ static inline bool is_fe_enabled(struct cfe_device *cfe)
+@@ -393,17 +392,6 @@ static bool test_all_nodes(struct cfe_de
+       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;
+@@ -656,22 +644,22 @@ static void cfe_prepare_next_job(struct
+ }
+ static void cfe_process_buffer_complete(struct cfe_node *node,
+-                                      unsigned int sequence)
++                                      enum vb2_buffer_state state)
+ {
+       struct cfe_device *cfe = node->cfe;
+       cfe_dbg_verbose("%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);
++      node->cur_frm->vb.sequence = node->fs_count - 1;
++      vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
+ }
+ 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,
++              .u.frame_sync.frame_sequence = node->fs_count - 1,
+       };
+       v4l2_event_queue(&node->video_dev, &event);
+@@ -680,28 +668,53 @@ static void cfe_queue_event_sof(struct c
+ static void cfe_sof_isr_handler(struct cfe_node *node)
+ {
+       struct cfe_device *cfe = node->cfe;
++      bool matching_fs = true;
++      unsigned int i;
+       cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+-                      cfe->sequence);
+-
+-      node->cur_frm = node->next_frm;
+-      node->next_frm = NULL;
++                      node->fs_count);
+       /*
+-       * If this is the first node to see a frame start,  sample the
+-       * timestamp to use for all frames across all channels.
++       * If the sensor is producing unexpected frame event ordering over a
++       * sustained period of time, guard against the possibility of coming
++       * here and orphaning the cur_frm if it's not been dequeued already.
++       * Unfortunately, there is not enough hardware state to tell if this
++       * may have occurred.
+        */
+-      if (!test_any_node(cfe, NODE_STREAMING | FS_INT))
+-              cfe->ts = ktime_get_ns();
++      if (WARN(node->cur_frm, "%s: [%s] Orphanded frame at seq %u\n",
++               __func__, node_desc[node->id].name, node->fs_count))
++              cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR);
+-      set_state(cfe, FS_INT, node->id);
++      node->cur_frm = node->next_frm;
++      node->next_frm = NULL;
++      node->fs_count++;
+-      /* If all nodes have seen a frame start, we can queue another job. */
+-      if (test_all_nodes(cfe, NODE_STREAMING, FS_INT))
++      node->ts = ktime_get_ns();
++      for (i = 0; i < NUM_NODES; i++) {
++              if (!check_state(cfe, NODE_STREAMING, i) || i == node->id)
++                      continue;
++              /*
++               * This checks if any other node has seen a FS. If yes, use the
++               * same timestamp, eventually across all node buffers.
++               */
++              if (cfe->node[i].fs_count >= node->fs_count)
++                      node->ts = cfe->node[i].ts;
++              /*
++               * This checks if all other node have seen a matching FS. If
++               * yes, we can flag another job to be queued.
++               */
++              if (matching_fs && cfe->node[i].fs_count != node->fs_count)
++                      matching_fs = false;
++      }
++
++      if (matching_fs)
+               cfe->job_queued = false;
+       if (node->cur_frm)
+-              node->cur_frm->vb.vb2_buf.timestamp = cfe->ts;
++              node->cur_frm->vb.vb2_buf.timestamp = node->ts;
++
++      set_state(cfe, FS_INT, node->id);
++      clear_state(cfe, FE_INT, node->id);
+       if (is_image_output_node(node))
+               cfe_queue_event_sof(node);
+@@ -712,22 +725,14 @@ static void cfe_eof_isr_handler(struct c
+       struct cfe_device *cfe = node->cfe;
+       cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+-                      cfe->sequence);
++                      node->fs_count - 1);
+       if (node->cur_frm)
+-              cfe_process_buffer_complete(node, cfe->sequence);
++              cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE);
+       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);
+-      }
++      clear_state(cfe, FS_INT, node->id);
+ }
+ static irqreturn_t cfe_isr(int irq, void *dev)
+@@ -794,7 +799,8 @@ static irqreturn_t cfe_isr(int irq, void
+                        * frame first before the FS handler for the current
+                        * frame.
+                        */
+-                      if (check_state(cfe, FS_INT, node->id)) {
++                      if (check_state(cfe, FS_INT, node->id) &&
++                          !check_state(cfe, FE_INT, node->id)) {
+                               cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n",
+                                       __func__, node_desc[node->id].name);
+                               cfe_eof_isr_handler(node);
+@@ -1131,6 +1137,7 @@ static int cfe_start_streaming(struct vb
+       clear_state(cfe, FS_INT | FE_INT, node->id);
+       set_state(cfe, NODE_STREAMING, node->id);
++      node->fs_count = 0;
+       cfe_start_channel(node);
+       if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
+@@ -1166,7 +1173,6 @@ static int cfe_start_streaming(struct vb
+       csi2_open_rx(&cfe->csi2);
+       cfe_dbg("Starting sensor streaming\n");
+-      cfe->sequence = 0;
+       ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
+       if (ret < 0) {
+               cfe_err("stream on failed in subdev\n");
diff --git a/target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch b/target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch
new file mode 100644 (file)
index 0000000..c187507
--- /dev/null
@@ -0,0 +1,89 @@
+From dfc04900c40eb14f9364d56e96db2cc3340a1f21 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 3 Jan 2024 14:43:43 +0000
+Subject: [PATCH] ASoC: dwc: Defer bclk_ratio handling to hw_params
+
+bclk_ratio is only a factor in clock producer mode, and needs to
+override the default value of num_channels * sample_size.
+Move the bclk_ratio handling into the hw_params method, only latching
+the value in set_bclk_ratio, to address both of those matters.
+
+See: https://github.com/raspberrypi/linux/issues/5817
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 38 +++++++++++++++++++++-----------------
+ sound/soc/dwc/local.h   |  1 +
+ 2 files changed, 22 insertions(+), 17 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -263,6 +263,25 @@ static int dw_i2s_hw_params(struct snd_p
+               return -EINVAL;
+       }
++      if ((dev->capability & DW_I2S_MASTER) && dev->bclk_ratio) {
++              switch (dev->bclk_ratio) {
++              case 32:
++                      dev->ccr = 0x00;
++                      break;
++
++              case 48:
++                      dev->ccr = 0x08;
++                      break;
++
++              case 64:
++                      dev->ccr = 0x10;
++                      break;
++
++              default:
++                      return -EINVAL;
++              }
++      }
++
+       config->chan_nr = params_channels(params);
+       switch (config->chan_nr) {
+@@ -436,23 +455,7 @@ static int dw_i2s_set_bclk_ratio(struct
+       dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
+-      switch (ratio) {
+-      case 32:
+-              dev->ccr = 0x00;
+-              break;
+-
+-      case 48:
+-              dev->ccr = 0x08;
+-              break;
+-
+-      case 64:
+-              dev->ccr = 0x10;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
++      dev->bclk_ratio = ratio;
+       return 0;
+ }
+@@ -746,6 +749,7 @@ static int dw_i2s_probe(struct platform_
+               }
+       }
++      dev->bclk_ratio = 0;
+       dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+       dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+       if (pdata) {
+--- a/sound/soc/dwc/local.h
++++ b/sound/soc/dwc/local.h
+@@ -107,6 +107,7 @@ struct dw_i2s_dev {
+       unsigned int quirks;
+       unsigned int i2s_reg_comp1;
+       unsigned int i2s_reg_comp2;
++      unsigned int bclk_ratio;
+       struct device *dev;
+       u32 ccr;
+       u32 xfer_resolution;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch b/target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch
new file mode 100644 (file)
index 0000000..4374e24
--- /dev/null
@@ -0,0 +1,79 @@
+From d5066442e39dd9bf4ba6431ffb3f99e3d5085d3f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 4 Jan 2024 12:02:43 +0000
+Subject: [PATCH] drm/vc4: Fix reading of frame count on GEN5 / Pi4
+
+The frame count values moved within registers DISPSTAT1 and
+DISPSTAT2 with GEN5, so update the accessor function to
+accommodate that.
+
+Fixes: b51cd7ad143d ("drm/vc4: hvs: Fix frame count register readout")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c  | 23 +++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_regs.h |  6 ++++++
+ 2 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -823,10 +823,28 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+       if (!drm_dev_enter(drm, &idx))
+               return 0;
+-      if (vc4->gen >= VC4_GEN_6) {
++      switch (vc4->gen) {
++      case VC4_GEN_6:
+               field = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(fifo)),
+                                     SCALER6_DISPX_STATUS_FRCNT);
+-      } else {
++              break;
++      case VC4_GEN_5:
++              switch (fifo) {
++              case 0:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++                                            SCALER5_DISPSTAT1_FRCNT0);
++                      break;
++              case 1:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++                                            SCALER5_DISPSTAT1_FRCNT1);
++                      break;
++              case 2:
++                      field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
++                                            SCALER5_DISPSTAT2_FRCNT2);
++                      break;
++              }
++              break;
++      case VC4_GEN_4:
+               switch (fifo) {
+               case 0:
+                       field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+@@ -841,6 +859,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+                                             SCALER_DISPSTAT2_FRCNT2);
+                       break;
+               }
++              break;
+       }
+       drm_dev_exit(idx);
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -424,6 +424,10 @@
+ # define SCALER_DISPSTAT1_FRCNT0_SHIFT                18
+ # define SCALER_DISPSTAT1_FRCNT1_MASK         VC4_MASK(17, 12)
+ # define SCALER_DISPSTAT1_FRCNT1_SHIFT                12
++# define SCALER5_DISPSTAT1_FRCNT0_MASK                VC4_MASK(25, 20)
++# define SCALER5_DISPSTAT1_FRCNT0_SHIFT               20
++# define SCALER5_DISPSTAT1_FRCNT1_MASK                VC4_MASK(19, 14)
++# define SCALER5_DISPSTAT1_FRCNT1_SHIFT               14
+ #define SCALER_DISPSTATX(x)                   (SCALER_DISPSTAT0 +        \
+                                                (x) * (SCALER_DISPSTAT1 - \
+@@ -442,6 +446,8 @@
+ #define SCALER_DISPSTAT2                        0x00000068
+ # define SCALER_DISPSTAT2_FRCNT2_MASK         VC4_MASK(17, 12)
+ # define SCALER_DISPSTAT2_FRCNT2_SHIFT                12
++# define SCALER5_DISPSTAT2_FRCNT2_MASK                VC4_MASK(19, 14)
++# define SCALER5_DISPSTAT2_FRCNT2_SHIFT               14
+ #define SCALER_DISPBASE2                        0x0000006c
+ #define SCALER_DISPALPHA2                       0x00000070
diff --git a/target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch b/target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch
new file mode 100644 (file)
index 0000000..f0c6efa
--- /dev/null
@@ -0,0 +1,56 @@
+From eeb5969ae34df16024f39a77496a44ae94bfab13 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Jan 2024 13:56:39 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Add eth_ledx parameters
+
+Include the dtparams controlling the Ethernet jack LEDs, as used on
+other Pis.
+
+See: https://github.com/raspberrypi/linux/issues/5825
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/overlays/README     | 6 +++---
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -830,6 +830,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               act_led_trigger = <&act_led>, "linux,default-trigger";
+               pwr_led_activelow = <&pwr_led>, "gpios:8";
+               pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++              eth_led0 = <&phy1>,"led-modes:0";
++              eth_led1 = <&phy1>,"led-modes:4";
+               drm_fb0_rp1_dsi0 = <&aliases>, "drm-fb0=",&dsi0;
+               drm_fb0_rp1_dsi1 = <&aliases>, "drm-fb0=",&dsi1;
+               drm_fb0_rp1_dpi = <&aliases>, "drm-fb0=",&dpi;
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -207,7 +207,7 @@ Params:
+                                 0 means never downshift (default 2). Pi3B+ only.
+         eth_led0                Set mode of LED0 - amber on Pi3B+ (default "1"),
+-                                green on Pi4 (default "0").
++                                green on Pi4/5 (default "0").
+                                 The legal values are:
+                                 Pi3B+
+@@ -217,7 +217,7 @@ Params:
+                                 4=link100/1000/activity  5=link10/1000/activity
+                                 6=link10/100/activity    14=off    15=on
+-                                Pi4
++                                Pi4/5
+                                 0=Speed/Activity         1=Speed
+                                 2=Flash activity         3=FDX
+@@ -226,7 +226,7 @@ Params:
+                                 8=Link                   9=Activity
+         eth_led1                Set mode of LED1 - green on Pi3B+ (default "6"),
+-                                amber on Pi4 (default "8"). See eth_led0 for
++                                amber on Pi4/5 (default "8"). See eth_led0 for
+                                 legal values.
+         eth_max_speed           Set the maximum speed a link is allowed
diff --git a/target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch b/target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch
new file mode 100644 (file)
index 0000000..da2345f
--- /dev/null
@@ -0,0 +1,71 @@
+From 2c085a1ff40521ab89d8f1894757aa83b59b4607 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Jan 2024 12:09:10 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Add fan speed dtparams
+
+Add dtparams for adjusting the Pi 5 cooling fan speeds and temperature
+thresholds.
+
+See: https://github.com/raspberrypi/linux/issues/5820
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 13 +++++++++++++
+ arch/arm/boot/dts/overlays/README     | 25 +++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -844,5 +844,18 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+               drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1;
+               drm_fb2_rp1_dpi = <&aliases>, "drm-fb2=",&dpi;
+               drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
++
++              fan_temp0 = <&cpu_tepid>,"temperature:0";
++              fan_temp1 = <&cpu_warm>,"temperature:0";
++              fan_temp2 = <&cpu_hot>,"temperature:0";
++              fan_temp3 = <&cpu_vhot>,"temperature:0";
++              fan_temp0_hyst = <&cpu_tepid>,"hysteresis:0";
++              fan_temp1_hyst = <&cpu_warm>,"hysteresis:0";
++              fan_temp2_hyst = <&cpu_hot>,"hysteresis:0";
++              fan_temp3_hyst = <&cpu_vhot>,"hysteresis:0";
++              fan_temp0_speed = <&fan>, "cooling-levels:4";
++              fan_temp1_speed = <&fan>, "cooling-levels:8";
++              fan_temp2_speed = <&fan>, "cooling-levels:12";
++              fan_temp3_speed = <&fan>, "cooling-levels:16";
+       };
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -233,6 +233,31 @@ Params:
+                                 to negotiate. Legal values are 10, 100 and
+                                 1000 (default 1000). Pi3B+ only.
++        fan_temp0               Temperature threshold (in millicelcius) for
++                                1st cooling level (default 50000). Pi5 only.
++        fan_temp0_hyst          Temperature hysteresis (in millicelcius) for
++                                1st cooling level (default 5000). Pi5 only.
++        fan_temp0_speed         Fan PWM setting for 1st cooling level (0-255,
++                                default 75). Pi5 only.
++        fan_temp1               Temperature threshold (in millicelcius) for
++                                2nd cooling level (default 60000). Pi5 only.
++        fan_temp1_hyst          Temperature hysteresis (in millicelcius) for
++                                2nd cooling level (default 5000). Pi5 only.
++        fan_temp1_speed         Fan PWM setting for 2nd cooling level (0-255,
++                                default 125). Pi5 only.
++        fan_temp2               Temperature threshold (in millicelcius) for
++                                3rd cooling level (default 67500). Pi5 only.
++        fan_temp2_hyst          Temperature hysteresis (in millicelcius) for
++                                3rd cooling level (default 5000). Pi5 only.
++        fan_temp2_speed         Fan PWM setting for 3rd cooling level (0-255,
++                                default 175). Pi5 only.
++        fan_temp3               Temperature threshold (in millicelcius) for
++                                4th cooling level (default 75000). Pi5 only.
++        fan_temp3_hyst          Temperature hysteresis (in millicelcius) for
++                                4th cooling level (default 5000). Pi5 only.
++        fan_temp3_speed         Fan PWM setting for 4th cooling level (0-255,
++                                default 250). Pi5 only.
++
+         hdmi                    Set to "off" to disable the HDMI interface
+                                 (default "on")
diff --git a/target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch b/target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch
new file mode 100644 (file)
index 0000000..7c7c05c
--- /dev/null
@@ -0,0 +1,93 @@
+From 146bbf9627f6c37816939de29538ec8ee9a7be1a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 5 Jan 2024 15:07:34 -0300
+Subject: [PATCH] drm/vc4: don't check if plane->state->fb == state->fb
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently, when using non-blocking commits, we can see the following
+kernel warning:
+
+[  110.908514] ------------[ cut here ]------------
+[  110.908529] refcount_t: underflow; use-after-free.
+[  110.908620] WARNING: CPU: 0 PID: 1866 at lib/refcount.c:87 refcount_dec_not_one+0xb8/0xc0
+[  110.908664] Modules linked in: rfcomm snd_seq_dummy snd_hrtimer snd_seq snd_seq_device cmac algif_hash aes_arm64 aes_generic algif_skcipher af_alg bnep hid_logitech_hidpp vc4 brcmfmac hci_uart btbcm brcmutil bluetooth snd_soc_hdmi_codec cfg80211 cec drm_display_helper drm_dma_helper drm_kms_helper snd_soc_core snd_compress snd_pcm_dmaengine fb_sys_fops sysimgblt syscopyarea sysfillrect raspberrypi_hwmon ecdh_generic ecc rfkill libaes i2c_bcm2835 binfmt_misc joydev snd_bcm2835(C) bcm2835_codec(C) bcm2835_isp(C) v4l2_mem2mem videobuf2_dma_contig snd_pcm bcm2835_v4l2(C) raspberrypi_gpiomem bcm2835_mmal_vchiq(C) videobuf2_v4l2 snd_timer videobuf2_vmalloc videobuf2_memops videobuf2_common snd videodev vc_sm_cma(C) mc hid_logitech_dj uio_pdrv_genirq uio i2c_dev drm fuse dm_mod drm_panel_orientation_quirks backlight ip_tables x_tables ipv6
+[  110.909086] CPU: 0 PID: 1866 Comm: kodi.bin Tainted: G         C         6.1.66-v8+ #32
+[  110.909104] Hardware name: Raspberry Pi 3 Model B Rev 1.2 (DT)
+[  110.909114] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+[  110.909132] pc : refcount_dec_not_one+0xb8/0xc0
+[  110.909152] lr : refcount_dec_not_one+0xb4/0xc0
+[  110.909170] sp : ffffffc00913b9c0
+[  110.909177] x29: ffffffc00913b9c0 x28: 000000556969bbb0 x27: 000000556990df60
+[  110.909205] x26: 0000000000000002 x25: 0000000000000004 x24: ffffff8004448480
+[  110.909230] x23: ffffff800570b500 x22: ffffff802e03a7bc x21: ffffffecfca68c78
+[  110.909257] x20: ffffff8002b42000 x19: ffffff802e03a600 x18: 0000000000000000
+[  110.909283] x17: 0000000000000011 x16: ffffffffffffffff x15: 0000000000000004
+[  110.909308] x14: 0000000000000fff x13: ffffffed577e47e0 x12: 0000000000000003
+[  110.909333] x11: 0000000000000000 x10: 0000000000000027 x9 : c912d0d083728c00
+[  110.909359] x8 : c912d0d083728c00 x7 : 65646e75203a745f x6 : 746e756f63666572
+[  110.909384] x5 : ffffffed579f62ee x4 : ffffffed579eb01e x3 : 0000000000000000
+[  110.909409] x2 : 0000000000000000 x1 : ffffffc00913b750 x0 : 0000000000000001
+[  110.909434] Call trace:
+[  110.909441]  refcount_dec_not_one+0xb8/0xc0
+[  110.909461]  vc4_bo_dec_usecnt+0x4c/0x1b0 [vc4]
+[  110.909903]  vc4_cleanup_fb+0x44/0x50 [vc4]
+[  110.910315]  drm_atomic_helper_cleanup_planes+0x88/0xa4 [drm_kms_helper]
+[  110.910669]  vc4_atomic_commit_tail+0x390/0x9dc [vc4]
+[  110.911079]  commit_tail+0xb0/0x164 [drm_kms_helper]
+[  110.911397]  drm_atomic_helper_commit+0x1d0/0x1f0 [drm_kms_helper]
+[  110.911716]  drm_atomic_commit+0xb0/0xdc [drm]
+[  110.912569]  drm_mode_atomic_ioctl+0x348/0x4b8 [drm]
+[  110.913330]  drm_ioctl_kernel+0xec/0x15c [drm]
+[  110.914091]  drm_ioctl+0x24c/0x3b0 [drm]
+[  110.914850]  __arm64_sys_ioctl+0x9c/0xd4
+[  110.914873]  invoke_syscall+0x4c/0x114
+[  110.914897]  el0_svc_common+0xd0/0x118
+[  110.914917]  do_el0_svc+0x38/0xd0
+[  110.914936]  el0_svc+0x30/0x8c
+[  110.914958]  el0t_64_sync_handler+0x84/0xf0
+[  110.914979]  el0t_64_sync+0x18c/0x190
+[  110.914996] ---[ end trace 0000000000000000 ]---
+
+This happens because, although `prepare_fb` and `cleanup_fb` are
+perfectly balanced, we cannot guarantee consistency in the check
+plane->state->fb == state->fb. This means that sometimes we can increase
+the refcount in `prepare_fb` and don't decrease it in `cleanup_fb`. The
+opposite can also be true.
+
+In fact, the struct drm_plane .state shouldn't be accessed directly
+but instead, the `drm_atomic_get_new_plane_state()` helper function should
+be used. So, we could stick to this check, but using
+`drm_atomic_get_new_plane_state()`. But actually, this check is not really
+needed. We can increase and decrease the refcount symmetrically without
+problems.
+
+This is going to make the code more simple and consistent.
+
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2225,9 +2225,6 @@ static int vc4_prepare_fb(struct drm_pla
+       drm_gem_plane_helper_prepare_fb(plane, state);
+-      if (plane->state->fb == state->fb)
+-              return 0;
+-
+       return vc4_bo_inc_usecnt(bo);
+ }
+@@ -2236,7 +2233,7 @@ static void vc4_cleanup_fb(struct drm_pl
+ {
+       struct vc4_bo *bo;
+-      if (plane->state->fb == state->fb || !state->fb)
++      if (!state->fb)
+               return;
+       bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);
diff --git a/target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch
new file mode 100644 (file)
index 0000000..6039363
--- /dev/null
@@ -0,0 +1,42 @@
+From 5d9075ed7e73dc6ccebf78710c78f39ddc2dd78e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 8 Jan 2024 11:42:57 +0000
+Subject: [PATCH] spi: bcm2835: Support spi0-0cs and SPI_NO_CS mode
+
+The forced conversion of native CS lines into software CS lines is done
+whether or not the controller has been given any CS lines to use. This
+breaks the use of the spi0-0cs overlay to prevent SPI from claiming any
+CS lines, particularly with spidev which doesn't pass in the SPI_NO_CS
+flag at creation.
+
+Use the presence of an empty cs-gpios property as an indication that no
+CS lines should be used, bypassing the native CS conversion code.
+
+See: https://github.com/raspberrypi/linux/issues/5835
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-bcm2835.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/spi/spi-bcm2835.c
++++ b/drivers/spi/spi-bcm2835.c
+@@ -1222,6 +1222,7 @@ static int bcm2835_spi_setup(struct spi_
+       struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
+       struct bcm2835_spidev *slv = spi_get_ctldata(spi);
+       struct gpio_chip *chip;
++      int len;
+       int ret;
+       u32 cs;
+@@ -1287,6 +1288,10 @@ static int bcm2835_spi_setup(struct spi_
+               goto err_cleanup;
+       }
++      /* Skip forced CS conversion if controller has an empty cs-gpios property */
++      if (of_find_property(ctlr->dev.of_node, "cs-gpios", &len) && len == 0)
++              return 0;
++
+       /*
+        * Translate native CS to GPIO
+        *
diff --git a/target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch b/target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch
new file mode 100644 (file)
index 0000000..889ddd7
--- /dev/null
@@ -0,0 +1,51 @@
+From 55ed8cded4af6530276b26f567601bed868ae8f5 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Wed, 10 Jan 2024 08:52:54 +0800
+Subject: [PATCH] drivers: media: imx519: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 408 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/imx519.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/media/i2c/imx519.c
++++ b/drivers/media/i2c/imx519.c
+@@ -145,6 +145,10 @@ struct imx519_mode {
+       struct imx519_reg_list reg_list;
+ };
++static const s64 imx519_link_freq_menu[] = {
++      IMX519_DEFAULT_LINK_FREQ,
++};
++
+ static const struct imx519_reg mode_common_regs[] = {
+       {0x0100, 0x00},
+       {0x0136, 0x18},
+@@ -1819,6 +1823,7 @@ static int imx519_init_controls(struct i
+       struct v4l2_ctrl_handler *ctrl_hdlr;
+       struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd);
+       struct v4l2_fwnode_device_properties props;
++      struct v4l2_ctrl *link_freq;
+       unsigned int i;
+       int ret;
+@@ -1837,6 +1842,15 @@ static int imx519_init_controls(struct i
+                                              IMX519_PIXEL_RATE, 1,
+                                              IMX519_PIXEL_RATE);
++      /* LINK_FREQ is also read only */
++      link_freq =
++              v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx519_ctrl_ops,
++                                     V4L2_CID_LINK_FREQ,
++                                     ARRAY_SIZE(imx519_link_freq_menu) - 1, 0,
++                                     imx519_link_freq_menu);
++      if (link_freq)
++              link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+       /*
+        * Create the controls here, but mode specific limits are setup
+        * in the imx519_set_framing_limits() call below.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch b/target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch
new file mode 100644 (file)
index 0000000..df353ff
--- /dev/null
@@ -0,0 +1,56 @@
+From 5e339e1502c9be0f624398cf774e5880a6d1a677 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Wed, 10 Jan 2024 09:06:16 +0800
+Subject: [PATCH] drivers: media: arducam_64mp: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 456 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/arducam_64mp.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -143,6 +143,10 @@ struct arducam_64mp_mode {
+       struct arducam_64mp_reg_list reg_list;
+ };
++static const s64 arducam_64mp_link_freq_menu[] = {
++      ARDUCAM_64MP_DEFAULT_LINK_FREQ,
++};
++
+ static const struct arducam_64mp_reg mode_common_regs[] = {
+       {0x0100, 0x00},
+       {0x0136, 0x18},
+@@ -2272,9 +2276,11 @@ static int arducam_64mp_init_controls(st
+       struct v4l2_ctrl_handler *ctrl_hdlr;
+       struct i2c_client *client = v4l2_get_subdevdata(&arducam_64mp->sd);
+       struct v4l2_fwnode_device_properties props;
++      struct v4l2_ctrl *link_freq;
+       unsigned int i;
+       int ret;
+       u8 test_pattern_max;
++      u8 link_freq_max;
+       ctrl_hdlr = &arducam_64mp->ctrl_handler;
+       ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
+@@ -2292,6 +2298,16 @@ static int arducam_64mp_init_controls(st
+                                                    ARDUCAM_64MP_PIXEL_RATE, 1,
+                                                    ARDUCAM_64MP_PIXEL_RATE);
++      /* LINK_FREQ is also read only */
++      link_freq_max = ARRAY_SIZE(arducam_64mp_link_freq_menu) - 1;
++      link_freq =
++              v4l2_ctrl_new_int_menu(ctrl_hdlr, &arducam_64mp_ctrl_ops,
++                                     V4L2_CID_LINK_FREQ,
++                                     link_freq_max, 0,
++                                     arducam_64mp_link_freq_menu);
++      if (link_freq)
++              link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+       /*
+        * Create the controls here, but mode specific limits are setup
+        * in the arducam_64mp_set_framing_limits() call below.
diff --git a/target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch b/target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch
new file mode 100644 (file)
index 0000000..e106b06
--- /dev/null
@@ -0,0 +1,25 @@
+From 7af1e18d2e90ec64f7cb78cdb5163b63b9fbb056 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 3 Jan 2024 11:45:04 +0800
+Subject: [PATCH]  drivers/gpu/drm/panel:Modify the DSI mode to fix the problem
+ that 7.9inch cannot be displayed
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -340,9 +340,8 @@ static int ws_panel_probe(struct i2c_cli
+        */
+       drm_panel_add(&ts->base);
+-      ts->dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+-                         MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+-                         MIPI_DSI_MODE_LPM);
++      ts->dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
++                         MIPI_DSI_CLOCK_NON_CONTINUOUS;
+       ts->dsi->format = MIPI_DSI_FMT_RGB888;
+       ts->dsi->lanes = 2;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch b/target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch
new file mode 100644 (file)
index 0000000..0f04b21
--- /dev/null
@@ -0,0 +1,22 @@
+From aec3f9b6058448c54b74349f16b21185148a8c6e Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 3 Jan 2024 11:46:50 +0800
+Subject: [PATCH]  drivers/gpu/drm/panel:Modified the timing of 11.9inch to fix
+ the issue that 11.9inch was displayed abnormally
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -131,7 +131,7 @@ static const struct drm_display_mode ws_
+       .hdisplay = 320,
+       .hsync_start = 320 + 60,
+       .hsync_end = 320 + 60 + 60,
+-      .htotal = 320 + 60 + 60 + 120,
++      .htotal = 320 + 60 + 60 + 60,
+       .vdisplay = 1480,
+       .vsync_start = 1480 + 60,
+       .vsync_end = 1480 + 60 + 60,
diff --git a/target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch b/target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch
new file mode 100644 (file)
index 0000000..c13eb35
--- /dev/null
@@ -0,0 +1,88 @@
+From a14da0fdb0e052a672c269df7f18c9de698bd827 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 4 Jan 2024 15:02:42 +0000
+Subject: [PATCH] drm/vc4: Add 2712 support to vc4_plane_async_set_fb
+
+vc4_plane_async_set_fb directly overwrites the plane address in
+the dlist entry, but hadn't been updated for the GEN6 / 2712
+dlist format, corrupting the address in the process.
+
+Add support for the 2712 dlist format to the function.
+
+Fixes: 1ab1fbbb7e76 ("drm/vc4: hvs: Support BCM2712 HVS")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 46 +++++++++++++++++++++++----------
+ 1 file changed, 33 insertions(+), 13 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1865,7 +1865,7 @@ static int vc6_plane_mode_set(struct drm
+                                * The UPM buffer will be allocated in
+                                * vc6_plane_allocate_upm().
+                                */
+-                              VC4_SET_FIELD(upper_32_bits(paddr) & 0xf,
++                              VC4_SET_FIELD(upper_32_bits(paddr) & 0xff,
+                                             SCALER6_PTR0_UPPER_ADDR));
+               /* Pointer Word 1 */
+@@ -2068,7 +2068,8 @@ void vc4_plane_async_set_fb(struct drm_p
+ {
+       struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+       struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
+-      uint32_t addr;
++      struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
++      dma_addr_t dma_addr = bo->dma_addr + fb->offsets[0];
+       int idx;
+       if (!drm_dev_enter(plane->dev, &idx))
+@@ -2078,19 +2079,38 @@ void vc4_plane_async_set_fb(struct drm_p
+        * because this is only called on the primary plane.
+        */
+       WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
+-      addr = bo->dma_addr + fb->offsets[0];
+-      /* Write the new address into the hardware immediately.  The
+-       * scanout will start from this address as soon as the FIFO
+-       * needs to refill with pixels.
+-       */
+-      writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++      if (vc4->gen == VC4_GEN_6) {
++              u32 value;
+-      /* Also update the CPU-side dlist copy, so that any later
+-       * atomic updates that don't do a new modeset on our plane
+-       * also use our updated address.
+-       */
+-      vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
++              value = vc4_state->dlist[vc4_state->ptr0_offset[0]] &
++                                      ~SCALER6_PTR0_UPPER_ADDR_MASK;
++              value |= VC4_SET_FIELD(upper_32_bits(dma_addr) & 0xff,
++                                     SCALER6_PTR0_UPPER_ADDR);
++
++              writel(value, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++              vc4_state->dlist[vc4_state->ptr0_offset[0]] = value;
++
++              value = lower_32_bits(dma_addr);
++              writel(value, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0] + 1]);
++              vc4_state->dlist[vc4_state->ptr0_offset[0] + 1] = value;
++      } else {
++              u32 addr;
++
++              addr = (u32)dma_addr;
++
++              /* Write the new address into the hardware immediately.  The
++               * scanout will start from this address as soon as the FIFO
++               * needs to refill with pixels.
++               */
++              writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++
++              /* Also update the CPU-side dlist copy, so that any later
++               * atomic updates that don't do a new modeset on our plane
++               * also use our updated address.
++               */
++              vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
++      }
+       drm_dev_exit(idx);
+ }
diff --git a/target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch b/target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch
new file mode 100644 (file)
index 0000000..683c4a4
--- /dev/null
@@ -0,0 +1,38 @@
+From bfe927647253ab3a86be16320baa1579518c6786 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 17 Jan 2024 17:00:35 +0000
+Subject: [PATCH] drm/vc4: Fix atomic_async_check to call the right mode_set
+ function
+
+vc4_plane_atomic_async_check was always calling vc4_plane_mode_set
+to validate and generate the dlist for the check. If async_check
+decided it had to fall back to a sync commit, then this GEN4/5
+dlist could get used on GEN6.
+
+Call either vc4_plane_mode_set or vc6_plane_mode_set as appropriate.
+
+Fixes: 1ab1fbbb7e76 ("drm/vc4: hvs: Support BCM2712 HVS")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2194,11 +2194,15 @@ static int vc4_plane_atomic_async_check(
+ {
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+                                                                                plane);
++      struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+       struct vc4_plane_state *old_vc4_state, *new_vc4_state;
+       int ret;
+       u32 i;
+-      ret = vc4_plane_mode_set(plane, new_plane_state);
++      if (vc4->gen >= VC4_GEN_6)
++              ret = vc6_plane_mode_set(plane, new_plane_state);
++      else
++              ret = vc4_plane_mode_set(plane, new_plane_state);
+       if (ret)
+               return ret;
diff --git a/target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch b/target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch
new file mode 100644 (file)
index 0000000..fb20dfe
--- /dev/null
@@ -0,0 +1,343 @@
+From 04b88e1a8b867b045bc90d997e8e0260b00dbb15 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Thu, 11 Jan 2024 12:13:03 +0000
+Subject: [PATCH] drm: rp1: rp1-vec: Allow non-standard modes with various
+ crops
+
+Tweak sync timings in the advertised modelines.
+
+Accept other, custom modes, provided they fit within the active
+area of one of the existing hardware-supported TV modes.
+
+Instead of always padding symmetrically, try to respect the user's
+[hv]sync_start values, allowing the image to be shifted around
+the screen (to fine-tune overscan correction).
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c    | 66 ++++++++++++---------
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 74 ++++++++++++++++++------
+ 2 files changed, 96 insertions(+), 44 deletions(-)
+
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -19,7 +19,6 @@
+ #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>
+@@ -208,26 +207,26 @@ static void rp1vec_connector_destroy(str
+ 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,
++                       720, 720 + 16, 720 + 16 + 64, 858, 0,
++                       480, 480 + 6, 480 + 6 + 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,
++                       704, 704 + 76, 704 + 76 + 72, 980, 0,
++                       432, 432 + 30, 432 + 30 + 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,
++                       576, 576 + 5, 576 + 5 + 5, 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,
++                       512, 512 + 37, 512 + 37 + 5, 625, 0,
+                        DRM_MODE_FLAG_INTERLACE)
+       }
+ };
+@@ -298,27 +297,42 @@ static enum drm_mode_status rp1vec_mode_
+                                             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.
++       * Check the mode roughly matches something we can generate.
++       * The hardware driver is very prescriptive about pixel clocks,
++       * line and frame durations, but we'll tolerate rounding errors.
++       * Within each hardware mode, allow image size and position to vary
++       * (to fine-tune overscan correction or emulate retro devices).
++       * Don't check sync timings here: the HW driver will sanitize them.
+        */
+-      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;
++      int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
++      int vtotal_full = mode->vtotal << prog;
++      int vdisplay_full = mode->vdisplay << prog;
++
++      /* Reject very small frames */
++      if (vtotal_full < 256 || mode->hdisplay < 256)
++              return MODE_BAD;
++
++      /* Check lines, frame period (ms) and vertical size limit */
++      if (vtotal_full >= 524 && vtotal_full <= 526 &&
++          mode->htotal * vtotal_full > 33 * mode->clock &&
++          mode->htotal * vtotal_full < 34 * mode->clock &&
++          vdisplay_full <= 480)
++              goto vgood;
++      if (vtotal_full >= 624 && vtotal_full <= 626 &&
++          mode->htotal * vtotal_full > 39 * mode->clock &&
++          mode->htotal * vtotal_full < 41 * mode->clock &&
++          vdisplay_full <= 576)
++              goto vgood;
++      return MODE_BAD;
+-              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;
+-      }
++vgood:
++      /* Check pixel rate (kHz) and horizontal size limit */
++      if (mode->clock == 13500 && mode->hdisplay <= 720)
++              return MODE_OK;
++      if (mode->clock >= 15428 && mode->clock <= 15429 &&
++          mode->hdisplay <= 800)
++              return MODE_OK;
+       return MODE_BAD;
+ }
+@@ -440,7 +454,7 @@ static int rp1vec_platform_probe(struct
+       ret = drmm_mode_config_init(drm);
+       if (ret)
+               goto err_free_drm;
+-      drm->mode_config.max_width  = 768;
++      drm->mode_config.max_width  = 800;
+       drm->mode_config.max_height = 576;
+       drm->mode_config.fb_base    = 0;
+       drm->mode_config.preferred_depth = 32;
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -11,7 +11,6 @@
+ #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>
+@@ -82,13 +81,13 @@ static const struct rp1vec_ipixfmt my_fo
+ /*
+  * 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  total_cols;        /* max active columns incl. padding and windowing   */
+       u16  rows_per_field;    /* active lines per field (including partial ones)  */
++      u16  ref_hfp;           /* nominal (hsync_start - hdisplay) when max width  */
++      u16  ref_vfp;           /* nominal (vsync_start - vdisplay) when max height */
+       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      */
+@@ -103,6 +102,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 240,
++                              .ref_hfp = 12,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x1071d0cf,
+@@ -118,6 +119,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 815,
+                               .rows_per_field = 240,
++                              .ref_hfp = 16,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x1c131962,
+@@ -135,6 +138,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 243,
++                              .ref_hfp = 12,
++                              .ref_vfp = 3,
+                               .interlaced = true,
+                               .first_field_odd = true,
+                               .yuv_scaling = 0x1071d0cf,
+@@ -150,6 +155,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 815,
+                               .rows_per_field = 243,
++                              .ref_hfp = 16,
++                              .ref_vfp = 3,
+                               .interlaced = true,
+                               .first_field_odd = true,
+                               .yuv_scaling = 0x1c131962,
+@@ -170,6 +177,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 288,
++                              .ref_hfp = 16,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x11c1f8e0,
+@@ -185,6 +194,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 804,
+                               .rows_per_field = 288,
++                              .ref_hfp = 24,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x1e635d7f,
+@@ -202,6 +213,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 288,
++                              .ref_hfp = 16,
++                              .ref_vfp = 5,
+                               .interlaced = true,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x11c1f8e0,
+@@ -217,6 +230,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 804,
+                               .rows_per_field = 288,
++                              .ref_hfp = 24,
++                              .ref_vfp = 5,
+                               .interlaced = true,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x1e635d7f,
+@@ -237,6 +252,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 240,
++                              .ref_hfp = 12,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x11c1f8e0,
+@@ -252,6 +269,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 815,
+                               .rows_per_field = 240,
++                              .ref_hfp = 16,
++                              .ref_vfp = 2,
+                               .interlaced = false,
+                               .first_field_odd = false,
+                               .yuv_scaling = 0x1e635d7f,
+@@ -269,6 +288,8 @@ static const struct rp1vec_hwmode rp1vec
+                       {
+                               .total_cols = 724,
+                               .rows_per_field = 243,
++                              .ref_hfp = 12,
++                              .ref_vfp = 3,
+                               .interlaced = true,
+                               .first_field_odd = true,
+                               .yuv_scaling = 0x11c1f8e0,
+@@ -284,6 +305,8 @@ static const struct rp1vec_hwmode rp1vec
+                       }, {
+                               .total_cols = 815,
+                               .rows_per_field = 243,
++                              .ref_hfp = 16,
++                              .ref_vfp = 3,
+                               .interlaced = true,
+                               .first_field_odd = true,
+                               .yuv_scaling = 0x1e635d7f,
+@@ -308,11 +331,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+ {
+       unsigned int i, mode_family, mode_ilaced, mode_narrow;
+       const struct rp1vec_hwmode *hwm;
+-      unsigned int w, h;
++      int w, h, hpad, vpad;
+       /* Pick the appropriate "base" mode, which we may modify */
+       mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+-      if (mode->vtotal > 263 * (1 + mode_ilaced))
++      if (mode->vtotal >= 272 * (1 + mode_ilaced))
+               mode_family = 1;
+       else
+               mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
+@@ -326,13 +349,28 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+               tvstd, rp1vec_tvstd_names[tvstd]);
+       w = mode->hdisplay;
+-      h = mode->vdisplay;
+-      if (mode_ilaced)
+-              h >>= 1;
++      h = mode->vdisplay >> mode_ilaced;
+       if (w > hwm->total_cols)
+               w = hwm->total_cols;
+       if (h > hwm->rows_per_field)
+-              w = hwm->rows_per_field;
++              h = hwm->rows_per_field;
++
++      /*
++       * Add padding so a framebuffer with the given dimensions and
++       * [hv]sync_start can be displayed in the chosen hardware mode.
++       *
++       *          |<----- mode->hsync_start ----->|
++       *          |<------ w ------>|             |
++       *          |                 |         >|--|<  ref_hfp
++       *                            |<- hpad ->|
++       * |<------------ total_cols ----------->|
++       *  ________FRAMEBUFFERCONTENTS__________
++       * '                                     `--\____/-<\/\/\>-'
++       */
++      hpad = max(0, mode->hsync_start - hwm->ref_hfp - w);
++      hpad = min(hpad, hwm->total_cols - w);
++      vpad = max(0, ((mode->vsync_start - hwm->ref_vfp) >> mode_ilaced) - h);
++      vpad = min(vpad, hwm->rows_per_field - h);
+       /* Configure the hardware */
+       VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
+@@ -347,18 +385,18 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+                 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));
++                BITS(VEC_BACK_PORCH_HBP_MINUS1, hwm->total_cols - w - hpad - 1) |
++                BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->rows_per_field - h - vpad - 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));
++                BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad - 1) |
++                BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad - 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_VFP_EN, (vpad > 0))                       |
++                BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h + vpad)) |
++                BITS(VEC_MODE_HFP_EN, (hpad > 0))                       |
++                BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w + hpad))     |
+                 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) {
diff --git a/target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch b/target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch
new file mode 100644 (file)
index 0000000..c94619c
--- /dev/null
@@ -0,0 +1,24 @@
+From ab8ba584f91576c41d921076221ceb48e2930ae0 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 18 Jan 2024 11:08:03 +0000
+Subject: [PATCH] ARM: pl011: Add rs485 to the RP1 support
+
+pl011_axi_probe, added for RP1 support, lacks the rs485 additions that
+appeared during its development.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/amba-pl011.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -3026,6 +3026,8 @@ static int pl011_axi_probe(struct platfo
+       uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
+       uap->port.irq = irq;
+       uap->port.ops = &amba_pl011_pops;
++      uap->port.rs485_config = pl011_rs485_config;
++      uap->port.rs485_supported = pl011_rs485_supported;
+       snprintf(uap->type, sizeof(uap->type), "PL011 AXI");
diff --git a/target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch b/target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch
new file mode 100644 (file)
index 0000000..8fc8d0c
--- /dev/null
@@ -0,0 +1,38 @@
+From 3bb5880ab3dd31f75c07c3c33bf29c5d469b28f3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 18 Jan 2024 15:41:21 +0000
+Subject: [PATCH] fixup! irqchip: irq-bcm2712-mip: Support for 2712's MIP
+
+Use irq_domain_add_hierarchy so that the root pointer is initialised
+correctly. Failure to do so leads to (at least) lockdep warnings when
+they are enabled.
+
+See: https://github.com/raspberrypi/linux/issues/5866
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/irqchip/irq-bcm2712-mip.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/irqchip/irq-bcm2712-mip.c
++++ b/drivers/irqchip/irq-bcm2712-mip.c
+@@ -209,16 +209,14 @@ static int mip_init_domains(struct mip_p
+               return -ENXIO;
+       }
+-      middle_domain = irq_domain_add_tree(NULL,
+-                                          &mip_irq_domain_ops,
+-                                          priv);
++      middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, 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);
index c1b4879a7c8cf0b275cccba03ed4c4cdf5c3bafc..cabe36ba7192f0d96a3d2398782505373f9c9c8e 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
 
 --- a/drivers/char/hw_random/iproc-rng200.c
 +++ b/drivers/char/hw_random/iproc-rng200.c
-@@ -252,6 +252,7 @@ static int iproc_rng200_probe(struct pla
+@@ -253,6 +253,7 @@ static int iproc_rng200_probe(struct pla
  
        priv->rng.name = pdev->name;
        priv->rng.cleanup = iproc_rng200_cleanup;
index d11c946dbca028b8355798ce63aaba7d64c2a613..c6301662f70be918197ed1f332cf2d7a16707976 100644 (file)
@@ -605,6 +605,7 @@ CONFIG_BASE_SMALL=0
 # CONFIG_BAYCOM_SER_FDX is not set
 # CONFIG_BAYCOM_SER_HDX is not set
 # CONFIG_BCACHE is not set
+# CONFIG_BCM2712_MIP is not set
 # CONFIG_BCM47XX is not set
 # CONFIG_BCM54140_PHY is not set
 # CONFIG_BCM63XX is not set
@@ -1036,6 +1037,8 @@ CONFIG_CMDLINE=""
 # CONFIG_COMMON_CLK_PWM is not set
 # CONFIG_COMMON_CLK_PXA is not set
 # CONFIG_COMMON_CLK_QCOM is not set
+# CONFIG_COMMON_CLK_RP1 is not set
+# CONFIG_COMMON_CLK_RP1_SDIO  is not set
 # CONFIG_COMMON_CLK_RS9_PCIE is not set
 # CONFIG_COMMON_CLK_SI514 is not set
 # CONFIG_COMMON_CLK_SI5341 is not set
@@ -1725,6 +1728,9 @@ CONFIG_DQL=y
 # CONFIG_DRM_RCAR_USE_LVDS is not set
 # CONFIG_DRM_RCAR_USE_MIPI_DSI is not set
 # CONFIG_DRM_ROCKCHIP is not set
+# CONFIG_DRM_RP1_DPI is not set
+# CONFIG_DRM_RP1_DSI is not set
+# CONFIG_DRM_RP1_VEC is not set
 # CONFIG_DRM_SII902X is not set
 # CONFIG_DRM_SII9234 is not set
 # CONFIG_DRM_SIL_SII8620 is not set
@@ -2083,6 +2089,7 @@ CONFIG_FB_NOTIFY=y
 # CONFIG_FB_PXA is not set
 # CONFIG_FB_RADEON is not set
 # CONFIG_FB_RIVA is not set
+# CONFIG_FB_RPISENSE is not set
 # CONFIG_FB_S1D13XXX is not set
 # CONFIG_FB_S3 is not set
 # CONFIG_FB_SAVAGE is not set
@@ -2283,6 +2290,7 @@ CONFIG_GPIOLIB_FASTPATH_LIMIT=512
 # CONFIG_GPIO_AMDPT is not set
 # CONFIG_GPIO_AMD_FCH is not set
 # CONFIG_GPIO_BCM_KONA is not set
+# CONFIG_GPIO_BRCMSTB is not set
 # CONFIG_GPIO_BT8XX is not set
 # CONFIG_GPIO_CADENCE is not set
 # CONFIG_GPIO_CASCADE is not set
@@ -2853,6 +2861,7 @@ CONFIG_INPUT_MISC=y
 # CONFIG_INPUT_POWERMATE is not set
 # CONFIG_INPUT_PWM_BEEPER is not set
 # CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_RASPBERRYPI_BUTTON is not set
 # CONFIG_INPUT_REGULATOR_HAPTIC is not set
 # CONFIG_INPUT_SOC_BUTTON_ARRAY is not set
 # CONFIG_INPUT_SPARSEKMAP is not set
@@ -3609,6 +3618,7 @@ CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
 # CONFIG_MFD_PM8921_CORE is not set
 # CONFIG_MFD_PM8XXX is not set
 # CONFIG_MFD_QCOM_PM8008 is not set
+# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
 # CONFIG_MFD_RC5T583 is not set
 # CONFIG_MFD_RDC321X is not set
 # CONFIG_MFD_RETU is not set
@@ -3618,6 +3628,8 @@ CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
 # CONFIG_MFD_ROHM_BD71828 is not set
 # CONFIG_MFD_ROHM_BD718XX is not set
 # CONFIG_MFD_ROHM_BD957XMUF is not set
+# CONFIG_MFD_RP1 is not set
+# CONFIG_MFD_RPISENSE_CORE is not set
 # CONFIG_MFD_RSMU_I2C is not set
 # CONFIG_MFD_RSMU_SPI is not set
 # CONFIG_MFD_RT4831 is not set
@@ -4839,6 +4851,7 @@ CONFIG_PCI_SYSCALL=y
 # CONFIG_PHYLIB is not set
 # CONFIG_PHYLIB_LEDS is not set
 # CONFIG_PHYS_ADDR_T_64BIT is not set
+# CONFIG_PHY_BRCM_USB is not set
 # CONFIG_PHY_CADENCE_DP is not set
 # CONFIG_PHY_CADENCE_DPHY is not set
 # CONFIG_PHY_CADENCE_DPHY_RX is not set
@@ -4875,6 +4888,7 @@ CONFIG_PINCONF=y
 # CONFIG_PINCTRL is not set
 # CONFIG_PINCTRL_AMD is not set
 # CONFIG_PINCTRL_AXP209 is not set
+# CONFIG_PINCTRL_BCM2712 is not set
 # CONFIG_PINCTRL_CEDARFORK is not set
 # CONFIG_PINCTRL_CY8C95X0 is not set
 # CONFIG_PINCTRL_EXYNOS is not set
@@ -4895,6 +4909,7 @@ CONFIG_PINCONF=y
 # CONFIG_PINCTRL_MTK_V2 is not set
 # CONFIG_PINCTRL_OCELOT is not set
 # CONFIG_PINCTRL_PISTACHIO is not set
+# CONFIG_PINCTRL_RP1 is not set
 # CONFIG_PINCTRL_SC7280 is not set
 # CONFIG_PINCTRL_SC8180X is not set
 # CONFIG_PINCTRL_SDX55 is not set
@@ -5053,6 +5068,7 @@ CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
 # CONFIG_PWM_MEDIATEK is not set
 # CONFIG_PWM_PCA9685 is not set
 # CONFIG_PWM_RASPBERRYPI_POE is not set
+# CONFIG_PWM_RP1 is not set
 # CONFIG_PWM_XILINX is not set
 CONFIG_PWRSEQ_EMMC=y
 # CONFIG_PWRSEQ_SD8787 is not set
@@ -5127,6 +5143,7 @@ CONFIG_RANDOM_TRUST_CPU=y
 # CONFIG_RANDSTRUCT_NONE is not set
 # CONFIG_RAPIDIO is not set
 # CONFIG_RAS is not set
+# CONFIG_RASPBERRYPI_GPIOMEM is not set
 # CONFIG_RBTREE_TEST is not set
 # CONFIG_RCU_BOOST is not set
 CONFIG_RCU_CPU_STALL_TIMEOUT=60
@@ -5251,6 +5268,7 @@ CONFIG_REISERFS_FS_XATTR=y
 # CONFIG_RENESAS_PHY is not set
 # CONFIG_RESET_ATH79 is not set
 # CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_BRCMSTB is not set
 # CONFIG_RESET_BRCMSTB_RESCAL is not set
 # CONFIG_RESET_CONTROLLER is not set
 # CONFIG_RESET_IMX7 is not set
@@ -5779,6 +5797,7 @@ CONFIG_SELECT_MEMORY_MODEL=y
 # CONFIG_SENSORS_Q54SJ108A2 is not set
 # CONFIG_SENSORS_RM3100_I2C is not set
 # CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSORS_RP1_ADC is not set
 # CONFIG_SENSORS_SBRMI is not set
 # CONFIG_SENSORS_SBTSI is not set
 # CONFIG_SENSORS_SCH5627 is not set
@@ -6593,6 +6612,7 @@ CONFIG_STDBINUTILS=y
 # CONFIG_STMMAC_PCI is not set
 # CONFIG_STMMAC_PLATFORM is not set
 # CONFIG_STMMAC_SELFTESTS is not set
+# CONFIG_STMPE_ADC is not set
 # CONFIG_STM_DUMMY is not set
 # CONFIG_STM_SOURCE_CONSOLE is not set
 CONFIG_STP=y
@@ -7510,6 +7530,7 @@ CONFIG_VHOST_MENU=y
 # CONFIG_VIDEO_BT848 is not set
 # CONFIG_VIDEO_BT856 is not set
 # CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_BU64754 is not set
 # CONFIG_VIDEO_CADENCE is not set
 # CONFIG_VIDEO_CADENCE_CSI2RX is not set
 # CONFIG_VIDEO_CADENCE_CSI2TX is not set
@@ -7603,6 +7624,7 @@ CONFIG_VHOST_MENU=y
 # CONFIG_VIDEO_OV5675 is not set
 # CONFIG_VIDEO_OV5693 is not set
 # CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV64A40 is not set
 # CONFIG_VIDEO_OV6650 is not set
 # CONFIG_VIDEO_OV7251 is not set
 # CONFIG_VIDEO_OV7640 is not set