Przeglądaj źródła

bcm27xx: pull 6.6 patches from RPi repo

Adds latest 6.6 patches from the Raspberry Pi repository.

These patches were generated from:
https://github.com/raspberrypi/linux/commits/rpi-6.6.y/
With the following command:
git format-patch -N v6.6.67..HEAD
(HEAD -> 811ff707533bcd67cdcd368bbd46223082009b12)

Signed-off-by: Álvaro Fernández Rojas <[email protected]>
Álvaro Fernández Rojas 1 rok temu
rodzic
commit
692205305d
100 zmienionych plików z 9513 dodań i 121 usunięć
  1. 2 0
      target/linux/bcm27xx/bcm2708/config-6.6
  2. 2 0
      target/linux/bcm27xx/bcm2709/config-6.6
  3. 2 0
      target/linux/bcm27xx/bcm2710/config-6.6
  4. 2 0
      target/linux/bcm27xx/bcm2711/config-6.6
  5. 2 0
      target/linux/bcm27xx/bcm2712/config-6.6
  6. 2 0
      target/linux/bcm27xx/config-6.6
  7. 0 34
      target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch
  8. 141 0
      target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch
  9. 330 0
      target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch
  10. 79 0
      target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch
  11. 29 0
      target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch
  12. 42 0
      target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch
  13. 35 0
      target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch
  14. 77 0
      target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch
  15. 30 0
      target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch
  16. 50 0
      target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch
  17. 32 0
      target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch
  18. 41 0
      target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch
  19. 59 0
      target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch
  20. 30 0
      target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch
  21. 101 0
      target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch
  22. 42 0
      target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch
  23. 71 0
      target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch
  24. 47 0
      target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch
  25. 164 0
      target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch
  26. 65 0
      target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch
  27. 40 0
      target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch
  28. 31 0
      target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch
  29. 25 0
      target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch
  30. 56 0
      target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch
  31. 199 0
      target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch
  32. 43 0
      target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch
  33. 29 0
      target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch
  34. 0 0
      target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch
  35. 0 0
      target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch
  36. 0 0
      target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch
  37. 118 0
      target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch
  38. 55 0
      target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch
  39. 0 0
      target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch
  40. 2 2
      target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch
  41. 99 0
      target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch
  42. 0 0
      target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch
  43. 2 2
      target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch
  44. 6 6
      target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch
  45. 3592 0
      target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch
  46. 108 0
      target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch
  47. 64 0
      target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch
  48. 2 2
      target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch
  49. 31 0
      target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch
  50. 3 3
      target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch
  51. 0 0
      target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch
  52. 1167 0
      target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch
  53. 100 0
      target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch
  54. 2 2
      target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch
  55. 2 2
      target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch
  56. 2 2
      target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch
  57. 2 2
      target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch
  58. 25 0
      target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch
  59. 1 1
      target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch
  60. 0 0
      target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch
  61. 147 0
      target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch
  62. 30 0
      target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch
  63. 25 0
      target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch
  64. 33 0
      target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch
  65. 24 0
      target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch
  66. 2 2
      target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch
  67. 1 2
      target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch
  68. 1 2
      target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch
  69. 4 4
      target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch
  70. 1 1
      target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch
  71. 1 1
      target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch
  72. 3 3
      target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch
  73. 2 2
      target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch
  74. 23 0
      target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch
  75. 22 0
      target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch
  76. 630 0
      target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch
  77. 95 0
      target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch
  78. 142 0
      target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch
  79. 25 0
      target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch
  80. 48 0
      target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch
  81. 23 0
      target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch
  82. 28 0
      target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch
  83. 55 0
      target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch
  84. 118 0
      target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch
  85. 309 0
      target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch
  86. 46 0
      target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch
  87. 37 0
      target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch
  88. 31 0
      target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch
  89. 22 0
      target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch
  90. 29 0
      target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch
  91. 25 0
      target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch
  92. 34 0
      target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch
  93. 2 2
      target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch
  94. 0 22
      target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch
  95. 0 20
      target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch
  96. 79 0
      target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch
  97. 30 0
      target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch
  98. 29 0
      target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch
  99. 73 0
      target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch
  100. 1 2
      target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch

+ 2 - 0
target/linux/bcm27xx/bcm2708/config-6.6

@@ -314,6 +314,7 @@ CONFIG_PRINTK_TIME=y
 CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_PWM=y
 CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
 CONFIG_PWM_SYSFS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
@@ -346,6 +347,7 @@ CONFIG_SERIAL_DEV_BUS=y
 # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
 CONFIG_SERIAL_MCTRL_GPIO=y
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
 CONFIG_SG_POOL=y
 CONFIG_SMSC_PHY=y
 CONFIG_SOFTIRQ_ON_OWN_STACK=y

+ 2 - 0
target/linux/bcm27xx/bcm2709/config-6.6

@@ -398,6 +398,7 @@ CONFIG_PTP_1588_CLOCK=y
 CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_PWM=y
 CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
 CONFIG_PWM_SYSFS=y
 CONFIG_RANDSTRUCT_NONE=y
 CONFIG_RAS=y
@@ -435,6 +436,7 @@ CONFIG_SERIAL_DEV_BUS=y
 # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
 CONFIG_SERIAL_MCTRL_GPIO=y
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
 CONFIG_SG_POOL=y
 CONFIG_SMP=y
 CONFIG_SMP_ON_UP=y

+ 2 - 0
target/linux/bcm27xx/bcm2710/config-6.6

@@ -389,6 +389,7 @@ CONFIG_PRINTK_TIME=y
 CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_PWM=y
 CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
 CONFIG_PWM_SYSFS=y
 CONFIG_QUEUED_RWLOCKS=y
 CONFIG_QUEUED_SPINLOCKS=y
@@ -428,6 +429,7 @@ CONFIG_SERIAL_DEV_BUS=y
 # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
 CONFIG_SERIAL_MCTRL_GPIO=y
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
 CONFIG_SG_POOL=y
 CONFIG_SMP=y
 CONFIG_SMSC_PHY=y

+ 2 - 0
target/linux/bcm27xx/bcm2711/config-6.6

@@ -405,6 +405,7 @@ CONFIG_PTP_1588_CLOCK=y
 CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_PWM=y
 CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
 CONFIG_PWM_SYSFS=y
 CONFIG_QUEUED_RWLOCKS=y
 CONFIG_QUEUED_SPINLOCKS=y
@@ -445,6 +446,7 @@ CONFIG_SERIAL_DEV_BUS=y
 # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
 CONFIG_SERIAL_MCTRL_GPIO=y
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
 CONFIG_SG_POOL=y
 CONFIG_SMP=y
 CONFIG_SOCK_RX_QUEUE_MAPPING=y

+ 2 - 0
target/linux/bcm27xx/bcm2712/config-6.6

@@ -513,6 +513,7 @@ CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 CONFIG_PWM=y
 CONFIG_PWM_BCM2835=y
 CONFIG_PWM_BRCMSTB=y
+CONFIG_PWM_GPIO=y
 CONFIG_PWM_RP1=y
 CONFIG_PWM_SYSFS=y
 CONFIG_QUEUED_RWLOCKS=y
@@ -577,6 +578,7 @@ CONFIG_SPARSEMEM_VMEMMAP=y
 CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
 CONFIG_SPARSE_IRQ=y
 CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+CONFIG_SRAM=y
 # CONFIG_STRIP_ASM_SYMS is not set
 CONFIG_SUSPEND=y
 CONFIG_SUSPEND_FREEZER=y

+ 2 - 0
target/linux/bcm27xx/config-6.6

@@ -28,6 +28,8 @@
 # CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 is not set
 # CONFIG_RP1_PIO is not set
 # CONFIG_SENSORS_RP1_ADC is not set
+# CONFIG_SERIAL_RPI_FW is not set
+# CONFIG_SND_PIMIDI is not set
 # CONFIG_SPI_RP2040_GPIO_BRIDGE is not set
 # CONFIG_VIDEO_AD5398 is not set
 # CONFIG_VIDEO_ARDUCAM_64MP is not set

+ 0 - 34
target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch

@@ -1,34 +0,0 @@
-From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <[email protected]>
-Date: Wed, 18 Sep 2024 16:45:24 +0100
-Subject: [PATCH 1267/1350] dts: rp1: Disable DMA usage for UART0
-
-Some recent DMA changes have led to data loss in UART0 on Pi 5. It also
-seems that even prior to these changes there was a problem with aborted
-transfers.
-
-As this is the only RP1 UART configured for DMA, it is better to remove
-the DMA usage until it is shown to be reliable.
-
-Link: https://github.com/raspberrypi/linux/issues/6365
-
-Signed-off-by: Phil Elwell <[email protected]>
----
- arch/arm64/boot/dts/broadcom/rp1.dtsi | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
-+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -55,9 +55,9 @@
- 			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";
-+			// 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;

+ 141 - 0
target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch

@@ -0,0 +1,141 @@
+From 25e6acfe00f589a5989ebd2c8d21a130fb3bf106 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Fri, 18 Oct 2024 09:18:10 +0100
+Subject: [PATCH] drivers: media: bcm2835_isp: Cache LS table dmabuf
+
+Clients such as libcamera do not change the LS table dmabuf on every
+frame. In such cases instead of mapping/remapping the same dmabuf on
+every frame to send to the firmware, cache the dmabuf once and only
+update and remap if the dmabuf has been changed by the userland client.
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ .../bcm2835-isp/bcm2835-v4l2-isp.c            | 77 +++++++++++--------
+ 1 file changed, 46 insertions(+), 31 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -139,6 +139,8 @@ struct bcm2835_isp_dev {
+ 	/* Image pipeline controls. */
+ 	int r_gain;
+ 	int b_gain;
++	struct dma_buf *last_ls_dmabuf;
++	struct mmal_parameter_lens_shading_v2 ls;
+ };
+ 
+ struct bcm2835_isp_buffer {
+@@ -657,18 +659,18 @@ static void bcm2835_isp_node_stop_stream
+ 	atomic_dec(&dev->num_streaming);
+ 	/* If all ports disabled, then disable the component */
+ 	if (atomic_read(&dev->num_streaming) == 0) {
+-		struct bcm2835_isp_lens_shading ls;
+ 		/*
+ 		 * The ISP component on the firmware has a reference to the
+ 		 * dmabuf handle for the lens shading table.  Pass a null handle
+ 		 * to remove that reference now.
+ 		 */
+-		memset(&ls, 0, sizeof(ls));
++		memset(&dev->ls, 0, sizeof(dev->ls));
+ 		/* Must set a valid grid size for the FW */
+-		ls.grid_cell_size = 16;
++		dev->ls.grid_cell_size = 16;
+ 		set_isp_param(&dev->node[0],
+ 			      MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+-			      &ls, sizeof(ls));
++			      &dev->ls, sizeof(dev->ls));
++		dev->last_ls_dmabuf = NULL;
+ 
+ 		ret = vchiq_mmal_component_disable(dev->mmal_instance,
+ 						   dev->component);
+@@ -719,6 +721,36 @@ static inline unsigned int get_sizeimage
+ 	return (bpl * height * fmt->size_multiplier_x2) >> 1;
+ }
+ 
++static int map_ls_table(struct bcm2835_isp_dev *dev, struct dma_buf *dmabuf,
++			const struct bcm2835_isp_lens_shading *v4l2_ls)
++{
++	void *vcsm_handle;
++	int ret;
++
++	if (IS_ERR_OR_NULL(dmabuf))
++		return -EINVAL;
++
++	/*
++	 * struct bcm2835_isp_lens_shading and struct
++	 * mmal_parameter_lens_shading_v2 match so that we can do a
++	 * simple memcpy here.
++	 * Only the dmabuf to the actual table needs any manipulation.
++	 */
++	memcpy(&dev->ls, v4l2_ls, sizeof(dev->ls));
++	ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle);
++	if (ret) {
++		dma_buf_put(dmabuf);
++		return ret;
++	}
++
++	dev->ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle);
++	dev->last_ls_dmabuf = dmabuf;
++
++	vc_sm_cma_free(vcsm_handle);
++
++	return 0;
++}
++
+ static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ 	struct bcm2835_isp_dev *dev =
+@@ -754,44 +786,27 @@ static int bcm2835_isp_s_ctrl(struct v4l
+ 	case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING:
+ 	{
+ 		struct bcm2835_isp_lens_shading *v4l2_ls;
+-		struct mmal_parameter_lens_shading_v2 ls;
+-		struct dma_buf *dmabuf;
+-		void *vcsm_handle;
+ 
+ 		v4l2_ls = (struct bcm2835_isp_lens_shading *)ctrl->p_new.p_u8;
+-		/*
+-		 * struct bcm2835_isp_lens_shading and struct
+-		 * mmal_parameter_lens_shading_v2 match so that we can do a
+-		 * simple memcpy here.
+-		 * Only the dmabuf to the actual table needs any manipulation.
+-		 */
+-		memcpy(&ls, v4l2_ls, sizeof(ls));
++		struct dma_buf *dmabuf = dma_buf_get(v4l2_ls->dmabuf);
+ 
+-		dmabuf = dma_buf_get(v4l2_ls->dmabuf);
+-		if (IS_ERR_OR_NULL(dmabuf))
+-			return -EINVAL;
+-
+-		ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle);
+-		if (ret) {
+-			dma_buf_put(dmabuf);
+-			return -EINVAL;
+-		}
++		if (dmabuf != dev->last_ls_dmabuf)
++			ret = map_ls_table(dev, dmabuf, v4l2_ls);
+ 
+-		ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle);
+-		if (ls.mem_handle_table)
+-			/* The VPU will take a reference on the vcsm handle,
++		if (!ret && dev->ls.mem_handle_table)
++			/*
++			 * The VPU will take a reference on the vcsm handle,
+ 			 * which in turn will retain a reference on the dmabuf.
+ 			 * This code can therefore safely release all
+ 			 * references to the buffer.
+ 			 */
+-			ret = set_isp_param(node,
+-					    MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+-					    &ls,
+-					    sizeof(ls));
++			ret =
++			set_isp_param(node,
++				      MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
++				      &dev->ls, sizeof(dev->ls));
+ 		else
+ 			ret = -EINVAL;
+ 
+-		vc_sm_cma_free(vcsm_handle);
+ 		dma_buf_put(dmabuf);
+ 		break;
+ 	}

+ 330 - 0
target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch

@@ -0,0 +1,330 @@
+From 3ab72fc21ea8576e59f6aad10bd6b1a0eae6e5eb Mon Sep 17 00:00:00 2001
+From: Vincent Whitchurch <[email protected]>
+Date: Tue, 4 Jun 2024 23:00:41 +0200
+Subject: [PATCH] pwm: Add GPIO PWM driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 7f61257cd6e1ad4769b4b819668cab00f68f2556 upstream.
+
+Add a software PWM which toggles a GPIO from a high-resolution timer.
+
+This will naturally not be as accurate or as efficient as a hardware
+PWM, but it is useful in some cases.  I have for example used it for
+evaluating LED brightness handling (via leds-pwm) on a board where the
+LED was just hooked up to a GPIO, and for a simple verification of the
+timer frequency on another platform.
+
+Since high-resolution timers are used, sleeping GPIO chips are not
+supported and are rejected in the probe function.
+
+Signed-off-by: Vincent Whitchurch <[email protected]>
+Co-developed-by: Stefan Wahren <[email protected]>
+Signed-off-by: Stefan Wahren <[email protected]>
+Co-developed-by: Linus Walleij <[email protected]>
+Reviewed-by: Andy Shevchenko <[email protected]>
+Signed-off-by: Linus Walleij <[email protected]>
+Reviewed-by: Dhruva Gole <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Uwe Kleine-König <[email protected]>
+Signed-off-by: Tim Gover <[email protected]>
+
+pwm: Backport pwm-gpio.c to rpi-6.6.y
+---
+ .../driver-api/gpio/drivers-on-gpio.rst       |   7 +-
+ drivers/pwm/Kconfig                           |  11 +
+ drivers/pwm/Makefile                          |   1 +
+ drivers/pwm/pwm-gpio.c                        | 240 ++++++++++++++++++
+ 4 files changed, 258 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/pwm/pwm-gpio.c
+
+--- a/Documentation/driver-api/gpio/drivers-on-gpio.rst
++++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst
+@@ -27,7 +27,12 @@ hardware descriptions such as device tre
+   to the lines for a more permanent solution of this type.
+ 
+ - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from
+-  an external speaker connected to a GPIO line.
++  an external speaker connected to a GPIO line. (If the beep is controlled by
++  off/on, for an actual PWM waveform, see pwm-gpio below.)
++
++- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high
++  resolution timer producing a PWM waveform on the GPIO line, as well as
++  Linux high resolution timers can do.
+ 
+ - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an
+   external connector status, such as a headset line for an audio driver or an
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -217,6 +217,17 @@ config PWM_FSL_FTM
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called pwm-fsl-ftm.
+ 
++config PWM_GPIO
++	tristate "GPIO PWM support"
++	depends on GPIOLIB
++	depends on HIGH_RES_TIMERS
++	help
++	  Generic PWM framework driver for software PWM toggling a GPIO pin
++	  from kernel high-resolution timers.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called pwm-gpio.
++
+ config PWM_HIBVT
+ 	tristate "HiSilicon BVT PWM support"
+ 	depends on ARCH_HISI || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_PWM_CROS_EC)	+= pwm-cros-ec
+ obj-$(CONFIG_PWM_DWC)		+= pwm-dwc.o
+ obj-$(CONFIG_PWM_EP93XX)	+= pwm-ep93xx.o
+ obj-$(CONFIG_PWM_FSL_FTM)	+= pwm-fsl-ftm.o
++obj-$(CONFIG_PWM_GPIO)		+= pwm-gpio.o
+ obj-$(CONFIG_PWM_HIBVT)		+= pwm-hibvt.o
+ obj-$(CONFIG_PWM_IMG)		+= pwm-img.o
+ obj-$(CONFIG_PWM_IMX1)		+= pwm-imx1.o
+--- /dev/null
++++ b/drivers/pwm/pwm-gpio.c
+@@ -0,0 +1,240 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Generic software PWM for modulating GPIOs
++ *
++ * Copyright (C) 2020 Axis Communications AB
++ * Copyright (C) 2020 Nicola Di Lieto
++ * Copyright (C) 2024 Stefan Wahren
++ * Copyright (C) 2024 Linus Walleij
++ */
++
++#include <linux/cleanup.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/gpio/consumer.h>
++#include <linux/hrtimer.h>
++#include <linux/math.h>
++#include <linux/module.h>
++#include <linux/mod_devicetable.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/pwm.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/types.h>
++
++struct pwm_gpio {
++	struct hrtimer gpio_timer;
++	struct gpio_desc *gpio;
++	struct pwm_state state;
++	struct pwm_state next_state;
++
++	/* Protect internal state between pwm_ops and hrtimer */
++	spinlock_t lock;
++
++	bool changing;
++	bool running;
++	bool level;
++	struct pwm_chip chip;
++};
++
++static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src)
++{
++	u64 dividend;
++	u32 remainder;
++
++	*dest = *src;
++
++	/* Round down to hrtimer resolution */
++	dividend = dest->period;
++	remainder = do_div(dividend, hrtimer_resolution);
++	dest->period -= remainder;
++
++	dividend = dest->duty_cycle;
++	remainder = do_div(dividend, hrtimer_resolution);
++	dest->duty_cycle -= remainder;
++}
++
++static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level)
++{
++	const struct pwm_state *state = &gpwm->state;
++	bool invert = state->polarity == PWM_POLARITY_INVERSED;
++
++	gpwm->level = level;
++	gpiod_set_value(gpwm->gpio, gpwm->level ^ invert);
++
++	if (!state->duty_cycle || state->duty_cycle == state->period) {
++		gpwm->running = false;
++		return 0;
++	}
++
++	gpwm->running = true;
++	return level ? state->duty_cycle : state->period - state->duty_cycle;
++}
++
++static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer)
++{
++	struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio,
++					     gpio_timer);
++	u64 next_toggle;
++	bool new_level;
++
++	guard(spinlock_irqsave)(&gpwm->lock);
++
++	/* Apply new state at end of current period */
++	if (!gpwm->level && gpwm->changing) {
++		gpwm->changing = false;
++		gpwm->state = gpwm->next_state;
++		new_level = !!gpwm->state.duty_cycle;
++	} else {
++		new_level = !gpwm->level;
++	}
++
++	next_toggle = pwm_gpio_toggle(gpwm, new_level);
++	if (next_toggle)
++		hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer),
++				ns_to_ktime(next_toggle));
++
++	return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART;
++}
++
++static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++			  const struct pwm_state *state)
++{
++	struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip);
++	bool invert = state->polarity == PWM_POLARITY_INVERSED;
++
++	if (state->duty_cycle && state->duty_cycle < hrtimer_resolution)
++		return -EINVAL;
++
++	if (state->duty_cycle != state->period &&
++	    (state->period - state->duty_cycle < hrtimer_resolution))
++		return -EINVAL;
++
++	if (!state->enabled) {
++		hrtimer_cancel(&gpwm->gpio_timer);
++	} else if (!gpwm->running) {
++		int ret;
++
++		/*
++		 * This just enables the output, but pwm_gpio_toggle()
++		 * really starts the duty cycle.
++		 */
++		ret = gpiod_direction_output(gpwm->gpio, invert);
++		if (ret)
++			return ret;
++	}
++
++	guard(spinlock_irqsave)(&gpwm->lock);
++
++	if (!state->enabled) {
++		pwm_gpio_round(&gpwm->state, state);
++		gpwm->running = false;
++		gpwm->changing = false;
++
++		gpiod_set_value(gpwm->gpio, invert);
++	} else if (gpwm->running) {
++		pwm_gpio_round(&gpwm->next_state, state);
++		gpwm->changing = true;
++	} else {
++		unsigned long next_toggle;
++
++		pwm_gpio_round(&gpwm->state, state);
++		gpwm->changing = false;
++
++		next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle);
++		if (next_toggle)
++			hrtimer_start(&gpwm->gpio_timer, next_toggle,
++				      HRTIMER_MODE_REL);
++	}
++
++	return 0;
++}
++
++static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
++			       struct pwm_state *state)
++{
++	struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip);
++
++	guard(spinlock_irqsave)(&gpwm->lock);
++
++	if (gpwm->changing)
++		*state = gpwm->next_state;
++	else
++		*state = gpwm->state;
++
++	return 0;
++}
++
++static const struct pwm_ops pwm_gpio_ops = {
++	.apply = pwm_gpio_apply,
++	.get_state = pwm_gpio_get_state,
++};
++
++static void pwm_gpio_disable_hrtimer(void *data)
++{
++	struct pwm_gpio *gpwm = data;
++
++	hrtimer_cancel(&gpwm->gpio_timer);
++}
++
++static int pwm_gpio_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct pwm_chip *chip;
++	struct pwm_gpio *gpwm;
++	int ret;
++
++	gpwm = devm_kzalloc(&pdev->dev, sizeof(*gpwm), GFP_KERNEL);
++	if (IS_ERR(gpwm))
++		return PTR_ERR(gpwm);
++
++	chip = &gpwm->chip;
++
++	spin_lock_init(&gpwm->lock);
++
++	gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
++	if (IS_ERR(gpwm->gpio))
++		return dev_err_probe(dev, PTR_ERR(gpwm->gpio),
++				     "%pfw: could not get gpio\n",
++				     dev_fwnode(dev));
++
++	if (gpiod_cansleep(gpwm->gpio))
++		return dev_err_probe(dev, -EINVAL,
++				     "%pfw: sleeping GPIO not supported\n",
++				     dev_fwnode(dev));
++
++	chip->dev = dev;
++	chip->ops = &pwm_gpio_ops;
++	chip->atomic = true;
++	chip->npwm = 1;
++
++	hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++	ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm);
++	if (ret)
++		return ret;
++
++	gpwm->gpio_timer.function = pwm_gpio_timer;
++
++	return devm_pwmchip_add(dev, chip);
++}
++
++static const struct of_device_id pwm_gpio_dt_ids[] = {
++	{ .compatible = "pwm-gpio" },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids);
++
++static struct platform_driver pwm_gpio_driver = {
++	.driver = {
++		.name = "pwm-gpio",
++		.of_match_table = pwm_gpio_dt_ids,
++	},
++	.probe = pwm_gpio_probe,
++};
++module_platform_driver(pwm_gpio_driver);
++
++MODULE_DESCRIPTION("PWM GPIO driver");
++MODULE_AUTHOR("Vincent Whitchurch");
++MODULE_LICENSE("GPL");

+ 79 - 0
target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch

@@ -0,0 +1,79 @@
+From ff0fe12ab875d587348b6f2b9e73ae928049ebee Mon Sep 17 00:00:00 2001
+From: Tim Gover <[email protected]>
+Date: Thu, 31 Oct 2024 16:12:54 +0000
+Subject: [PATCH] dtoverlay: Add a dtoverlay for pwm-gpio
+
+Signed-off-by: Tim Gover <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  6 +++
+ .../boot/dts/overlays/pwm-gpio-overlay.dts    | 38 +++++++++++++++++++
+ 3 files changed, 45 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -217,6 +217,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	proto-codec.dtbo \
+ 	pwm.dtbo \
+ 	pwm-2chan.dtbo \
++	pwm-gpio.dtbo \
+ 	pwm-ir-tx.dtbo \
+ 	pwm1.dtbo \
+ 	qca7000.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3903,6 +3903,12 @@ Params: pin                     Output p
+         clock                   PWM clock frequency (informational)
+ 
+ 
++Name:   pwm-gpio
++Info:   Configures the software PWM GPIO driver
++Load:   dtoverlay=pwm-gpio,<param>=<val>
++Params: gpio                    Output pin (default 4)
++
++
+ Name:   pwm-ir-tx
+ Info:   Use GPIO pin as pwm-assisted infrared transmitter output.
+         This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts
+@@ -0,0 +1,38 @@
++// Device tree overlay for software GPIO PWM.
++/dts-v1/;
++/plugin/;
++
++/ {
++	compatible = "brcm,bcm2835";
++
++	fragment@0 {
++		target = <&gpio>;
++		__overlay__ {
++			pwm_gpio_pins: pwm_gpio_pins@4 {
++				brcm,pins = <4>; /* gpio 4 */
++				brcm,function = <1>; /* output */
++				brcm,pull = <0>; /* pull-none */
++			};
++		};
++	};
++
++	fragment@1 {
++		target-path = "/";
++		__overlay__ {
++			pwm_gpio: pwm_gpio@4 {
++				  compatible = "pwm-gpio";
++				  pinctrl-names = "default";
++				  pinctrl-0 = <&pwm_gpio_pins>;
++				  gpios = <&gpio 4 0>;
++			};
++		};
++	};
++
++	__overrides__ {
++		gpio = <&pwm_gpio>,"gpios:4",
++		       <&pwm_gpio_pins>,"brcm,pins:0",
++		       /* modify reg values to allow multiple instantiation */
++		       <&pwm_gpio>,"reg:0",
++		       <&pwm_gpio_pins>,"reg:0";
++	};
++};

+ 29 - 0
target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch

@@ -0,0 +1,29 @@
+From 624eb357e1a16385b3d6171e9194e4c5f8d4fd5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Wed, 23 Oct 2024 19:09:18 +0100
+Subject: [PATCH] dts: 2712: Drop some numa options from bootargs
+
+iommu_dma_numa_policy=interleave is not valid in the current tree
+It generates an unknown setting will be passed to usespace warning
+
+system_heap.max_order=0 is wanted when numa is enabled, but may not
+be when it is disabled.
+
+Add it on firmware side when we know if numa=fake=<n> is used.
+
+Signed-off-by: Dom Cobley <[email protected]>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -99,7 +99,7 @@
+ 
+ / {
+ 	chosen: chosen {
+-		bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0";
++		bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave";
+ 		stdout-path = "serial10:115200n8";
+ 	};
+ 

+ 42 - 0
target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch

@@ -0,0 +1,42 @@
+From 74f3ca5e39586ea26201fe6eaf1b9e6793b101b7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Tue, 29 Oct 2024 13:33:21 +0000
+Subject: [PATCH] mmc: quirks: add more broken Kingston Canvas Go! SD card date
+ ranges
+
+A user has reported that a card of this model from late 2021 doesn't
+work, so extend the date range and make it match on all card sizes.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/mmc/core/quirks.h | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/core/quirks.h
++++ b/drivers/mmc/core/quirks.h
+@@ -18,10 +18,22 @@
+ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = {
+ 	/*
+ 	 * Kingston Canvas Go! Plus microSD cards never finish SD cache flush.
+-	 * This has so far only been observed on cards from 11/2019, while new
+-	 * cards from 2023/05 do not exhibit this behavior.
++	 * This has been observed on cards from 2019/11 and 2021/11, while new
++	 * cards from 2023/05 and 2024/08 do not exhibit this behavior.
+ 	 */
+-	_FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11,
++	_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2019, CID_MONTH_ANY,
++		   0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++		   MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++	_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2020, CID_MONTH_ANY,
++		   0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++		   MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++	_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2021, CID_MONTH_ANY,
++		   0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++		   MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++	_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2022, CID_MONTH_ANY,
+ 		   0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
+ 		   MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
+ 

+ 35 - 0
target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch

@@ -0,0 +1,35 @@
+From 6c0f34fb0f83741f7f03f6bfd3fcbc89cb2c7cde Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Wed, 6 Nov 2024 10:26:55 +0000
+Subject: [PATCH] dt-bindings: usb: snps,dwc3: add FS/HS periodic NAK polling
+ quirk
+
+Add two quirk properties that control whether or not the controller
+issues many more handshakes to FS/HS Async endpoints in a single
+(micro)frame. Enabling these can significantly increase throughput for
+endpoints that frequently respond with NAKs.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
++++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
+@@ -231,6 +231,16 @@ properties:
+     description: When set, disable u2mac linestate check during HS transmit
+     type: boolean
+ 
++  snps,enhanced-nak-fs-quirk:
++    description:
++      When set, the controller schedules many more handshakes to Async FS
++      endpoints, improving throughput when they frequently respond with NAKs.
++
++  snps,enhanced-nak-hs-quirk:
++    description:
++      When set, the controller schedules many more handshakes to Async HS
++      endpoints, improving throughput when they frequently respond with NAKs.
++
+   snps,parkmode-disable-ss-quirk:
+     description:
+       When set, disable park mode for all Superspeed bus instances.

+ 77 - 0
target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch

@@ -0,0 +1,77 @@
+From bb53ca75f9e3631e753f397ccab704a8f975658b Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Wed, 6 Nov 2024 10:45:24 +0000
+Subject: [PATCH] usb: dwc3: core: add support for setting NAK enhancement bits
+ for FS/HS
+
+If a device frequently NAKs, it can exhaust the scheduled handshakes in
+a frame. It will then not get polled by the controller until the next
+frame interval. This is most noticeable on FS devices as the controller
+schedules a small set of transactions only once per full-speed frame.
+
+Setting the ENH_PER_NAK_FS/LS bits in the GUCTL1 register increases the
+number of transactions that can be scheduled to Async (Control/Bulk)
+endpoints in the respective frame time. In the FS case, this only
+applies to FS devices directly connected to root ports.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/usb/dwc3/core.c | 10 ++++++++++
+ drivers/usb/dwc3/core.h |  6 ++++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -1366,6 +1366,12 @@ static int dwc3_core_init(struct dwc3 *d
+ 		if (dwc->dis_tx_ipgap_linecheck_quirk)
+ 			reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+ 
++		if (dwc->enh_nak_fs_quirk)
++			reg |= DWC3_GUCTL1_NAK_PER_ENH_FS;
++
++		if (dwc->enh_nak_hs_quirk)
++			reg |= DWC3_GUCTL1_NAK_PER_ENH_HS;
++
+ 		if (dwc->parkmode_disable_ss_quirk)
+ 			reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+ 
+@@ -1669,6 +1675,10 @@ static void dwc3_get_properties(struct d
+ 				"snps,resume-hs-terminations");
+ 	dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev,
+ 				"snps,ulpi-ext-vbus-drv");
++	dwc->enh_nak_fs_quirk = device_property_read_bool(dev,
++				"snps,enhanced-nak-fs-quirk");
++	dwc->enh_nak_hs_quirk = device_property_read_bool(dev,
++				"snps,enhanced-nak-hs-quirk");
+ 	dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
+ 				"snps,parkmode-disable-ss-quirk");
+ 	dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev,
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -269,6 +269,8 @@
+ #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS	BIT(28)
+ #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK	BIT(26)
+ #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW		BIT(24)
++#define DWC3_GUCTL1_NAK_PER_ENH_FS		BIT(19)
++#define DWC3_GUCTL1_NAK_PER_ENH_HS		BIT(18)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_SS		BIT(17)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_HS		BIT(16)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS	BIT(15)
+@@ -1118,6 +1120,8 @@ struct dwc3_scratchpad_array {
+  *			generation after resume from suspend.
+  * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin
+  *			VBUS with an external supply.
++ * @enh_nak_fs_quirk: Set to schedule more handshakes to Async FS endpoints.
++ * @enh_nak_hs_quirk: Set to schedule more handshakes to Async HS endpoints.
+  * @parkmode_disable_ss_quirk: If set, disable park mode feature for all
+  *			Superspeed instances.
+  * @parkmode_disable_hs_quirk: If set, disable park mode feature for all
+@@ -1348,6 +1352,8 @@ struct dwc3 {
+ 	unsigned		dis_tx_ipgap_linecheck_quirk:1;
+ 	unsigned		resume_hs_terminations:1;
+ 	unsigned		ulpi_ext_vbus_drv:1;
++	unsigned		enh_nak_fs_quirk:1;
++	unsigned		enh_nak_hs_quirk:1;
+ 	unsigned		parkmode_disable_ss_quirk:1;
+ 	unsigned		parkmode_disable_hs_quirk:1;
+ 	unsigned		parkmode_disable_fsls_quirk:1;

+ 30 - 0
target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch

@@ -0,0 +1,30 @@
+From 803757627b48bdad9530b50053321fdea6dfcab4 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Wed, 6 Nov 2024 10:54:58 +0000
+Subject: [PATCH] DTS: rp1: set enhanced FS NAK quirk for usb3 controllers
+
+There seem to be only benefits, and no downsides.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ arch/arm64/boot/dts/broadcom/rp1.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -1077,6 +1077,7 @@
+ 			usb3-lpm-capable;
+ 			snps,axi-pipe-limit = /bits/ 8 <8>;
+ 			snps,dis_rxdet_inp3_quirk;
++			snps,enhanced-nak-fs-quirk;
+ 			snps,parkmode-disable-ss-quirk;
+ 			snps,parkmode-disable-hs-quirk;
+ 			snps,parkmode-disable-fsls-quirk;
+@@ -1093,6 +1094,7 @@
+ 			usb3-lpm-capable;
+ 			snps,axi-pipe-limit = /bits/ 8 <8>;
+ 			snps,dis_rxdet_inp3_quirk;
++			snps,enhanced-nak-fs-quirk;
+ 			snps,parkmode-disable-ss-quirk;
+ 			snps,parkmode-disable-hs-quirk;
+ 			snps,parkmode-disable-fsls-quirk;

+ 50 - 0
target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch

@@ -0,0 +1,50 @@
+From e9e852af347ae3ccee4e7abb01f9ef91387980f9 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Wed, 6 Nov 2024 11:07:55 +0000
+Subject: [PATCH] drivers: usb: xhci: prevent a theoretical race on
+ non-coherent platforms
+
+For platforms that have xHCI controllers attached over PCIe, and
+non-coherent routes to main memory, a theoretical race exists between
+posting new TRBs to a ring, and writing to the doorbell register.
+
+In a contended system, write traffic from the CPU may be stalled before
+the memory controller, whereas the CPU to Endpoint route is separate
+and not likely to be contended. Similarly, the DMA route from the
+endpoint to main memory may be separate and uncontended.
+
+Therefore the xHCI can receive a doorbell write and find a stale view
+of a transfer ring. In cases where only a single TRB is ping-ponged at
+a time, this can cause the endpoint to not get polled at all.
+
+Adding a readl() before the write forces a round-trip transaction
+across PCIe, definitively serialising the CPU along the PCI
+producer-consumer ordering rules.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/usb/host/xhci-ring.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -505,6 +505,19 @@ void xhci_ring_ep_doorbell(struct xhci_h
+ 
+ 	trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
+ 
++	/*
++	 * For non-coherent systems with PCIe DMA (such as Pi 4, Pi 5) there
++	 * is a theoretical race between the TRB write and barrier, which
++	 * is reported complete as soon as the write leaves the CPU domain,
++	 * the doorbell write, which may be reported as complete by the RC
++	 * at some arbitrary point, and the visibility of new TRBs in system
++	 * RAM by the endpoint DMA engine.
++	 *
++	 * This read before the write positively serialises the CPU state
++	 * by incurring a round-trip across the link.
++	 */
++	readl(db_addr);
++
+ 	writel(DB_VALUE(ep_index, stream_id), db_addr);
+ 	/* flush the write */
+ 	readl(db_addr);

+ 32 - 0
target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch

@@ -0,0 +1,32 @@
+From ce65ed02cb6707ae5c9f3a304f5b0124f4eed559 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 4 Nov 2024 14:10:53 +0000
+Subject: [PATCH] iio: humidity: dht11: Allow non-zero decimals
+
+The DHT11 datasheet is pretty cryptic, but it does suggest that after
+each integer value (humidity and temperature) there are "decimal"
+values. Validate these as integers in the range 0-9 and treat them as
+tenths of a unit.
+
+Link: https://github.com/raspberrypi/linux/issues/6220
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/iio/humidity/dht11.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/iio/humidity/dht11.c
++++ b/drivers/iio/humidity/dht11.c
+@@ -152,9 +152,9 @@ static int dht11_decode(struct dht11 *dh
+ 		dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
+ 					((temp_int & 0x80) ? -100 : 100);
+ 		dht11->humidity = ((hum_int << 8) + hum_dec) * 100;
+-	} else if (temp_dec == 0 && hum_dec == 0) {  /* DHT11 */
+-		dht11->temperature = temp_int * 1000;
+-		dht11->humidity = hum_int * 1000;
++	} else if (temp_dec < 10 && hum_dec < 10) {  /* DHT11 */
++		dht11->temperature = temp_int * 1000 + temp_dec * 100;
++		dht11->humidity = hum_int * 1000 + hum_dec * 100;
+ 	} else {
+ 		dev_err(dht11->dev,
+ 			"Don't know how to decode data: %d %d %d %d\n",

+ 41 - 0
target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch

@@ -0,0 +1,41 @@
+From c3393ac1098d1f191e37eed73bf366ebc88ac4ee Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 11 Sep 2024 14:49:05 +0100
+Subject: [PATCH] drm/vc4: Correct condition for ignoring a plane to src rect
+ =0, not <1.0
+
+The logic for dropping a plane less than zero didn't account for the
+possibility that a plane could be being upscaled with a src_rect with
+width/height < 1 pixel, but not 0 subpixels.
+
+Check for not 0 subpixels, not < 1, in both vc4 and vc6 paths.
+
+Fixes: dac616899f87 ("drm/vc4: Drop planes that have 0 destination size")
+Fixes: f73b18eb0d48 ("drm/vc4: Drop planes that are completely off-screen")
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1160,7 +1160,8 @@ 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 || !vc4_state->crtc_w || !vc4_state->crtc_h) {
++	if (!vc4_state->src_w[0] || !vc4_state->src_h[0] ||
++	    !vc4_state->crtc_w || !vc4_state->crtc_h) {
+ 		/* 0 source size probably means the plane is offscreen */
+ 		vc4_state->dlist_initialized = 1;
+ 		return 0;
+@@ -1698,7 +1699,8 @@ 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 || !vc4_state->crtc_w || !vc4_state->crtc_h) {
++	if (!vc4_state->src_w[0] || !vc4_state->src_h[0] ||
++	    !vc4_state->crtc_w || !vc4_state->crtc_h) {
+ 		/* 0 source size probably means the plane is offscreen.
+ 		 * 0 destination size is a redundant plane.
+ 		 */

+ 59 - 0
target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch

@@ -0,0 +1,59 @@
+From ca621585c573cae54dc1235d90822e8bcef2f73d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 11 Sep 2024 15:23:33 +0100
+Subject: [PATCH] drm/vc4: Use the TPZ scaling filter for 1x1 source images
+
+The documentation says that the TPZ filter can not upscale,
+and requesting a scaling factor > 1:1 will output the original
+image in the top left, and repeat the right/bottom most pixels
+thereafter.
+That fits perfectly with upscaling a 1x1 image which is done
+a fair amount by some compositors to give solid colour, and it
+saves a large amount of LBM (TPZ is based on src size, whilst
+PPF is based on dest size).
+
+Select TPZ filter for images with source rectangle <=1.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -265,7 +265,11 @@ static enum vc4_scaling_mode vc4_get_sca
+ {
+ 	if (dst == src >> 16)
+ 		return VC4_SCALING_NONE;
+-	if (3 * dst >= 2 * (src >> 16))
++
++	if (src <= (1 << 16))
++		/* Source rectangle <= 1 pixel can use TPZ for resize/upscale */
++		return VC4_SCALING_TPZ;
++	else if (3 * dst >= 2 * (src >> 16))
+ 		return VC4_SCALING_PPF;
+ 	else
+ 		return VC4_SCALING_TPZ;
+@@ -560,12 +564,17 @@ static void vc4_write_tpz(struct vc4_pla
+ 
+ 	WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
+ 
+-	scale = src / dst;
++	if ((dst << 16) < src) {
++		scale = src / dst;
+ 
+-	/* The specs note that while the reciprocal would be defined
+-	 * as (1<<32)/scale, ~0 is close enough.
+-	 */
+-	recip = ~0 / scale;
++		/* The specs note that while the reciprocal would be defined
++		 * as (1<<32)/scale, ~0 is close enough.
++		 */
++		recip = ~0 / scale;
++	} else {
++		scale = (1 << 16) + 1;
++		recip = (1 << 16) - 1;
++	}
+ 
+ 	vc4_dlist_write(vc4_state,
+ 			/*

+ 30 - 0
target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch

@@ -0,0 +1,30 @@
+From 68b0ff3549148e614e1733d773cee8e689c763c6 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 20 Aug 2024 16:25:10 +0100
+Subject: [PATCH] drm: Set non-desktop property to true for writeback and
+ virtual connectors
+
+The non-desktop property "Indicates the output should be ignored for
+purposes of displaying a standard desktop environment or console."
+
+That sounds like it should be true for all writeback and virtual
+connectors as you shouldn't render a desktop to them, so set it
+by default.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_connector.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -361,7 +361,8 @@ static int __drm_connector_init(struct d
+ 
+ 	drm_object_attach_property(&connector->base,
+ 				   config->non_desktop_property,
+-				   0);
++				   (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
++				   connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1;
+ 	drm_object_attach_property(&connector->base,
+ 				   config->tile_property,
+ 				   0);

+ 101 - 0
target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch

@@ -0,0 +1,101 @@
+From 8181e682d6f4ef209845ec24f0a1eb37764d6731 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Fri, 21 Oct 2022 14:26:12 +0100
+Subject: [PATCH] drm: Increase plane_mask to 64bit.
+
+The limit of 32 planes per DRM device is dictated by the use
+of planes_mask returning a u32.
+
+Change to a u64 such that 64 planes can be supported by a device.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_atomic.c           | 2 +-
+ drivers/gpu/drm/drm_framebuffer.c      | 2 +-
+ drivers/gpu/drm/drm_mode_config.c      | 2 +-
+ drivers/gpu/drm/drm_plane.c            | 2 +-
+ drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c | 2 +-
+ include/drm/drm_crtc.h                 | 2 +-
+ include/drm/drm_plane.h                | 4 ++--
+ 7 files changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/drm_atomic.c
++++ b/drivers/gpu/drm/drm_atomic.c
+@@ -451,7 +451,7 @@ static void drm_atomic_crtc_print_state(
+ 	drm_printf(p, "\tactive_changed=%d\n", state->active_changed);
+ 	drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed);
+ 	drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed);
+-	drm_printf(p, "\tplane_mask=%x\n", state->plane_mask);
++	drm_printf(p, "\tplane_mask=%llx\n", state->plane_mask);
+ 	drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask);
+ 	drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask);
+ 	drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode));
+--- a/drivers/gpu/drm/drm_framebuffer.c
++++ b/drivers/gpu/drm/drm_framebuffer.c
+@@ -959,7 +959,7 @@ static int atomic_remove_fb(struct drm_f
+ 	struct drm_connector *conn __maybe_unused;
+ 	struct drm_connector_state *conn_state;
+ 	int i, ret;
+-	unsigned plane_mask;
++	u64 plane_mask;
+ 	bool disable_crtcs = false;
+ 
+ retry_disable:
+--- a/drivers/gpu/drm/drm_mode_config.c
++++ b/drivers/gpu/drm/drm_mode_config.c
+@@ -636,7 +636,7 @@ void drm_mode_config_validate(struct drm
+ 	struct drm_encoder *encoder;
+ 	struct drm_crtc *crtc;
+ 	struct drm_plane *plane;
+-	u32 primary_with_crtc = 0, cursor_with_crtc = 0;
++	u64 primary_with_crtc = 0, cursor_with_crtc = 0;
+ 	unsigned int num_primary = 0;
+ 
+ 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+--- a/drivers/gpu/drm/drm_plane.c
++++ b/drivers/gpu/drm/drm_plane.c
+@@ -249,7 +249,7 @@ static int __drm_universal_plane_init(st
+ 	int ret;
+ 
+ 	/* plane index is used with 32bit bitmasks */
+-	if (WARN_ON(config->num_total_plane >= 32))
++	if (WARN_ON(config->num_total_plane >= 64))
+ 		return -EINVAL;
+ 
+ 	/*
+--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
++++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
+@@ -230,7 +230,7 @@ static int ipu_crtc_atomic_check(struct
+ {
+ 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+ 									  crtc);
+-	u32 primary_plane_mask = drm_plane_mask(crtc->primary);
++	u64 primary_plane_mask = drm_plane_mask(crtc->primary);
+ 
+ 	if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0)
+ 		return -EINVAL;
+--- a/include/drm/drm_crtc.h
++++ b/include/drm/drm_crtc.h
+@@ -192,7 +192,7 @@ struct drm_crtc_state {
+ 	 * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to
+ 	 * this CRTC.
+ 	 */
+-	u32 plane_mask;
++	u64 plane_mask;
+ 
+ 	/**
+ 	 * @connector_mask: Bitmask of drm_connector_mask(connector) of
+--- a/include/drm/drm_plane.h
++++ b/include/drm/drm_plane.h
+@@ -915,9 +915,9 @@ static inline unsigned int drm_plane_ind
+  * drm_plane_mask - find the mask of a registered plane
+  * @plane: plane to find mask for
+  */
+-static inline u32 drm_plane_mask(const struct drm_plane *plane)
++static inline u64 drm_plane_mask(const struct drm_plane *plane)
+ {
+-	return 1 << drm_plane_index(plane);
++	return 1ULL << drm_plane_index(plane);
+ }
+ 
+ struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx);

+ 42 - 0
target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch

@@ -0,0 +1,42 @@
+From 5dc4cef7d7fcda4ea59b9e456a835fa54336af6b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Fri, 21 Oct 2022 14:27:45 +0100
+Subject: [PATCH] drm/vc4: Increase number of overlay planes from 16 to 48
+
+The HVS can accept an arbitrary number of planes, provided
+that the overall pixel read load is within limits, and
+the display list can fit into the dlist memory.
+
+Now that DRM will support 64 planes per device, increase
+the number of overlay planes from 16 to 48 so that the
+dlist complexity can be increased (eg 4x4 video wall on
+each of 3 displays).
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_connector.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_plane.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -362,7 +362,7 @@ static int __drm_connector_init(struct d
+ 	drm_object_attach_property(&connector->base,
+ 				   config->non_desktop_property,
+ 				   (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
+-				   connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1;
++				   connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1);
+ 	drm_object_attach_property(&connector->base,
+ 				   config->tile_property,
+ 				   0);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2517,7 +2517,7 @@ struct drm_plane *vc4_plane_init(struct
+ 	return plane;
+ }
+ 
+-#define VC4_NUM_OVERLAY_PLANES	16
++#define VC4_NUM_OVERLAY_PLANES	48
+ 
+ int vc4_plane_create_additional_planes(struct drm_device *drm)
+ {

+ 71 - 0
target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch

@@ -0,0 +1,71 @@
+From dd340cb082a020fbd42b794493ffd063dd8e15b4 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 15 Aug 2023 15:44:34 +0100
+Subject: [PATCH] drm/vc4: Assign 32 overlay planes to writeback only
+
+Instead of having 48 generic overlay planes, assign 32 to the
+writeback connector so that there is no ambiguity in wlroots
+when trying to find a plane for composition using the writeback
+connector vs display.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 34 +++++++++++++++++++++++++++++++--
+ 1 file changed, 32 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2517,13 +2517,28 @@ struct drm_plane *vc4_plane_init(struct
+ 	return plane;
+ }
+ 
+-#define VC4_NUM_OVERLAY_PLANES	48
++#define VC4_NUM_OVERLAY_PLANES	16
++#define VC4_NUM_TXP_OVERLAY_PLANES 32
+ 
+ int vc4_plane_create_additional_planes(struct drm_device *drm)
+ {
+ 	struct drm_plane *cursor_plane;
+ 	struct drm_crtc *crtc;
+ 	unsigned int i;
++	struct drm_crtc *txp_crtc;
++	uint32_t non_txp_crtc_mask;
++
++	drm_for_each_crtc(crtc, drm) {
++		struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++
++		if (vc4_crtc->feeds_txp) {
++			txp_crtc = crtc;
++			break;
++		}
++	}
++
++	non_txp_crtc_mask = GENMASK(drm->mode_config.num_crtc - 1, 0) -
++					drm_crtc_mask(txp_crtc);
+ 
+ 	/* Set up some arbitrary number of planes.  We're not limited
+ 	 * by a set number of physical registers, just the space in
+@@ -2537,7 +2552,22 @@ int vc4_plane_create_additional_planes(s
+ 	for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) {
+ 		struct drm_plane *plane =
+ 			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
+-				       GENMASK(drm->mode_config.num_crtc - 1, 0));
++				       non_txp_crtc_mask);
++
++		if (IS_ERR(plane))
++			continue;
++
++		/* Create zpos property. Max of all the overlays + 1 primary +
++		 * 1 cursor plane on a crtc.
++		 */
++		drm_plane_create_zpos_property(plane, i + 1, 1,
++					       VC4_NUM_OVERLAY_PLANES + 1);
++	}
++
++	for (i = 0; i < VC4_NUM_TXP_OVERLAY_PLANES; i++) {
++		struct drm_plane *plane =
++			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
++				       drm_crtc_mask(txp_crtc));
+ 
+ 		if (IS_ERR(plane))
+ 			continue;

+ 47 - 0
target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch

@@ -0,0 +1,47 @@
+From b3b3d12cf0734318a0fed0b33e13d714188369db Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 22 Oct 2024 17:17:31 +0100
+Subject: [PATCH] drm: Add a DRM_MODE_TRANSPOSE option to the DRM rotation
+ property
+
+Some hardware will implement transpose as a rotation operation,
+which when combined with X and Y reflect can result in a rotation,
+but is a discrete operation in its own right.
+
+Add an option for transpose only.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_blend.c | 3 +++
+ include/uapi/drm/drm_mode.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/drm_blend.c
++++ b/drivers/gpu/drm/drm_blend.c
+@@ -263,6 +263,8 @@ EXPORT_SYMBOL(drm_plane_create_alpha_pro
+  * 	"reflect-x"
+  * DRM_MODE_REFLECT_Y:
+  * 	"reflect-y"
++ * DRM_MODE_TRANSPOSE:
++ * 	"transpose"
+  *
+  * Rotation is the specified amount in degrees in counter clockwise direction,
+  * the X and Y axis are within the source rectangle, i.e.  the X/Y axis before
+@@ -280,6 +282,7 @@ int drm_plane_create_rotation_property(s
+ 		{ __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
+ 		{ __builtin_ffs(DRM_MODE_REFLECT_X) - 1,  "reflect-x" },
+ 		{ __builtin_ffs(DRM_MODE_REFLECT_Y) - 1,  "reflect-y" },
++		{ __builtin_ffs(DRM_MODE_TRANSPOSE) - 1,  "transpose" },
+ 	};
+ 	struct drm_property *prop;
+ 
+--- a/include/uapi/drm/drm_mode.h
++++ b/include/uapi/drm/drm_mode.h
+@@ -203,6 +203,7 @@ extern "C" {
+  */
+ #define DRM_MODE_REFLECT_X      (1<<4)
+ #define DRM_MODE_REFLECT_Y      (1<<5)
++#define DRM_MODE_TRANSPOSE      (1<<6)
+ 
+ /*
+  * DRM_MODE_REFLECT_MASK

+ 164 - 0
target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch

@@ -0,0 +1,164 @@
+From 8fec3ff870499256f2c18fe7983f6ed3fea4faaf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 22 Oct 2024 17:22:40 +0100
+Subject: [PATCH] drm: Add a rotation parameter to connectors.
+
+Some connectors, particularly writeback, can implement flip
+or transpose operations as writing back to memory.
+
+Add a connector rotation property to control this.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c |  4 +++
+ drivers/gpu/drm/drm_blend.c       | 50 ++++++++++++++++++++++++-------
+ include/drm/drm_blend.h           |  5 ++++
+ include/drm/drm_connector.h       | 11 +++++++
+ 4 files changed, 60 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -811,6 +811,8 @@ static int drm_atomic_connector_set_prop
+ 		state->max_requested_bpc = val;
+ 	} else if (property == connector->privacy_screen_sw_state_property) {
+ 		state->privacy_screen_sw_state = val;
++	} else if (property == connector->rotation_property) {
++		state->rotation = val;
+ 	} else if (connector->funcs->atomic_set_property) {
+ 		return connector->funcs->atomic_set_property(connector,
+ 				state, property, val);
+@@ -900,6 +902,8 @@ drm_atomic_connector_get_property(struct
+ 		*val = state->max_requested_bpc;
+ 	} else if (property == connector->privacy_screen_sw_state_property) {
+ 		*val = state->privacy_screen_sw_state;
++	} else if (property == connector->rotation_property) {
++		*val = state->rotation;
+ 	} else if (connector->funcs->atomic_get_property) {
+ 		return connector->funcs->atomic_get_property(connector,
+ 				state, property, val);
+--- a/drivers/gpu/drm/drm_blend.c
++++ b/drivers/gpu/drm/drm_blend.c
+@@ -235,6 +235,16 @@ int drm_plane_create_alpha_property(stru
+ }
+ EXPORT_SYMBOL(drm_plane_create_alpha_property);
+ 
++static const struct drm_prop_enum_list drm_rotate_props[] = {
++	{ __builtin_ffs(DRM_MODE_ROTATE_0) - 1,   "rotate-0" },
++	{ __builtin_ffs(DRM_MODE_ROTATE_90) - 1,  "rotate-90" },
++	{ __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" },
++	{ __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
++	{ __builtin_ffs(DRM_MODE_REFLECT_X) - 1,  "reflect-x" },
++	{ __builtin_ffs(DRM_MODE_REFLECT_Y) - 1,  "reflect-y" },
++	{ __builtin_ffs(DRM_MODE_TRANSPOSE) - 1,  "transpose" },
++};
++
+ /**
+  * drm_plane_create_rotation_property - create a new rotation property
+  * @plane: drm plane
+@@ -275,15 +285,6 @@ int drm_plane_create_rotation_property(s
+ 				       unsigned int rotation,
+ 				       unsigned int supported_rotations)
+ {
+-	static const struct drm_prop_enum_list props[] = {
+-		{ __builtin_ffs(DRM_MODE_ROTATE_0) - 1,   "rotate-0" },
+-		{ __builtin_ffs(DRM_MODE_ROTATE_90) - 1,  "rotate-90" },
+-		{ __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" },
+-		{ __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
+-		{ __builtin_ffs(DRM_MODE_REFLECT_X) - 1,  "reflect-x" },
+-		{ __builtin_ffs(DRM_MODE_REFLECT_Y) - 1,  "reflect-y" },
+-		{ __builtin_ffs(DRM_MODE_TRANSPOSE) - 1,  "transpose" },
+-	};
+ 	struct drm_property *prop;
+ 
+ 	WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0);
+@@ -291,7 +292,8 @@ int drm_plane_create_rotation_property(s
+ 	WARN_ON(rotation & ~supported_rotations);
+ 
+ 	prop = drm_property_create_bitmask(plane->dev, 0, "rotation",
+-					   props, ARRAY_SIZE(props),
++					   drm_rotate_props,
++					   ARRAY_SIZE(drm_rotate_props),
+ 					   supported_rotations);
+ 	if (!prop)
+ 		return -ENOMEM;
+@@ -307,6 +309,34 @@ int drm_plane_create_rotation_property(s
+ }
+ EXPORT_SYMBOL(drm_plane_create_rotation_property);
+ 
++int drm_connector_create_rotation_property(struct drm_connector *conn,
++					   unsigned int rotation,
++					   unsigned int supported_rotations)
++{
++	struct drm_property *prop;
++
++	WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0);
++	WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK));
++	WARN_ON(rotation & ~supported_rotations);
++
++	prop = drm_property_create_bitmask(conn->dev, 0, "rotation",
++					   drm_rotate_props,
++					   ARRAY_SIZE(drm_rotate_props),
++					   supported_rotations);
++	if (!prop)
++		return -ENOMEM;
++
++	drm_object_attach_property(&conn->base, prop, rotation);
++
++	if (conn->state)
++		conn->state->rotation = rotation;
++
++	conn->rotation_property = prop;
++
++	return 0;
++}
++EXPORT_SYMBOL(drm_connector_create_rotation_property);
++
+ /**
+  * drm_rotation_simplify() - Try to simplify the rotation
+  * @rotation: Rotation to be simplified
+--- a/include/drm/drm_blend.h
++++ b/include/drm/drm_blend.h
+@@ -34,6 +34,7 @@
+ struct drm_device;
+ struct drm_atomic_state;
+ struct drm_plane;
++struct drm_connector;
+ 
+ static inline bool drm_rotation_90_or_270(unsigned int rotation)
+ {
+@@ -58,4 +59,8 @@ int drm_atomic_normalize_zpos(struct drm
+ 			      struct drm_atomic_state *state);
+ int drm_plane_create_blend_mode_property(struct drm_plane *plane,
+ 					 unsigned int supported_modes);
++
++int drm_connector_create_rotation_property(struct drm_connector *conn,
++					   unsigned int rotation,
++					   unsigned int supported_rotations);
+ #endif
+--- a/include/drm/drm_connector.h
++++ b/include/drm/drm_connector.h
+@@ -1029,6 +1029,11 @@ struct drm_connector_state {
+ 	 * DRM blob property for HDR output metadata
+ 	 */
+ 	struct drm_property_blob *hdr_output_metadata;
++
++	/**
++	 * @rotation: Connector property to rotate the maximum output image.
++	 */
++	u32 rotation;
+ };
+ 
+ /**
+@@ -1696,6 +1701,12 @@ struct drm_connector {
+ 	 */
+ 	struct drm_property *privacy_screen_hw_state_property;
+ 
++	/**
++	 * @rotation_property: Optional DRM property controlling rotation of the
++	 * output.
++	 */
++	struct drm_property *rotation_property;
++
+ #define DRM_CONNECTOR_POLL_HPD (1 << 0)
+ #define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
+ #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)

+ 65 - 0
target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch

@@ -0,0 +1,65 @@
+From 8346446098032c62d1de891a97c7f62264b18f81 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 14 Aug 2024 16:41:07 +0100
+Subject: [PATCH] drm/vc4: txp: Add a rotation property to the writeback
+ connector
+
+The txp block can implement transpose as it writes out the image
+data, so expose that through the new connector rotation property.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -15,6 +15,7 @@
+ 
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
++#include <drm/drm_blend.h>
+ #include <drm/drm_drv.h>
+ #include <drm/drm_edid.h>
+ #include <drm/drm_fb_dma_helper.h>
+@@ -259,10 +260,15 @@ static int vc4_txp_connector_atomic_chec
+ 	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ 
+ 	fb = conn_state->writeback_job->fb;
+-	if (fb->width != crtc_state->mode.hdisplay ||
+-	    fb->height != crtc_state->mode.vdisplay) {
+-		DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
+-			      fb->width, fb->height);
++	if ((conn_state->rotation == DRM_MODE_ROTATE_0 &&
++	     fb->width != crtc_state->mode.hdisplay &&
++	     fb->height != crtc_state->mode.vdisplay) ||
++	    (conn_state->rotation == (DRM_MODE_ROTATE_0 | DRM_MODE_TRANSPOSE) &&
++	     fb->width != crtc_state->mode.vdisplay &&
++	     fb->height != crtc_state->mode.hdisplay)) {
++		DRM_DEBUG_KMS("Invalid framebuffer size %ux%u vs mode %ux%u\n",
++			      fb->width, fb->height,
++			      crtc_state->mode.hdisplay, crtc_state->mode.vdisplay);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -330,6 +336,9 @@ static void vc4_txp_connector_atomic_com
+ 		 */
+ 		ctrl |= TXP_ALPHA_INVERT;
+ 
++	if (conn_state->rotation & DRM_MODE_TRANSPOSE)
++		ctrl |= TXP_TRANSPOSE;
++
+ 	if (!drm_dev_enter(drm, &idx))
+ 		return;
+ 
+@@ -608,6 +617,10 @@ static int vc4_txp_bind(struct device *d
+ 	if (ret)
+ 		return ret;
+ 
++	drm_connector_create_rotation_property(&txp->connector.base, DRM_MODE_ROTATE_0,
++					       DRM_MODE_ROTATE_0 |
++					       DRM_MODE_TRANSPOSE);
++
+ 	ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
+ 			       dev_name(dev), txp);
+ 	if (ret)

+ 40 - 0
target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch

@@ -0,0 +1,40 @@
+From a2fa911d90495762047c05dec4241308ae61ca36 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 19 Sep 2024 18:05:00 +0100
+Subject: [PATCH] dmaengine: dw-axi-dmac: Allow client-chosen width
+
+For devices where transfer lengths are not known upfront, there is a
+danger when the destination is wider than the source that partial words
+can be lost at the end of a transfer. Ideally the controller would be
+able to flush the residue, but it can't - it's not even possible to tell
+that there is any.
+
+Instead, allow the client driver to avoid the problem by setting a
+smaller width.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -724,6 +724,18 @@ static int dw_axi_dma_set_hw_desc(struct
+ 	case DMA_DEV_TO_MEM:
+ 		reg_burst_msize = axi_dma_encode_msize(chan->config.src_maxburst);
+ 		reg_width = __ffs(chan->config.src_addr_width);
++		/*
++		 * For devices where transfer lengths are not known upfront,
++		 * there is a danger when the destination is wider than the
++		 * source that partial words can be lost at the end of a transfer.
++		 * Ideally the controller would be able to flush the residue, but
++		 * it can't - it's not even possible to tell that there is any.
++		 * Instead, allow the client driver to avoid the problem by setting
++		 * a smaller width.
++		 */
++		if (chan->config.dst_addr_width &&
++		    (chan->config.dst_addr_width < mem_width))
++			mem_width = chan->config.dst_addr_width;
+ 		device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr);
+ 		ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS |
+ 			mem_width << CH_CTL_L_DST_WIDTH_POS |

+ 31 - 0
target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch

@@ -0,0 +1,31 @@
+From 5cf7209c294a58029984880d4858e2d3c7e46a3c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 19 Sep 2024 18:12:12 +0100
+Subject: [PATCH] spi: dw: Let the DMAC set the transfer widths
+
+SPI transfers are of defined length, unlike some UART traffic, so it is
+safe to let the DMA controller choose a suitable memory width.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/spi/spi-dw-dma.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/spi/spi-dw-dma.c
++++ b/drivers/spi/spi-dw-dma.c
+@@ -330,7 +330,6 @@ static int dw_spi_dma_config_tx(struct d
+ 	txconf.direction = DMA_MEM_TO_DEV;
+ 	txconf.dst_addr = dws->dma_addr;
+ 	txconf.dst_maxburst = dws->txburst;
+-	txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ 	txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+ 	txconf.device_fc = false;
+ 
+@@ -431,7 +430,6 @@ static int dw_spi_dma_config_rx(struct d
+ 	rxconf.direction = DMA_DEV_TO_MEM;
+ 	rxconf.src_addr = dws->dma_addr;
+ 	rxconf.src_maxburst = dws->rxburst;
+-	rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ 	rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+ 	rxconf.device_fc = false;
+ 

+ 25 - 0
target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch

@@ -0,0 +1,25 @@
+From 8894298105f4cb41dfa41e0b0d3c40c3f7b92c44 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 19 Sep 2024 18:22:24 +0100
+Subject: [PATCH] serial: pl011: Request a memory width of 1 byte
+
+In order to avoid losing residue bytes when a receive is terminated
+early, set the destination width to single bytes.
+
+Link: https://github.com/raspberrypi/linux/issues/6365
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/tty/serial/amba-pl011.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -468,6 +468,7 @@ static void pl011_dma_probe(struct uart_
+ 			.src_addr = uap->port.mapbase +
+ 				pl011_reg_to_offset(uap, REG_DR),
+ 			.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
++			.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ 			.direction = DMA_DEV_TO_MEM,
+ 			.src_maxburst = uap->fifosize >> 2,
+ 			.device_fc = false,

+ 56 - 0
target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch

@@ -0,0 +1,56 @@
+From 66aef6ce3557edd9d58d794e4a800c5be49ca0e7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <[email protected]>
+Date: Mon, 11 Nov 2024 10:30:38 +0000
+Subject: [PATCH] drivers: usb: xhci: set HID bit in streaming endpoint
+ contexts
+
+The xHC may commence Host Initiated Data Moves for streaming endpoints -
+see USB3.2 spec s8.12.1.4.2.4. However, this behaviour is typically
+counterproductive as the submission of UAS URBs in {Status, Data,
+Command} order and 1 outstanding IO per stream ID means the device never
+enters Move Data after a HIMD for Status or Data stages with the same
+stream ID. For OUT transfers this is especially inefficient as the host
+will start transmitting multiple bulk packets as a burst, all of which
+get NAKed by the device - wasting bandwidth.
+
+Also, some buggy UAS adapters don't properly handle the EP flow control
+state this creates - e.g. RTL9210.
+
+Set Host Initiated Data Move Disable to always defer stream selection to
+the device. xHC implementations may treat this field as "don't care,
+forced to 1" anyway - xHCI 1.2 s4.12.1.
+
+Signed-off-by: Jonathan Bell <[email protected]>
+---
+ drivers/usb/host/xhci-mem.c | 8 ++++++++
+ drivers/usb/host/xhci.h     | 2 ++
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -716,6 +716,14 @@ void xhci_setup_streams_ep_input_ctx(str
+ 	ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK);
+ 	ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams)
+ 				       | EP_HAS_LSA);
++
++	/*
++	 * Set Host Initiated Data Move Disable to always defer stream
++	 * selection to the device. xHC implementations may treat this
++	 * field as "don't care, forced to 1" anyway - xHCI 1.2 s4.12.1.
++	 */
++	ep_ctx->ep_info2 |= EP_HID;
++
+ 	ep_ctx->deq  = cpu_to_le64(stream_info->ctx_array_dma);
+ }
+ 
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -492,6 +492,8 @@ struct xhci_ep_ctx {
+ #define CTX_TO_EP_MAXPSTREAMS(p)	(((p) & EP_MAXPSTREAMS_MASK) >> 10)
+ /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
+ #define	EP_HAS_LSA		(1 << 15)
++/* Host initiated data move disable in info2 */
++#define EP_HID			(1 << 7)
+ /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */
+ #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p)	(((p) >> 24) & 0xff)
+ 

+ 199 - 0
target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch

@@ -0,0 +1,199 @@
+From 35e50ee3d66e014d869f0d7a3468bef964d26d32 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Thu, 14 Nov 2024 13:14:02 +0000
+Subject: [PATCH] media: i2c: imx477: Add options for slightly modifying the
+ link freq
+
+The default link frequency of 450MHz has been noted to interfere
+with GPS if they are in close proximty.
+Add the option for 453 and 456MHz to move the signal slightly out
+of the band. (447MHz can not be offered as corruption is then observed
+on the 133x992 10bit mode).
+
+Signed-off-by: Dave Stevenson <[email protected]>
+
+fixup imx477 gps
+---
+ drivers/media/i2c/imx477.c | 86 +++++++++++++++++++++++++++++---------
+ 1 file changed, 67 insertions(+), 19 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -164,8 +164,48 @@ struct imx477_mode {
+ 	struct imx477_reg_list reg_list;
+ };
+ 
+-static const s64 imx477_link_freq_menu[] = {
+-	IMX477_DEFAULT_LINK_FREQ,
++/* Link frequency setup */
++enum {
++	IMX477_LINK_FREQ_450MHZ,
++	IMX477_LINK_FREQ_453MHZ,
++	IMX477_LINK_FREQ_456MHZ,
++};
++
++static const s64 link_freqs[] = {
++	[IMX477_LINK_FREQ_450MHZ] = 450000000,
++	[IMX477_LINK_FREQ_453MHZ] = 453000000,
++	[IMX477_LINK_FREQ_456MHZ] = 456000000,
++};
++
++/* 450MHz is the nominal "default" link frequency */
++static const struct imx477_reg link_450Mhz_regs[] = {
++	{0x030E, 0x00},
++	{0x030F, 0x96},
++};
++
++static const struct imx477_reg link_453Mhz_regs[] = {
++	{0x030E, 0x00},
++	{0x030F, 0x97},
++};
++
++static const struct imx477_reg link_456Mhz_regs[] = {
++	{0x030E, 0x00},
++	{0x030F, 0x98},
++};
++
++static const struct imx477_reg_list link_freq_regs[] = {
++	[IMX477_LINK_FREQ_450MHZ] = {
++		.regs = link_450Mhz_regs,
++		.num_of_regs = ARRAY_SIZE(link_450Mhz_regs)
++	},
++	[IMX477_LINK_FREQ_453MHZ] = {
++		.regs = link_453Mhz_regs,
++		.num_of_regs = ARRAY_SIZE(link_453Mhz_regs)
++	},
++	[IMX477_LINK_FREQ_456MHZ] = {
++		.regs = link_456Mhz_regs,
++		.num_of_regs = ARRAY_SIZE(link_456Mhz_regs)
++	},
+ };
+ 
+ static const struct imx477_reg mode_common_regs[] = {
+@@ -558,8 +598,6 @@ static const struct imx477_reg mode_4056
+ 	{0x0309, 0x0c},
+ 	{0x030b, 0x02},
+ 	{0x030d, 0x02},
+-	{0x030e, 0x00},
+-	{0x030f, 0x96},
+ 	{0x0310, 0x01},
+ 	{0x0820, 0x07},
+ 	{0x0821, 0x08},
+@@ -659,8 +697,6 @@ static const struct imx477_reg mode_2028
+ 	{0x0309, 0x0c},
+ 	{0x030b, 0x02},
+ 	{0x030d, 0x02},
+-	{0x030e, 0x00},
+-	{0x030f, 0x96},
+ 	{0x0310, 0x01},
+ 	{0x0820, 0x07},
+ 	{0x0821, 0x08},
+@@ -760,8 +796,6 @@ static const struct imx477_reg mode_2028
+ 	{0x0309, 0x0c},
+ 	{0x030b, 0x02},
+ 	{0x030d, 0x02},
+-	{0x030e, 0x00},
+-	{0x030f, 0x96},
+ 	{0x0310, 0x01},
+ 	{0x0820, 0x07},
+ 	{0x0821, 0x08},
+@@ -890,8 +924,6 @@ static const struct imx477_reg mode_1332
+ 	{0x0309, 0x0a},
+ 	{0x030b, 0x02},
+ 	{0x030d, 0x02},
+-	{0x030e, 0x00},
+-	{0x030f, 0x96},
+ 	{0x0310, 0x01},
+ 	{0x0820, 0x07},
+ 	{0x0821, 0x08},
+@@ -1121,6 +1153,8 @@ struct imx477 {
+ 	struct v4l2_ctrl *vblank;
+ 	struct v4l2_ctrl *hblank;
+ 
++	unsigned int link_freq_idx;
++
+ 	/* Current mode */
+ 	const struct imx477_mode *mode;
+ 
+@@ -1712,7 +1746,7 @@ static int imx477_get_selection(struct v
+ static int imx477_start_streaming(struct imx477 *imx477)
+ {
+ 	struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+-	const struct imx477_reg_list *reg_list;
++	const struct imx477_reg_list *reg_list, *freq_regs;
+ 	const struct imx477_reg_list *extra_regs;
+ 	int ret, tm;
+ 
+@@ -1725,6 +1759,13 @@ static int imx477_start_streaming(struct
+ 						extra_regs->num_of_regs);
+ 		}
+ 
++		if (!ret) {
++			/* Update the link frequency registers */
++			freq_regs = &link_freq_regs[imx477->link_freq_idx];
++			ret = imx477_write_regs(imx477, freq_regs->regs,
++						freq_regs->num_of_regs);
++		}
++
+ 		if (ret) {
+ 			dev_err(&client->dev, "%s failed to set common settings\n",
+ 				__func__);
+@@ -2010,9 +2051,8 @@ static int imx477_init_controls(struct i
+ 	/* LINK_FREQ is also read only */
+ 	imx477->link_freq =
+ 		v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops,
+-				       V4L2_CID_LINK_FREQ,
+-				       ARRAY_SIZE(imx477_link_freq_menu) - 1, 0,
+-				       imx477_link_freq_menu);
++				       V4L2_CID_LINK_FREQ, 1, 0,
++				       &link_freqs[imx477->link_freq_idx]);
+ 	if (imx477->link_freq)
+ 		imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ 
+@@ -2110,13 +2150,14 @@ static void imx477_free_controls(struct
+ 	mutex_destroy(&imx477->mutex);
+ }
+ 
+-static int imx477_check_hwcfg(struct device *dev)
++static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477)
+ {
+ 	struct fwnode_handle *endpoint;
+ 	struct v4l2_fwnode_endpoint ep_cfg = {
+ 		.bus_type = V4L2_MBUS_CSI2_DPHY
+ 	};
+ 	int ret = -EINVAL;
++	int i;
+ 
+ 	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ 	if (!endpoint) {
+@@ -2141,11 +2182,18 @@ static int imx477_check_hwcfg(struct dev
+ 		goto error_out;
+ 	}
+ 
+-	if (ep_cfg.nr_of_link_frequencies != 1 ||
+-	    ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) {
++	for (i = 0; i < ARRAY_SIZE(link_freqs); i++) {
++		if (link_freqs[i] == ep_cfg.link_frequencies[0]) {
++			imx477->link_freq_idx = i;
++			break;
++		}
++	}
++
++	if (i == ARRAY_SIZE(link_freqs)) {
+ 		dev_err(dev, "Link frequency not supported: %lld\n",
+ 			ep_cfg.link_frequencies[0]);
+-		goto error_out;
++			ret = -EINVAL;
++			goto error_out;
+ 	}
+ 
+ 	ret = 0;
+@@ -2206,7 +2254,7 @@ static int imx477_probe(struct i2c_clien
+ 		(const struct imx477_compatible_data *)match->data;
+ 
+ 	/* Check the hardware configuration in device tree */
+-	if (imx477_check_hwcfg(dev))
++	if (imx477_check_hwcfg(dev, imx477))
+ 		return -EINVAL;
+ 
+ 	/* Default the trigger mode from OF to -1, which means invalid */

+ 43 - 0
target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch

@@ -0,0 +1,43 @@
+From 7e253a062d5a14de13ccfb410570975099c238be Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Thu, 14 Nov 2024 13:15:24 +0000
+Subject: [PATCH] dtoverlays: Add link-frequency override to imx477/378 overlay
+
+Copy of the imx708 change.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README                  | 4 ++++
+ arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 1 +
+ 2 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2780,6 +2780,8 @@ Params: rotation                Mounting
+                                 camera clamping I/Os such as XVS to 0V.
+         sync-source             Configure as vsync source
+         sync-sink               Configure as vsync sink
++        link-frequency          Allowable link frequency values to use in Hz:
++                                450000000 (default), 453000000, 456000000.
+ 
+ 
+ Name:   imx462
+@@ -2822,6 +2824,8 @@ Params: rotation                Mounting
+                                 camera clamping I/Os such as XVS to 0V.
+         sync-source             Configure as vsync source
+         sync-sink               Configure as vsync sink
++        link-frequency          Allowable link frequency values to use in Hz:
++                                450000000 (default), 453000000, 456000000.
+ 
+ 
+ Name:   imx500
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -80,6 +80,7 @@
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+ 		       <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
+ 		always-on = <0>, "+99";
++		link-frequency = <&cam_endpoint>,"link-frequencies#0";
+ 	};
+ };
+ 

+ 29 - 0
target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch

@@ -0,0 +1,29 @@
+From 59a8855b51c1d8acf37d3c80f34782d71f474617 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 13 Nov 2024 10:37:22 +0000
+Subject: [PATCH] dmaengine: dw-axi-dmac: Only start idle channels
+
+Attempting to start a non-idle channel causes an error message to be
+logged, and is inefficient. Test for emptiness of the desc_issued list
+before doing so.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -536,9 +536,11 @@ static void dma_chan_issue_pending(struc
+ {
+ 	struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ 	unsigned long flags;
++	bool was_empty;
+ 
+ 	spin_lock_irqsave(&chan->vc.lock, flags);
+-	if (vchan_issue_pending(&chan->vc))
++	was_empty = list_empty(&chan->vc.desc_issued);
++	if (vchan_issue_pending(&chan->vc) && was_empty)
+ 		axi_chan_start_first_queued(chan);
+ 	spin_unlock_irqrestore(&chan->vc.lock, flags);
+ }

+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1351-mailbox-Add-RP1-mailbox-support.patch → target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch


+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1352-firmware-Add-an-RP1-firmware-interface.patch → target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch


+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1353-misc-Add-RP1-PIO-driver.patch → target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch


+ 118 - 0
target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch

@@ -0,0 +1,118 @@
+From 0b76dec8dfba8c1a4793dff0c86bf73d088a812e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 1 Nov 2024 09:12:01 +0000
+Subject: [PATCH] dts: bcm2712-rpi: Add RP1 firmware and mailboxes
+
+Declare the communications channel to RP1.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ .../boot/dts/broadcom/bcm2712-rpi-5-b.dts     |  4 +--
+ .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi    |  4 +--
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi |  4 +++
+ arch/arm64/boot/dts/broadcom/rp1.dtsi         | 27 +++++++++++++++++++
+ 4 files changed, 35 insertions(+), 4 deletions(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -195,7 +195,7 @@ i2c_rp1boot: &_i2c3 { };
+ 	// This is the RP1 peripheral space
+ 	ranges = <0xc0 0x40000000
+ 		  0x02000000 0x00 0x00000000
+-		  0x00 0x00400000>;
++		  0x00 0x00410000>;
+ 
+ 	dma-ranges =
+ 	// inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+@@ -207,7 +207,7 @@ i2c_rp1boot: &_i2c3 { };
+ 	// This allows the RP1 DMA controller to address RP1 hardware
+ 		     <0xc0 0x40000000
+ 		      0x02000000 0x0 0x00000000
+-		      0x0 0x00400000>,
++		      0x0 0x00410000>,
+ 
+ 	// inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+ 		     <0x00 0x00000000
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -176,7 +176,7 @@ i2c_rp1boot: &_i2c3 { };
+ 	// This is the RP1 peripheral space
+ 	ranges = <0xc0 0x40000000
+ 		  0x02000000 0x00 0x00000000
+-		  0x00 0x00400000>;
++		  0x00 0x00410000>;
+ 
+ 	dma-ranges =
+ 	// inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+@@ -188,7 +188,7 @@ i2c_rp1boot: &_i2c3 { };
+ 	// This allows the RP1 DMA controller to address RP1 hardware
+ 		     <0xc0 0x40000000
+ 		      0x02000000 0x0 0x00000000
+-		      0x0 0x00400000>,
++		      0x0 0x00410000>,
+ 
+ 	// inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+ 		     <0x00 0x00000000
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -294,6 +294,10 @@ pciex4: &pcie2 { };
+ 	status = "okay";
+ };
+ 
++&rp1_mbox {
++	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>.
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -13,6 +13,14 @@
+ 
+ 		// ranges and dma-ranges must be provided by the includer
+ 
++		rp1_mbox: mailbox@8000 {
++			compatible = "raspberrypi,rp1-mbox";
++			status = "disabled";
++			reg = <0xc0 0x40008000  0x0 0x4000>;  // SYSCFG
++			interrupts = <RP1_INT_SYSCFG IRQ_TYPE_LEVEL_HIGH>;
++			#mbox-cells = <1>;
++		};
++
+ 		rp1_clocks: clocks@18000 {
+ 			compatible = "raspberrypi,rp1-clocks";
+ 			#clock-cells = <1>;
+@@ -1183,6 +1191,19 @@
+ 			assigned-clocks        = <&rp1_clocks RP1_CLK_DPI>;
+ 			assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>;
+ 		};
++
++		sram: sram@400000 {
++			compatible = "mmio-sram";
++			reg = <0xc0 0x40400000  0x0 0x10000>;
++			#address-cells = <1>;
++			#size-cells = <1>;
++			ranges = <0  0xc0 0x40400000  0x10000>;
++
++			rp1_fw_shmem: shmem@ff00 {
++				compatible = "raspberrypi,rp1-shmem";
++				reg = <0xff00 0x100>; // firmware mailbox buffer
++			};
++		};
+ 	};
+ };
+ 
+@@ -1281,6 +1302,12 @@
+ };
+ 
+ / {
++	rp1_firmware: rp1_firmware {
++		compatible = "raspberrypi,rp1-firmware", "simple-mfd";
++		mboxes = <&rp1_mbox 0>;
++		shmem = <&rp1_fw_shmem>;
++	};
++
+ 	rp1_vdd_3v3: rp1_vdd_3v3 {
+ 		compatible = "regulator-fixed";
+ 		regulator-name = "vdd-3v3";

+ 55 - 0
target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch

@@ -0,0 +1,55 @@
+From 3e3c1b9922b22d362a4a9133361597ac80b974bb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Fri, 1 Nov 2024 09:13:53 +0000
+Subject: [PATCH] dts: bcm2712-rpi: Add the RP1 PIO device
+
+Declare the device that proxies RP1's PIO hardware.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi |  5 +++++
+ arch/arm64/boot/dts/broadcom/rp1.dtsi         | 12 ++++++++++++
+ 2 files changed, 17 insertions(+)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -97,6 +97,10 @@
+ 	};
+ };
+ 
++pio: &rp1_pio {
++	status = "okay";
++};
++
+ / {
+ 	chosen: chosen {
+ 		bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave";
+@@ -129,6 +133,7 @@
+ 		i2c12 = &i2c_rp1boot;
+ 		mailbox = &mailbox;
+ 		mmc0 = &sdio1;
++		pio0 = &pio;
+ 		serial0 = &uart0;
+ 		serial1 = &uart1;
+ 		serial10 = &uart10;
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -1028,6 +1028,18 @@
+ 			status = "disabled";
+ 		};
+ 
++		rp1_pio: pio@178000 {
++			reg = <0xc0 0x40178000  0x0 0x20>;
++			compatible = "raspberrypi,rp1-pio";
++			firmware = <&rp1_firmware>;
++			dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>,
++				   <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>,
++				   <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>,
++				   <&rp1_dma RP1_DMA_PIO_CH3_TX>, <&rp1_dma RP1_DMA_PIO_CH3_RX>;
++			dma-names = "tx0", "rx0", "tx1", "rx1", "tx2", "rx2", "tx3", "rx3";
++			status = "disabled";
++		};
++
+ 		rp1_mmc0: mmc@180000 {
+ 			reg = <0xc0 0x40180000  0x0 0x100>;
+ 			compatible = "raspberrypi,rp1-dwcmshc";

+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1355-misc-rp1-pio-Add-an-in-kernel.patch → target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch


+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1356-pwm-Add-pwm-pio-rp1-driver.patch → target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch

@@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell <[email protected]>
 
 --- a/drivers/pwm/Kconfig
 +++ b/drivers/pwm/Kconfig
-@@ -454,6 +454,17 @@ config PWM_PCA9685
+@@ -465,6 +465,17 @@ config PWM_PCA9685
  	  To compile this driver as a module, choose M here: the module
  	  will be called pwm-pca9685.
  
@@ -35,7 +35,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  	depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
 --- a/drivers/pwm/Makefile
 +++ b/drivers/pwm/Makefile
-@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
+@@ -42,6 +42,7 @@ obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
  obj-$(CONFIG_PWM_NTXEC)		+= pwm-ntxec.o
  obj-$(CONFIG_PWM_OMAP_DMTIMER)	+= pwm-omap-dmtimer.o
  obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o

+ 99 - 0
target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch

@@ -0,0 +1,99 @@
+From ba7e2e3d03a432acbc338c6c03e46dcd97cfa1b3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 7 Nov 2024 11:41:33 +0000
+Subject: [PATCH] overlays: Add pwm-pio overlay
+
+Add an overlay to enable a single-channel PIO-assisted PWM interface on any
+header pin.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  8 ++++
+ arch/arm/boot/dts/overlays/overlay_map.dts    |  4 ++
+ .../arm/boot/dts/overlays/pwm-pio-overlay.dts | 39 +++++++++++++++++++
+ 4 files changed, 52 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pwm-pio-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -219,6 +219,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	pwm-2chan.dtbo \
+ 	pwm-gpio.dtbo \
+ 	pwm-ir-tx.dtbo \
++	pwm-pio.dtbo \
+ 	pwm1.dtbo \
+ 	qca7000.dtbo \
+ 	qca7000-uart0.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3926,6 +3926,14 @@ Params: gpio_pin                Output G
+         func                    Pin function (default 2 = Alt5)
+ 
+ 
++Name:   pwm-pio
++Info:   Configures a GPIO pin as PIO-assisted PWM output. Unlike hardware PWM,
++        this can be used on any RP1 GPIO in bank 0 (0-27). Up to 4 are
++        supported, assuming nothing else is using PIO. Pi 5 only.
++Load:   dtoverlay=pwm-pio,<param>=<val>
++Params: gpio                    Output GPIO (0-27, default 4)
++
++
+ Name:   pwm1
+ Info:   Configures one or two PWM channel on PWM1 (BCM2711 only)
+         N.B.:
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -240,6 +240,10 @@
+ 		bcm2712;
+ 	};
+ 
++	pwm-pio {
++		bcm2712;
++	};
++
+ 	pwm1 {
+ 		bcm2711;
+ 	};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts
+@@ -0,0 +1,39 @@
++// SPDX-License-Identifier: GPL-2.0
++// Device tree overlay for RP1 PIO PWM.
++/dts-v1/;
++/plugin/;
++
++/ {
++	compatible = "brcm,bcm2712";
++
++	fragment@0 {
++		target = <&gpio>;
++		__overlay__ {
++			pwm_pio_pins: pwm_pio_pins@4 {
++				brcm,pins = <4>; /* gpio 4 */
++				function = "pio";
++				bias-disable;
++			};
++		};
++	};
++
++	fragment@1 {
++		target-path = "/";
++		__overlay__ {
++			pwm_pio: pwm_pio@4 {
++				  compatible = "raspberrypi,pwm-pio-rp1";
++				  pinctrl-names = "default";
++				  pinctrl-0 = <&pwm_pio_pins>;
++				  gpios = <&gpio 4 0>;
++			};
++		};
++	};
++
++	__overrides__ {
++		gpio = <&pwm_pio>,"gpios:4",
++		       <&pwm_pio_pins>,"brcm,pins:0",
++		       /* modify reg values to allow multiple instantiation */
++		       <&pwm_pio>,"reg:0",
++		       <&pwm_pio_pins>,"reg:0";
++	};
++};

+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1354-fixup-musc-add-RP1-PIO-driver.patch → target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch


+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch

@@ -1,7 +1,7 @@
 From b4472d09b1ffdafd8132803ffbec62596e559fd8 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <[email protected]>
 Date: Mon, 18 Nov 2024 09:10:52 +0000
-Subject: [PATCH 1394/1482] misc: rp1-pio: Add compat_ioctl method
+Subject: [PATCH] misc: rp1-pio: Add compat_ioctl method
 
 Provide a compat_ioctl method, to support running a 64-bit kernel with
 a 32-bit userland.
@@ -13,7 +13,7 @@ Signed-off-by: Phil Elwell <[email protected]>
 
 --- a/drivers/misc/rp1-pio.c
 +++ b/drivers/misc/rp1-pio.c
-@@ -1023,11 +1023,75 @@ static long rp1_pio_ioctl(struct file *f
+@@ -996,11 +996,75 @@ static long rp1_pio_ioctl(struct file *f
  	return ret;
  }
  

+ 6 - 6
target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch → target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch

@@ -1,7 +1,7 @@
-From 24cb07b0c0724a22e474d12e7c2d5b834bf3b076 Mon Sep 17 00:00:00 2001
+From 0e4968617aad7d0f88e0a630499202eaae407a19 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <[email protected]>
 Date: Tue, 26 Mar 2024 15:57:46 +0000
-Subject: [PATCH 0998/1085] i2c: designware: Add support for bus clear feature
+Subject: [PATCH] i2c: designware: Add support for bus clear feature
 
 Newer versions of the DesignWare I2C block support the detection of
 stuck signals, and a mechanism to recover from them. Add the required
@@ -108,7 +108,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  						 DW_IC_TX_ABRT_10ADDR1_NOACK | \
 --- a/drivers/i2c/busses/i2c-designware-master.c
 +++ b/drivers/i2c/busses/i2c-designware-master.c
-@@ -212,6 +212,7 @@ static int i2c_dw_set_timings_master(str
+@@ -215,6 +215,7 @@ static int i2c_dw_set_timings_master(str
   */
  static int i2c_dw_init_master(struct dw_i2c_dev *dev)
  {
@@ -116,7 +116,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  	int ret;
  
  	ret = i2c_dw_acquire_lock(dev);
-@@ -235,6 +236,17 @@ static int i2c_dw_init_master(struct dw_
+@@ -238,6 +239,17 @@ static int i2c_dw_init_master(struct dw_
  		regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
  	}
  
@@ -134,7 +134,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  	/* Write SDA hold time if supported */
  	if (dev->sda_hold_time)
  		regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
-@@ -1071,6 +1083,7 @@ int i2c_dw_probe_master(struct dw_i2c_de
+@@ -1074,6 +1086,7 @@ int i2c_dw_probe_master(struct dw_i2c_de
  	struct i2c_adapter *adap = &dev->adapter;
  	unsigned long irq_flags;
  	unsigned int ic_con;
@@ -142,7 +142,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  	int ret;
  
  	init_completion(&dev->cmd_complete);
-@@ -1106,7 +1119,11 @@ int i2c_dw_probe_master(struct dw_i2c_de
+@@ -1109,7 +1122,11 @@ int i2c_dw_probe_master(struct dw_i2c_de
  	if (ret)
  		return ret;
  

+ 3592 - 0
target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch

@@ -0,0 +1,3592 @@
+From 32511f035b086bca254d8adab234cef3541492b4 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <[email protected]>
+Date: Thu, 17 Oct 2024 11:37:29 +0100
+Subject: [PATCH] drivers: media: pci: Update Hailo accelerator device driver
+ to v4.19
+
+Sourced from https://github.com/hailo-ai/hailort-drivers/
+
+Signed-off-by: Naushir Patuck <[email protected]>
+---
+ drivers/media/pci/hailo/Makefile              |   4 +-
+ drivers/media/pci/hailo/common/fw_operation.c |  50 ++-
+ drivers/media/pci/hailo/common/fw_operation.h |   8 +-
+ .../media/pci/hailo/common/fw_validation.c    |  10 +-
+ .../media/pci/hailo/common/fw_validation.h    |   7 +-
+ .../pci/hailo/common/hailo_ioctl_common.h     |  28 +-
+ .../media/pci/hailo/common/hailo_resource.c   |  23 +-
+ .../media/pci/hailo/common/hailo_resource.h   |   2 +-
+ drivers/media/pci/hailo/common/pcie_common.c  | 380 +++++++++---------
+ drivers/media/pci/hailo/common/pcie_common.h  |  38 +-
+ drivers/media/pci/hailo/common/soc_structs.h  |  79 ++++
+ drivers/media/pci/hailo/common/utils.h        |  23 +-
+ drivers/media/pci/hailo/common/vdma_common.c  |  93 +++--
+ drivers/media/pci/hailo/common/vdma_common.h  |  22 +-
+ drivers/media/pci/hailo/src/fops.c            | 284 ++-----------
+ drivers/media/pci/hailo/src/fops.h            |   5 +-
+ drivers/media/pci/hailo/src/nnc.c             | 299 ++++++++++++++
+ drivers/media/pci/hailo/src/nnc.h             |  22 +
+ drivers/media/pci/hailo/src/pci_soc_ioctl.c   | 155 -------
+ drivers/media/pci/hailo/src/pcie.c            | 166 +++-----
+ drivers/media/pci/hailo/src/pcie.h            |  26 +-
+ drivers/media/pci/hailo/src/soc.c             | 244 +++++++++++
+ .../pci/hailo/src/{pci_soc_ioctl.h => soc.h}  |  13 +-
+ drivers/media/pci/hailo/src/sysfs.c           |   2 +-
+ drivers/media/pci/hailo/src/sysfs.h           |   2 +-
+ drivers/media/pci/hailo/src/utils.c           |  26 --
+ drivers/media/pci/hailo/utils/compact.h       |   2 +-
+ drivers/media/pci/hailo/utils/fw_common.h     |   2 +-
+ .../pci/hailo/utils/integrated_nnc_utils.c    |  10 +-
+ .../pci/hailo/utils/integrated_nnc_utils.h    |   2 +-
+ drivers/media/pci/hailo/utils/logs.c          |   2 +-
+ drivers/media/pci/hailo/utils/logs.h          |   2 +-
+ drivers/media/pci/hailo/vdma/ioctl.c          |  18 +-
+ drivers/media/pci/hailo/vdma/ioctl.h          |   6 +-
+ drivers/media/pci/hailo/vdma/memory.c         |  12 +-
+ drivers/media/pci/hailo/vdma/memory.h         |   2 +-
+ drivers/media/pci/hailo/vdma/vdma.c           |  39 +-
+ drivers/media/pci/hailo/vdma/vdma.h           |   5 +-
+ 38 files changed, 1224 insertions(+), 889 deletions(-)
+ create mode 100644 drivers/media/pci/hailo/common/soc_structs.h
+ create mode 100644 drivers/media/pci/hailo/src/nnc.c
+ create mode 100644 drivers/media/pci/hailo/src/nnc.h
+ delete mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c
+ create mode 100644 drivers/media/pci/hailo/src/soc.c
+ rename drivers/media/pci/hailo/src/{pci_soc_ioctl.h => soc.h} (53%)
+ mode change 100755 => 100644
+ delete mode 100644 drivers/media/pci/hailo/src/utils.c
+
+--- a/drivers/media/pci/hailo/Makefile
++++ b/drivers/media/pci/hailo/Makefile
+@@ -8,9 +8,9 @@ obj-$(CONFIG_MEDIA_PCI_HAILO) := hailo_p
+ 
+ hailo_pci-objs += src/pcie.o
+ hailo_pci-objs += src/fops.o
+-hailo_pci-objs += src/utils.o
+ hailo_pci-objs += src/sysfs.o
+-hailo_pci-objs += src/pci_soc_ioctl.o
++hailo_pci-objs += src/nnc.o
++hailo_pci-objs += src/soc.o
+ 
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o
+--- a/drivers/media/pci/hailo/common/fw_operation.c
++++ b/drivers/media/pci/hailo/common/fw_operation.c
+@@ -1,7 +1,7 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+-**/
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
+ 
+ #include "fw_operation.h"
+ 
+@@ -15,7 +15,10 @@ typedef struct {
+     u32 chip_offset;
+ } FW_DEBUG_BUFFER_HEADER_t;
+ 
+-#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t))
++#define DEBUG_BUFFER_DATA_SIZE              (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t))
++#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET   (0x640 + 0x640)
++#define PCIE_APP_CPU_DEBUG_OFFSET           (8*1024)
++#define PCIE_CORE_CPU_DEBUG_OFFSET          (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE)
+ 
+ int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification)
+ {
+@@ -35,6 +38,21 @@ int hailo_read_firmware_notification(str
+     return 0;
+ }
+ 
++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource,
++    struct hailo_d2h_notification *notification)
++{
++    struct hailo_resource notification_resource;
++
++    if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) {
++        return -EINVAL;
++    }
++
++    notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET,
++    notification_resource.size = sizeof(struct hailo_d2h_notification);
++
++    return hailo_read_firmware_notification(&notification_resource, notification);
++}
++
+ static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header)
+ {
+     size_t ready_to_read = 0;
+@@ -100,4 +118,30 @@ long hailo_read_firmware_log(struct hail
+     
+     params->read_bytes = ready_to_read;
+     return 0;
++}
++
++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params)
++{
++    long err = 0;
++    struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE};
++
++    if (HAILO_CPU_ID_CPU0 == params->cpu_id) {
++        log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET;
++    } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) {
++        log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET;
++    } else {
++        return -EINVAL;
++    }
++
++    if (0 == params->buffer_size) {
++        params->read_bytes = 0;
++        return 0;
++    }
++
++    err = hailo_read_firmware_log(&log_resource, params);
++    if (0 != err) {
++        return err;
++    }
++
++    return 0;
+ }
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/fw_operation.h
++++ b/drivers/media/pci/hailo/common/fw_operation.h
+@@ -1,7 +1,7 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+-**/
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
+ 
+ #ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_
+ #define _HAILO_COMMON_FIRMWARE_OPERATION_H_
+@@ -16,8 +16,12 @@ extern "C" {
+ 
+ int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification);
+ 
++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification);
++
+ long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params);
+ 
++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/drivers/media/pci/hailo/common/fw_validation.c
++++ b/drivers/media/pci/hailo/common/fw_validation.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "fw_validation.h"
+@@ -85,15 +85,15 @@ exit:
+ }
+ 
+ int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address,
+-    size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert)
++    size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert)
+ {
+ 
+-    secure_boot_certificate_t *firmware_cert = NULL;
++    secure_boot_certificate_header_t *firmware_cert = NULL;
+     int err = -EINVAL;
+     u32 consumed_firmware_offset = *outer_consumed_firmware_offset;
+ 
+-    firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset);
+-    CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), -EINVAL);
++    firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset);
++    CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL);
+ 
+     if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) ||
+         (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) {
+--- a/drivers/media/pci/hailo/common/fw_validation.h
++++ b/drivers/media/pci/hailo/common/fw_validation.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_
+@@ -44,8 +44,7 @@ typedef struct {
+ typedef struct {
+     u32 key_size;
+     u32 content_size;
+-    u8 certificates_data[0];
+-} secure_boot_certificate_t;
++} secure_boot_certificate_header_t;
+ 
+ #ifdef _MSC_VER
+ #pragma warning(pop)
+@@ -60,6 +59,6 @@ int FW_VALIDATION__validate_fw_header(ui
+     firmware_header_t **out_firmware_header, enum hailo_board_type board_type);
+ 
+ int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address,
+-    size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert);
++    size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert);
+ 
+ #endif
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h
++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h
+@@ -1,13 +1,13 @@
+ // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_IOCTL_COMMON_H_
+ #define _HAILO_IOCTL_COMMON_H_
+ 
+ #define HAILO_DRV_VER_MAJOR 4
+-#define HAILO_DRV_VER_MINOR 18
++#define HAILO_DRV_VER_MINOR 19
+ #define HAILO_DRV_VER_REVISION 0
+ 
+ #define _STRINGIFY_EXPANDED( x ) #x
+@@ -17,10 +17,11 @@
+ 
+ // This value is not easily changeable.
+ // For example: the channel interrupts ioctls assume we have up to 32 channels
+-#define MAX_VDMA_CHANNELS_PER_ENGINE    (32)
+-#define MAX_VDMA_ENGINES                (3)
+-#define SIZE_OF_VDMA_DESCRIPTOR         (16)
+-#define VDMA_DEST_CHANNELS_START        (16)
++#define MAX_VDMA_CHANNELS_PER_ENGINE            (32)
++#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION  (16)
++#define MAX_VDMA_ENGINES                        (3)
++#define SIZE_OF_VDMA_DESCRIPTOR                 (16)
++#define VDMA_DEST_CHANNELS_START                (16)
+ 
+ #define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128)
+ #define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1)
+@@ -37,8 +38,8 @@
+ #define FW_ACCESS_APP_CPU_CONTROL_MASK      (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT)
+ #define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT     (2)
+ #define FW_ACCESS_DRIVER_SHUTDOWN_MASK      (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT)
+-#define FW_ACCESS_SOC_CONNECT_SHIFT         (3)
+-#define FW_ACCESS_SOC_CONNECT_MASK          (1 << FW_ACCESS_SOC_CONNECT_SHIFT)
++#define FW_ACCESS_SOC_CONTROL_SHIFT         (3)
++#define FW_ACCESS_SOC_CONTROL_MASK          (1 << FW_ACCESS_SOC_CONTROL_SHIFT)
+ 
+ #define INVALID_VDMA_CHANNEL                (0xff)
+ 
+@@ -245,6 +246,12 @@ struct hailo_desc_list_release_params {
+     uintptr_t desc_handle;      // in
+ };
+ 
++struct hailo_write_action_list_params {
++    uint8_t *data;              // in
++    size_t size;                // in
++    uint64_t dma_address;       // out
++};
++
+ /* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */
+ struct hailo_desc_list_program_params {
+     size_t buffer_handle;       // in
+@@ -508,6 +515,7 @@ struct hailo_vdma_launch_transfer_params
+ 
+ /* structure used in ioctl HAILO_SOC_CONNECT */
+ struct hailo_soc_connect_params {
++    uint16_t port_number;           // in
+     uint8_t input_channel_index;    // out
+     uint8_t output_channel_index;   // out
+     uintptr_t input_desc_handle;    // in
+@@ -522,6 +530,7 @@ struct hailo_soc_close_params {
+ 
+ /* structure used in ioctl HAILO_PCI_EP_ACCEPT */
+ struct hailo_pci_ep_accept_params {
++    uint16_t port_number;           // in
+     uint8_t input_channel_index;    // out
+     uint8_t output_channel_index;   // out
+     uintptr_t input_desc_handle;    // in
+@@ -562,6 +571,7 @@ struct tCompatibleHailoIoctlData
+         struct hailo_soc_close_params SocCloseParams;
+         struct hailo_pci_ep_accept_params AcceptParams;
+         struct hailo_pci_ep_close_params PciEpCloseParams;
++        struct hailo_write_action_list_params WriteActionListParams;
+     } Buffer;
+ };
+ #endif // _MSC_VER
+@@ -632,6 +642,7 @@ enum hailo_nnc_ioctl_code {
+     HAILO_DISABLE_NOTIFICATION_CODE,
+     HAILO_READ_LOG_CODE,
+     HAILO_RESET_NN_CORE_CODE,
++    HAILO_WRITE_ACTION_LIST_CODE,
+ 
+     // Must be last
+     HAILO_NNC_IOCTL_MAX_NR
+@@ -642,6 +653,7 @@ enum hailo_nnc_ioctl_code {
+ #define HAILO_DISABLE_NOTIFICATION      _IO_(HAILO_NNC_IOCTL_MAGIC,    HAILO_DISABLE_NOTIFICATION_CODE)
+ #define HAILO_READ_LOG                  _IOWR_(HAILO_NNC_IOCTL_MAGIC,  HAILO_READ_LOG_CODE,                   struct hailo_read_log_params)
+ #define HAILO_RESET_NN_CORE             _IO_(HAILO_NNC_IOCTL_MAGIC,    HAILO_RESET_NN_CORE_CODE)
++#define HAILO_WRITE_ACTION_LIST         _IOW_(HAILO_NNC_IOCTL_MAGIC,    HAILO_WRITE_ACTION_LIST_CODE,     struct hailo_write_action_list_params)
+ 
+ enum hailo_soc_ioctl_code {
+     HAILO_SOC_IOCTL_CONNECT_CODE,
+--- a/drivers/media/pci/hailo/common/hailo_resource.c
++++ b/drivers/media/pci/hailo/common/hailo_resource.c
+@@ -1,24 +1,31 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "hailo_resource.h"
+ 
++#include "utils.h"
++
+ #include <linux/io.h>
+ #include <linux/errno.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ 
++#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3)))
+ 
+ u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset)
+ {
+-    return ioread8((u8*)resource->address + offset);
++    u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++    u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++    return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val);
+ }
+ 
+ u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset)
+ {
+-    return ioread16((u8*)resource->address + offset);
++    u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++    u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++    return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val);
+ }
+ 
+ u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset)
+@@ -28,12 +35,18 @@ u32 hailo_resource_read32(struct hailo_r
+ 
+ void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value)
+ {
+-    iowrite8(value, (u8*)resource->address + offset);
++    u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++    u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++    iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value),
++        (u8*)ALIGN_TO_32_BIT(resource->address + offset));
+ }
+ 
+ void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value)
+ {
+-    iowrite16(value, (u8*)resource->address + offset);
++    u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++    u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++    iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value),
++        (u8*)ALIGN_TO_32_BIT(resource->address + offset));
+ }
+ 
+ void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value)
+--- a/drivers/media/pci/hailo/common/hailo_resource.h
++++ b/drivers/media/pci/hailo/common/hailo_resource.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_COMMON_HAILO_RESOURCE_H_
+--- a/drivers/media/pci/hailo/common/pcie_common.c
++++ b/drivers/media/pci/hailo/common/pcie_common.c
+@@ -1,10 +1,11 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "pcie_common.h"
+ #include "fw_operation.h"
++#include "soc_structs.h"
+ 
+ #include <linux/errno.h>
+ #include <linux/bug.h>
+@@ -35,10 +36,6 @@
+ #define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100)
+ #define FIRMWARE_LOAD_SLEEP_MS         (50)
+ 
+-#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024)
+-#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE)
+-
+-#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640)
+ #define PCIE_REQUEST_SIZE_OFFSET (0x640)
+ 
+ #define PCIE_CONFIG_VENDOR_OFFSET (0x0098)
+@@ -59,7 +56,6 @@ struct hailo_fw_addresses {
+     u32 app_fw_code_ram_base;
+     u32 boot_key_cert;
+     u32 boot_cont_cert;
+-    u32 boot_fw_trigger;
+     u32 core_code_ram_base;
+     u32 core_fw_header;
+     u32 atr0_trsl_addr1;
+@@ -69,13 +65,11 @@ struct hailo_fw_addresses {
+ 
+ struct loading_stage {
+     const struct hailo_file_batch *batch;
++    u32 trigger_address;
+ };
+ 
+ struct hailo_board_compatibility {
+     struct hailo_fw_addresses fw_addresses;
+-    const char *fw_filename;
+-    const struct hailo_config_constants board_cfg;
+-    const struct hailo_config_constants fw_cfg;
+     const struct loading_stage stages[MAX_LOADING_STAGES];
+ };
+ 
+@@ -85,28 +79,32 @@ static const struct hailo_file_batch hai
+         .address = 0xA0000,
+         .max_size = 0x8004,
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+     {
+         .filename = "hailo/hailo10h/u-boot.dtb.signed",
+         .address = 0xA8004,
+         .max_size = 0x20000,
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+     {
+         .filename = "hailo/hailo10h/scu_fw.bin",
+         .address = 0x20000,
+         .max_size = 0x40000,
+         .is_mandatory = true,
+-        .has_header = true
++        .has_header = true,
++        .has_core = false
+     },
+     {
+         .filename = NULL,
+         .address = 0x00,
+         .max_size = 0x00,
+         .is_mandatory = false,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     }
+ };
+ 
+@@ -116,36 +114,140 @@ static const struct hailo_file_batch hai
+         .address = 0x85000000,
+         .max_size = 0x1000000,
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+     {
+         .filename = "hailo/hailo10h/u-boot-tfa.itb",
+         .address = 0x86000000,
+         .max_size = 0x1000000,
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+     {
+         .filename = "hailo/hailo10h/fitImage",
+         .address = 0x87000000,
+         .max_size = 0x1000000,
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+     {
+         .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz",
+         .address = 0x88000000,
+         .max_size = 0x20000000, // Max size 512MB
+         .is_mandatory = true,
+-        .has_header = false
++        .has_header = false,
++        .has_core = false
+     },
+ };
+ 
++// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb)
++static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = {
++    {
++        .filename = "hailo/hailo10h/u-boot-spl.bin",
++        .address = 0x85000000,
++        .max_size = 0x1000000,
++        .is_mandatory = true,
++        .has_header = false,
++        .has_core = false
++    },
++    {
++        .filename = "hailo/hailo10h/u-boot-tfa.itb",
++        .address = 0x86000000,
++        .max_size = 0x1000000,
++        .is_mandatory = true,
++        .has_header = false,
++        .has_core = false
++    },
++    {
++        .filename = NULL,
++        .address = 0x00,
++        .max_size = 0x00,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    },
++};
++
++static const struct hailo_file_batch hailo8_files_stg1[] = {
++    {
++        .filename = "hailo/hailo8_fw.4.19.0.bin",
++        .address = 0x20000,
++        .max_size = 0x50000,
++        .is_mandatory = true,
++        .has_header = true,
++        .has_core = true
++    },
++    {
++        .filename = "hailo/hailo8_board_cfg.bin",
++        .address = 0x60001000,
++        .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    },
++    {
++        .filename = "hailo/hailo8_fw_cfg.bin",
++        .address = 0x60001500,
++        .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    },
++    {
++        .filename = NULL,
++        .address = 0x00,
++        .max_size = 0x00,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    }
++};
++
++static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = {
++    {
++        .filename = "hailo/hailo15_fw.bin",
++        .address = 0x20000,
++        .max_size = 0x100000,
++        .is_mandatory = true,
++        .has_header = true,
++        .has_core = true
++    },
++    {
++        .filename = NULL,
++        .address = 0x00,
++        .max_size = 0x00,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    }
++};
++
++static const struct hailo_file_batch pluto_files_stg1[] = {
++    {
++        .filename = "hailo/pluto_fw.bin",
++        .address = 0x20000,
++        .max_size = 0x100000,
++        .is_mandatory = true,
++        .has_header = true,
++        .has_core = true
++    },
++    {
++        .filename = NULL,
++        .address = 0x00,
++        .max_size = 0x00,
++        .is_mandatory = false,
++        .has_header = false,
++        .has_core = false
++    }
++};
++
+ static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = {
+     [HAILO_BOARD_TYPE_HAILO8] = {
+         .fw_addresses = {
+             .boot_fw_header = 0xE0030,
+-            .boot_fw_trigger = 0xE0980,
+             .boot_key_cert = 0xE0048,
+             .boot_cont_cert = 0xE0390,
+             .app_fw_code_ram_base = 0x60000,
+@@ -155,22 +257,16 @@ static const struct hailo_board_compatib
+             .raise_ready_offset = 0x1684,
+             .boot_status = 0xe0000,
+         },
+-        .fw_filename = "hailo/hailo8_fw.bin",
+-        .board_cfg = {
+-            .filename = "hailo/hailo8_board_cfg.bin",
+-            .address = 0x60001000,
+-            .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE,
+-        },
+-        .fw_cfg = {
+-            .filename = "hailo/hailo8_fw_cfg.bin",
+-            .address = 0x60001500,
+-            .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE,
++        .stages = {
++            {
++                .batch = hailo8_files_stg1,
++                .trigger_address = 0xE0980
++            },
+         },
+     },
+     [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = {
+         .fw_addresses = {
+             .boot_fw_header = 0x88000,
+-            .boot_fw_trigger = 0x88c98,
+             .boot_key_cert = 0x88018,
+             .boot_cont_cert = 0x886a8,
+             .app_fw_code_ram_base = 0x20000,
+@@ -180,22 +276,16 @@ static const struct hailo_board_compatib
+             .raise_ready_offset = 0x1754,
+             .boot_status = 0x80000,
+         },
+-        .fw_filename = "hailo/hailo15_fw.bin",
+-        .board_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
+-        },
+-        .fw_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
++        .stages = {
++            {
++                .batch = hailo10h_legacy_files_stg1,
++                .trigger_address = 0x88c98
++            },
+         },
+     },
+     [HAILO_BOARD_TYPE_HAILO10H] = {
+         .fw_addresses = {
+             .boot_fw_header = 0x88000,
+-            .boot_fw_trigger = 0x88c98,
+             .boot_key_cert = 0x88018,
+             .boot_cont_cert = 0x886a8,
+             .app_fw_code_ram_base = 0x20000,
+@@ -205,23 +295,18 @@ static const struct hailo_board_compatib
+             .raise_ready_offset = 0x1754,
+             .boot_status = 0x80000,
+         },
+-        .fw_filename = NULL,
+-        .board_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
+-        },
+-        .fw_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
+-        },
+         .stages = {
+             {
+                 .batch = hailo10h_files_stg1,
++                .trigger_address = 0x88c98
+             },
+             {
+                 .batch = hailo10h_files_stg2,
++                .trigger_address = 0x84000000
++            },
++            {
++                .batch = hailo10h_files_stg2_linux_in_emmc,
++                .trigger_address = 0x84000000
+             },
+         },
+     },
+@@ -230,7 +315,6 @@ static const struct hailo_board_compatib
+     [HAILO_BOARD_TYPE_PLUTO] = {
+         .fw_addresses = {
+             .boot_fw_header = 0x88000,
+-            .boot_fw_trigger = 0x88c98,
+             .boot_key_cert = 0x88018,
+             .boot_cont_cert = 0x886a8,
+             .app_fw_code_ram_base = 0x20000,
+@@ -241,16 +325,11 @@ static const struct hailo_board_compatib
+             .raise_ready_offset = 0x174c,
+             .boot_status = 0x80000,
+         },
+-        .fw_filename = "hailo/pluto_fw.bin",
+-        .board_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
+-        },
+-        .fw_cfg = {
+-            .filename = NULL,
+-            .address = 0,
+-            .max_size = 0,
++        .stages = {
++            {
++                .batch = pluto_files_stg1,
++                .trigger_address = 0x88c98
++            },
+         },
+     }
+ };
+@@ -340,21 +419,6 @@ void hailo_pcie_write_firmware_driver_sh
+     hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value);
+ }
+ 
+-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources,
+-    struct hailo_d2h_notification *notification)
+-{
+-    struct hailo_resource notification_resource;
+-
+-    if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resources->fw_access.size) {
+-        return -EINVAL;
+-    }
+-
+-    notification_resource.address = resources->fw_access.address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET,
+-    notification_resource.size = sizeof(struct hailo_d2h_notification);
+-
+-    return hailo_read_firmware_notification(&notification_resource, notification);
+-}
+-
+ int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index)
+ {
+     size_t offset = 0;
+@@ -388,7 +452,7 @@ static void write_memory_chunk(struct ha
+     u32 ATR_INDEX = 0;
+     BUG_ON(dest_offset + len > (u32)resources->fw_access.size);
+ 
+-    (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX);
++    (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX);
+     (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src);
+ }
+ 
+@@ -398,13 +462,13 @@ static void read_memory_chunk(
+     u32 ATR_INDEX = 0;
+     BUG_ON(src_offset + len > (u32)resources->fw_access.size);
+ 
+-    (void)hailo_pcie_configure_atr_table(&resources->config, (u64)src, ATR_INDEX);
++    (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX);
+     (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest);
+ }
+ 
+ // Note: this function modify the device ATR table (that is also used by the firmware for control and vdma).
+ // Use with caution, and restore the original atr if needed.
+-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
++static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
+ {
+     struct hailo_atr_config previous_atr = {0};
+     hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK);
+@@ -417,8 +481,8 @@ void write_memory(struct hailo_pcie_reso
+ 
+     if (base_address != dest) {
+         // Data is not aligned, write the first chunk
+-        chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len);
+-        write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len);
++        chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len);
++        write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len);
+         offset += chunk_len;
+     }
+ 
+@@ -447,8 +511,8 @@ static void read_memory(struct hailo_pci
+ 
+     if (base_address != src) {
+         // Data is not aligned, write the first chunk
+-        chunk_len = min(base_address + ATR_TABLE_SIZE - src, len);
+-        read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len);
++        chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len);
++        read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len);
+         offset += chunk_len;
+     }
+ 
+@@ -463,12 +527,12 @@ static void read_memory(struct hailo_pci
+ }
+ 
+ static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header,
+-    secure_boot_certificate_t *fw_cert)
++    secure_boot_certificate_header_t *fw_cert)
+ {
+     const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+-    void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t));
+-    void *key_data = &fw_cert->certificates_data[0];
+-    void *content_data = &fw_cert->certificates_data[fw_cert->key_size];
++    u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t));
++    u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t));
++    u8 *content_data = key_data + fw_cert->key_size;
+ 
+     write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t));
+ 
+@@ -487,13 +551,11 @@ static void hailo_write_core_firmware(st
+     write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t));
+ }
+ 
+-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources)
++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address)
+ {
+-    const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+     u32 pcie_finished = 1;
+ 
+-    write_memory(resources, fw_addresses->boot_fw_trigger,
+-        (void*)&pcie_finished, sizeof(pcie_finished));
++    write_memory(resources, address, (void*)&pcie_finished, sizeof(pcie_finished));
+ }
+ 
+ u32 hailo_get_boot_status(struct hailo_pcie_resources *resources)
+@@ -501,8 +563,7 @@ u32 hailo_get_boot_status(struct hailo_p
+     u32 boot_status = 0;
+     const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+ 
+-    read_memory(resources, fw_addresses->boot_status,
+-        &boot_status, sizeof(boot_status));
++    read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status));
+ 
+     return boot_status;
+ }
+@@ -517,11 +578,11 @@ u32 hailo_get_boot_status(struct hailo_p
+ */
+ static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size,
+     firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header,
+-    secure_boot_certificate_t **out_firmware_cert, enum hailo_board_type board_type)
++    secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type)
+ {
+     firmware_header_t *app_firmware_header = NULL;
+     firmware_header_t *core_firmware_header = NULL;
+-    secure_boot_certificate_t *firmware_cert = NULL;
++    secure_boot_certificate_header_t *firmware_cert = NULL;
+     int err = -EINVAL;
+     u32 consumed_firmware_offset = 0;
+ 
+@@ -571,25 +632,25 @@ exit:
+     return err;
+ }
+ 
+-static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev)
++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev)
+ {
+     const struct firmware *firmware = NULL;
+     firmware_header_t *app_firmware_header = NULL;
+-    secure_boot_certificate_t *firmware_cert = NULL;
++    secure_boot_certificate_header_t *firmware_cert = NULL;
+     firmware_header_t *core_firmware_header = NULL;
+     int err = 0;
+ 
+-    err = request_firmware_direct(&firmware, files_batch->filename, dev);
++    err = request_firmware_direct(&firmware, file_info->filename, dev);
+     if (err < 0) {
+         return err;
+     }
+ 
+-    if (firmware->size > files_batch->max_size) {
++    if (firmware->size > file_info->max_size) {
+         release_firmware(firmware);
+         return -EFBIG;
+     }
+ 
+-    if (files_batch->has_header) {
++    if (file_info->has_header) {
+         err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size,
+             &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type);
+         if (err < 0) {
+@@ -598,8 +659,11 @@ static int write_single_file(struct hail
+         }
+ 
+         hailo_write_app_firmware(resources, app_firmware_header, firmware_cert);
++        if (file_info->has_core) {
++            hailo_write_core_firmware(resources, core_firmware_header);
++        }
+     } else {
+-        write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size);
++        write_memory(resources, file_info->address, (void*)firmware->data, firmware->size);
+     }
+ 
+     release_firmware(firmware);
+@@ -632,31 +696,13 @@ int hailo_pcie_write_firmware_batch(stru
+         dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename);
+     }
+ 
+-    return 0;
+-}
+-
+-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size)
+-{
+-    firmware_header_t *app_firmware_header = NULL;
+-    secure_boot_certificate_t *firmware_cert = NULL;
+-    firmware_header_t *core_firmware_header = NULL;
+-
+-    int err = FW_VALIDATION__validate_fw_headers((uintptr_t)fw_data, fw_size,
+-        &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type);
+-    if (err < 0) {
+-        return err;
+-    }
+-
+-    hailo_write_app_firmware(resources, app_firmware_header, firmware_cert);
+-    hailo_write_core_firmware(resources, core_firmware_header);
+-
+-    hailo_trigger_firmware_boot(resources);
++    hailo_trigger_firmware_boot(resources, compat[resources->board_type].stages[stage].trigger_address);
+ 
+     return 0;
+ }
+ 
+ // TODO: HRT-14147 - remove this function
+-bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources)
++static bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources)
+ {
+     return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED;
+ }
+@@ -691,32 +737,6 @@ bool hailo_pcie_wait_for_firmware(struct
+     return false;
+ }
+ 
+-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data,
+-    const size_t config_size, const struct hailo_config_constants *config_consts)
+-{
+-    if (config_size > config_consts->max_size) {
+-        return -EINVAL;
+-    }
+-
+-    write_memory(resources, config_consts->address, config_data, (u32)config_size);
+-    return 0;
+-}
+-
+-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type) {
+-    BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+-    return &compat[board_type].board_cfg;
+-}
+-
+-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type) {
+-    BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+-    return &compat[board_type].fw_cfg;
+-}
+-
+-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type) {
+-    BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+-    return compat[board_type].fw_filename;
+-}
+-
+ void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap)
+ {
+     size_t i = 0;
+@@ -745,7 +765,7 @@ void hailo_pcie_enable_interrupts(struct
+     hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF);
+ 
+     mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION |
+-        BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED);
++        BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED | BCS_ISTATUS_SOC_CLOSED_IRQ);
+     hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask);
+ }
+ 
+@@ -754,45 +774,15 @@ void hailo_pcie_disable_interrupts(struc
+     hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0);
+ }
+ 
+-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params)
+-{
+-    long err = 0;
+-    struct hailo_resource log_resource = {resources->fw_access.address, DEBUG_BUFFER_TOTAL_SIZE};
+-
+-    if (HAILO_CPU_ID_CPU0 == params->cpu_id) {
+-        log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET;
+-    } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) {
+-        log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET;
+-    } else {
+-        return -EINVAL;
+-    }
+-
+-    if (0 == params->buffer_size) {
+-        params->read_bytes = 0;
+-        return 0;
+-    }
+-
+-    err = hailo_read_firmware_log(&log_resource, params);
+-    if (0 != err) {
+-        return err;
+-    }
+-
+-    return 0;
+-}
+-
+ static int direct_memory_transfer(struct hailo_pcie_resources *resources,
+     struct hailo_memory_transfer_params *params)
+ {
+-    if (params->address > U32_MAX) {
+-        return -EFAULT;
+-    }
+-
+     switch (params->transfer_direction) {
+     case TRANSFER_READ:
+-        read_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
++        read_memory(resources, params->address, params->buffer, (u32)params->count);
+         break;
+     case TRANSFER_WRITE:
+-        write_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
++        write_memory(resources, params->address, params->buffer, (u32)params->count);
+         break;
+     default:
+         return -EINVAL;
+@@ -845,16 +835,18 @@ int hailo_set_device_type(struct hailo_p
+     return 0;
+ }
+ 
+-// On PCIe, just return the address
+-static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id)
++// On PCIe, just return the start address
++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id)
+ {
+     (void)channel_id;
+-    return (u64)dma_address;
++    (void)dma_address_end;
++    (void)step;
++    return (u64)dma_address_start;
+ }
+ 
+ struct hailo_vdma_hw hailo_pcie_vdma_hw = {
+     .hw_ops = {
+-        .encode_desc_dma_address = encode_dma_address
++        .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range,
+     },
+     .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID,
+     .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK,
+@@ -862,11 +854,19 @@ struct hailo_vdma_hw hailo_pcie_vdma_hw
+     .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK,
+ };
+ 
+-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources)
++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources,
++    const struct hailo_pcie_soc_request *request)
+ {
+     const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+-    const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK;
++    BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes");
+ 
+-    // Write shutdown flag to FW
+-    hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, soc_connect_value);
+-}
+\ No newline at end of file
++    hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request);
++    hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK);
++}
++
++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources,
++    struct hailo_pcie_soc_response *response)
++{
++    BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes");
++    hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response);
++}
+--- a/drivers/media/pci/hailo/common/pcie_common.h
++++ b/drivers/media/pci/hailo/common/pcie_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_COMMON_PCIE_COMMON_H_
+@@ -12,6 +12,7 @@
+ #include "fw_operation.h"
+ #include "utils.h"
+ #include "vdma_common.h"
++#include "soc_structs.h"
+ 
+ #include <linux/types.h>
+ #include <linux/firmware.h>
+@@ -21,6 +22,7 @@
+ #define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000)
+ #define BCS_ISTATUS_HOST_DRIVER_DOWN         (0x08000000)
+ #define BCS_ISTATUS_SOC_CONNECT_ACCEPTED     (0x10000000)
++#define BCS_ISTATUS_SOC_CLOSED_IRQ           (0x20000000)
+ #define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK   (0x000000FF)
+ #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK  (0x0000FF00)
+ 
+@@ -42,7 +44,7 @@
+ #define PCI_DEVICE_ID_HAILO_HAILO15   0x45C4
+ #define PCI_DEVICE_ID_HAILO_PLUTO     0x43a2
+ 
+-typedef u32 hailo_ptr_t;
++typedef u64 hailo_ptr_t;
+ 
+ struct hailo_pcie_resources {
+     struct hailo_resource config;               // BAR0
+@@ -63,7 +65,8 @@ struct hailo_atr_config {
+ enum loading_stages {
+     FIRST_STAGE = 0,
+     SECOND_STAGE = 1,
+-    MAX_LOADING_STAGES = 2
++    SECOND_STAGE_LINUX_IN_EMMC = 2,
++    MAX_LOADING_STAGES = 3
+ };
+ 
+ enum hailo_pcie_interrupt_masks {
+@@ -71,6 +74,7 @@ enum hailo_pcie_interrupt_masks {
+     FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION,
+     DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN,
+     SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED,
++    SOC_CLOSED_IRQ = BCS_ISTATUS_SOC_CLOSED_IRQ,
+     VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK,
+     VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK
+ };
+@@ -80,18 +84,13 @@ struct hailo_pcie_interrupt_source {
+     u32 vdma_channels_bitmap;
+ };
+ 
+-struct hailo_config_constants {
+-    const char *filename;
+-    u32 address;
+-    size_t max_size;
+-};
+-
+ struct hailo_file_batch {
+     const char *filename;
+     u32 address;
+     size_t max_size;
+     bool is_mandatory;
+     bool has_header;
++    bool has_core;
+ };
+ 
+ // TODO: HRT-6144 - Align Windows/Linux to QNX
+@@ -130,27 +129,15 @@ void hailo_pcie_disable_interrupts(struc
+ int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command);
+ int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command);
+ 
+-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size);
+ int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage);
+ bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources);
+ bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources);
+ 
+-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources,
+-    struct hailo_d2h_notification *notification);
+-
+-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data,
+-    const size_t config_size, const struct hailo_config_constants *config_consts);
+-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type);
+-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type);
+-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type);
+-
+-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params);
+ int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params);
+ 
+ bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources);
+ void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources);
+-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len);
+-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources);
++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address);
+ 
+ int hailo_set_device_type(struct hailo_pcie_resources *resources);
+ 
+@@ -159,7 +146,12 @@ u32 hailo_get_boot_status(struct hailo_p
+ int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index);
+ void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index);
+ 
+-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources);
++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id);
++
++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources,
++    const struct hailo_pcie_soc_request *request);
++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources,
++    struct hailo_pcie_soc_response *response);
+ 
+ #ifdef __cplusplus
+ }
+--- /dev/null
++++ b/drivers/media/pci/hailo/common/soc_structs.h
+@@ -0,0 +1,79 @@
++// SPDX-License-Identifier: MIT
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * Contains definitions for pcie soc to pcie ep communication
++ */
++
++#ifndef __HAILO_COMMON_SOC_STRUCTS__
++#define __HAILO_COMMON_SOC_STRUCTS__
++
++#include <linux/types.h>
++
++#pragma pack(push, 1)
++
++struct hailo_pcie_soc_connect_request {
++    u16 port;
++};
++
++struct hailo_pcie_soc_connect_response {
++    u8 input_channel_index;
++    u8 output_channel_index;
++};
++
++
++struct hailo_pcie_soc_close_request {
++    u32 channels_bitmap;
++};
++
++struct hailo_pcie_soc_close_response {
++    u8 reserved;
++};
++
++enum hailo_pcie_soc_control_code {
++    // Start from big initial value to ensure the right code was used (using 0
++    // as initiale may cause confusion if the code was not set correctly).
++    HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100,
++    HAILO_PCIE_SOC_CONTROL_CODE_CLOSE,
++    HAILO_PCIE_SOC_CONTROL_CODE_INVALID,
++};
++
++#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES  (16)
++#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16)
++
++// IRQ to signal the PCIe that the EP was closed/released
++#define PCI_EP_SOC_CLOSED_IRQ       (0x00000020)
++#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010)
++
++struct hailo_pcie_soc_request {
++    u32 control_code;
++    union {
++        struct hailo_pcie_soc_connect_request connect;
++        struct hailo_pcie_soc_close_request close;
++        u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES];
++    };
++};
++
++struct hailo_pcie_soc_response {
++    u32 control_code;
++    s32 status;
++    union {
++        struct hailo_pcie_soc_connect_response connect;
++        struct hailo_pcie_soc_close_response close;
++        u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES];
++    };
++};
++
++#pragma pack(pop)
++
++// Compile time validate function. Don't need to call it.
++static inline void __validate_soc_struct_sizes(void)
++{
++    BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) !=
++        sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size");
++    BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) !=
++        sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size");
++}
++
++#endif /* __HAILO_COMMON_SOC_STRUCTS__ */
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/utils.h
++++ b/drivers/media/pci/hailo/common/utils.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_DRIVER_UTILS_H_
+@@ -8,6 +8,11 @@
+ 
+ #include <linux/bitops.h>
+ 
++#define DWORD_SIZE                  (4)
++#define WORD_SIZE                   (2)
++#define BYTE_SIZE                   (1)
++#define BITS_IN_BYTE                (8)
++
+ #define hailo_clear_bit(bit, pval)  { *(pval) &= ~(1 << bit); }
+ #define hailo_test_bit(pos,var_addr)  ((*var_addr) & (1<<(pos)))
+ 
+@@ -50,6 +55,22 @@ static inline uint8_t ceil_log2(uint32_t
+     return result;
+ }
+ 
++// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned.
++#define MAX_POWER_OF_2_VALUE (0x80000000)
++#define POWER_OF_2_ERROR ((uint32_t)-1)
++static inline uint32_t get_nearest_powerof_2(uint32_t value)
++{
++    uint32_t power_of_2 = 1;
++    if (value > MAX_POWER_OF_2_VALUE) {
++        return POWER_OF_2_ERROR;
++    }
++
++    while (value > power_of_2) {
++        power_of_2 <<=  1;
++    }
++    return power_of_2;
++}
++
+ #ifndef DIV_ROUND_UP
+ #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+ #endif
+--- a/drivers/media/pci/hailo/common/vdma_common.c
++++ b/drivers/media/pci/hailo/common/vdma_common.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "vdma_common.h"
+@@ -62,11 +62,6 @@
+ #define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1)
+ #define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK
+ 
+-#define DWORD_SIZE                  (4)
+-#define WORD_SIZE                   (2)
+-#define BYTE_SIZE                   (1)
+-#define BITS_IN_BYTE                (8)
+-
+ #define TIMESTAMPS_CIRC_SPACE(timestamp_list) \
+     CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE)
+ #define TIMESTAMPS_CIRC_CNT(timestamp_list) \
+@@ -150,7 +145,7 @@ static bool validate_last_desc_status(st
+     return true;
+ }
+ 
+-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
++static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
+     u8 data_id)
+ {
+     descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) +
+@@ -174,33 +169,45 @@ static int program_descriptors_in_chunk(
+     u32 max_desc_index,
+     u8 channel_id)
+ {
+-    const u32 desc_per_chunk = DIV_ROUND_UP(chunk_size, desc_list->desc_page_size);
++    const u16 page_size = desc_list->desc_page_size;
++    const u8 ddr_data_id = vdma_hw->ddr_data_id;
++    const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size);
++    const u32 starting_desc_index = desc_index;
++    const u32 residue_size = chunk_size % page_size;
+     struct hailo_vdma_descriptor *dma_desc = NULL;
+-    u16 size_to_program = 0;
+-    u32 index = 0;
+     u64 encoded_addr = 0;
+ 
+-    for (index = 0; index < desc_per_chunk; index++) {
+-        if (desc_index > max_desc_index) {
+-            return -ERANGE;
+-        }
+-
+-        encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address(chunk_addr, channel_id);
+-        if (INVALID_VDMA_ADDRESS == encoded_addr) {
+-            return -EFAULT;
+-        }
++    if (descs_to_program == 0) {
++        // Nothing to program
++        return 0;
++    }
+ 
+-        dma_desc = &desc_list->desc_list[desc_index % desc_list->desc_count];
+-        size_to_program = chunk_size > desc_list->desc_page_size ?
+-            desc_list->desc_page_size : (u16)chunk_size;
+-        hailo_vdma_program_descriptor(dma_desc, encoded_addr, size_to_program, vdma_hw->ddr_data_id);
++    // We iterate through descriptors [desc_index, desc_index + descs_to_program)
++    if (desc_index + descs_to_program > max_desc_index + 1) {
++        return -ERANGE;
++    }
+ 
+-        chunk_addr += size_to_program;
+-        chunk_size -= size_to_program;
+-        desc_index++;
++    encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, channel_id);
++    if (INVALID_VDMA_ADDRESS == encoded_addr) {
++        return -EFAULT;
+     }
+ 
+-    return (int)desc_per_chunk;
++    // Program all descriptors except the last one
++    for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) {
++        // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation.
++        hailo_vdma_program_descriptor(
++            &desc_list->desc_list[desc_index & desc_list->desc_count_mask],
++            encoded_addr, page_size, ddr_data_id);
++        encoded_addr += page_size;
++    }
++
++    // Handle the last descriptor outside of the loop
++    // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation.
++    dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask];
++    hailo_vdma_program_descriptor(dma_desc, encoded_addr,
++        (residue_size == 0) ? page_size : (u16)residue_size, ddr_data_id);
++
++    return (int)descs_to_program;
+ }
+ 
+ static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw,
+@@ -236,11 +243,11 @@ static int bind_and_program_descriptors_
+ {
+     const u8 channel_id = get_channel_id(channel_index);
+     int desc_programmed = 0;
++    int descs_programmed_in_chunk = 0;
+     u32 max_desc_index = 0;
+     u32 chunk_size = 0;
+     struct scatterlist *sg_entry = NULL;
+     unsigned int i = 0;
+-    int ret = 0;
+     size_t buffer_current_offset = 0;
+     dma_addr_t chunk_start_addr = 0;
+     u32 program_size = buffer->size;
+@@ -272,14 +279,14 @@ static int bind_and_program_descriptors_
+             (u32)(sg_dma_len(sg_entry));
+         chunk_size = min((u32)program_size, chunk_size);
+ 
+-        ret = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list,
++        descs_programmed_in_chunk  = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list,
+             starting_desc, max_desc_index, channel_id);
+-        if (ret < 0) {
+-            return ret;
++        if (descs_programmed_in_chunk < 0) {
++            return descs_programmed_in_chunk;
+         }
+ 
+-        desc_programmed += ret;
+-        starting_desc = starting_desc + ret;
++        desc_programmed += descs_programmed_in_chunk;
++        starting_desc = starting_desc + descs_programmed_in_chunk;
+         program_size -= chunk_size;
+         buffer_current_offset += sg_dma_len(sg_entry);
+     }
+@@ -583,21 +590,23 @@ void hailo_vdma_engine_disable_channels(
+     engine->enabled_channels &= ~bitmap;
+ 
+     for_each_vdma_channel(engine, channel, channel_index) {
+-        channel_state_init(&channel->state);
++        if (hailo_test_bit(channel_index, &bitmap)) {
++            channel_state_init(&channel->state);
+ 
+-        while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) {
+-            struct hailo_ongoing_transfer transfer;
+-            ongoing_transfer_pop(channel, &transfer);
+-
+-            if (channel->last_desc_list == NULL) {
+-                pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index);
+-                continue;
++            while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) {
++                struct hailo_ongoing_transfer transfer;
++                ongoing_transfer_pop(channel, &transfer);
++
++                if (channel->last_desc_list == NULL) {
++                    pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index);
++                    continue;
++                }
++
++                clear_dirty_descs(channel, &transfer);
+             }
+ 
+-            clear_dirty_descs(channel, &transfer);
++            channel->last_desc_list = NULL;
+         }
+-
+-        channel->last_desc_list = NULL;
+     }
+ }
+ 
+--- a/drivers/media/pci/hailo/common/vdma_common.h
++++ b/drivers/media/pci/hailo/common/vdma_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_COMMON_VDMA_COMMON_H_
+@@ -30,7 +30,13 @@ struct hailo_vdma_descriptor {
+ 
+ struct hailo_vdma_descriptors_list {
+     struct hailo_vdma_descriptor *desc_list;
+-    u32                           desc_count;  // Must be power of 2 if is_circular is set.
++    // Must be power of 2 if is_circular is set.
++    u32                           desc_count;
++    // The nearest power of 2 to desc_count (including desc_count), minus 1.
++    // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo.
++    // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask'
++    //   will return the same value.
++    u32                           desc_count_mask;
+     u16                           desc_page_size;
+     bool                          is_circular;
+ };
+@@ -113,9 +119,10 @@ struct hailo_vdma_engine {
+ };
+ 
+ struct hailo_vdma_hw_ops {
+-    // Accepts some dma_addr_t mapped to the device and encodes it using
+-    // hw specific encode. returns INVALID_VDMA_ADDRESS on failure.
+-    u64 (*encode_desc_dma_address)(dma_addr_t dma_address, u8 channel_id);
++    // Accepts start, end and step of an address range (of type  dma_addr_t).
++    // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid.
++    // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid.
++    u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id);
+ };
+ 
+ struct hailo_vdma_hw {
+@@ -136,12 +143,9 @@ struct hailo_vdma_hw {
+     for (index = 0, element = &array[index]; index < size; index++, element = &array[index])
+ 
+ #define for_each_vdma_channel(engine, channel, channel_index) \
+-    _for_each_element_array(engine->channels, MAX_VDMA_CHANNELS_PER_ENGINE,   \
++    _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE,   \
+         channel, channel_index)
+ 
+-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
+-    u8 data_id);
+-
+ /**
+  * Program the given descriptors list to map the given buffer.
+  *
+--- a/drivers/media/pci/hailo/src/fops.c
++++ b/drivers/media/pci/hailo/src/fops.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include <linux/version.h>
+@@ -19,14 +19,14 @@
+ #include <linux/sched/signal.h>
+ #endif
+ 
+-#include "utils.h"
+ #include "fops.h"
+ #include "vdma_common.h"
+ #include "utils/logs.h"
+ #include "vdma/memory.h"
+ #include "vdma/ioctl.h"
+ #include "utils/compact.h"
+-#include "pci_soc_ioctl.h"
++#include "nnc.h"
++#include "soc.h"
+ 
+ 
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 )
+@@ -48,13 +48,6 @@
+ // On pcie driver there is only one dma engine
+ #define DEFAULT_VDMA_ENGINE_INDEX       (0)
+ 
+-#if !defined(HAILO_EMULATOR)
+-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5)
+-#else /* !defined(HAILO_EMULATOR) */
+-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000)
+-#endif /* !defined(HAILO_EMULATOR) */
+-
+-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp);
+ 
+ static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp)
+ {
+@@ -124,7 +117,7 @@ int hailo_pcie_fops_open(struct inode *i
+ 
+     previous_power_state = pBoard->pDev->current_state;
+     if (PCI_D0 != previous_power_state) {
+-        hailo_info(pBoard, "Waking up board");
++        hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state);
+         err = pci_set_power_state(pBoard->pDev, PCI_D0);
+         if (err < 0) {
+             hailo_err(pBoard, "Failed waking up board %d", err);
+@@ -148,7 +141,11 @@ int hailo_pcie_fops_open(struct inode *i
+         interrupts_enabled_by_filp = true;
+     }
+ 
+-    err = hailo_add_notification_wait(pBoard, filp);
++    if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++        err = hailo_nnc_file_context_init(pBoard, context);
++    } else {
++        err = hailo_soc_file_context_init(pBoard, context);
++    }
+     if (err < 0) {
+         goto l_release_irq;
+     }
+@@ -166,6 +163,7 @@ l_release_irq:
+ 
+ l_revert_power_state:
+     if (pBoard->pDev->current_state != previous_power_state) {
++        hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state);
+         if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) {
+             hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state);
+         }
+@@ -180,34 +178,6 @@ l_exit:
+     return err;
+ }
+ 
+-int hailo_pcie_driver_down(struct hailo_pcie_board *board)
+-{
+-    long completion_result = 0;
+-    int err = 0;
+-
+-    reinit_completion(&board->driver_down.reset_completed);
+-
+-    hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
+-
+-    // Wait for response
+-    completion_result =
+-        wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS));
+-    if (completion_result <= 0) {
+-        if (0 == completion_result) {
+-            hailo_err(board, "hailo_pcie_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS);
+-            err = -ETIMEDOUT;
+-        } else {
+-            hailo_info(board, "hailo_pcie_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n",
+-                completion_result);
+-            err = completion_result;
+-        }
+-        goto l_exit;
+-    }
+-
+-l_exit:
+-    return err;
+-}
+-
+ int hailo_pcie_fops_release(struct inode *inode, struct file *filp)
+ {
+     struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data;
+@@ -234,12 +204,10 @@ int hailo_pcie_fops_release(struct inode
+             hailo_err(board, "Invalid file context\n");
+         }
+ 
+-        hailo_pcie_clear_notification_wait_list(board, filp);
+-
+-        if (filp == board->vdma.used_by_filp) {
+-            if (hailo_pcie_driver_down(board)) {
+-                hailo_err(board, "Failed sending FW shutdown event");
+-            }
++        if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++            hailo_nnc_file_context_finalize(board, context);
++        } else {
++            hailo_soc_file_context_finalize(board, context);
+         }
+ 
+         hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp);
+@@ -250,6 +218,7 @@ int hailo_pcie_fops_release(struct inode
+             hailo_disable_interrupts(board);
+ 
+             if (power_mode_enabled()) {
++                hailo_info(board, "Power change state to PCI_D3hot\n");
+                 if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) {
+                     hailo_err(board, "Failed setting power state to D3hot");
+                 }
+@@ -301,44 +270,23 @@ static long hailo_memory_transfer_ioctl(
+     return err;
+ }
+ 
+-static long hailo_read_log_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg)
+-{
+-    long err = 0;
+-    struct hailo_read_log_params params;
+-
+-    if (copy_from_user(&params, (void __user*)arg, sizeof(params))) {
+-        hailo_err(pBoard, "HAILO_READ_LOG, copy_from_user fail\n");
+-        return -ENOMEM;
+-    }
+-
+-    if (0 > (err = hailo_pcie_read_firmware_log(&pBoard->pcie_resources, &params))) {
+-        hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
+-        return err;
+-    }
+-
+-    if (copy_to_user((void*)arg, &params, sizeof(params))) {
+-        return -ENOMEM;
+-    }
+-
+-    return 0;
+-}
+-
+ static void firmware_notification_irq_handler(struct hailo_pcie_board *board)
+ {
+     struct hailo_notification_wait *notif_wait_cursor = NULL;
+     int err = 0;
+     unsigned long irq_saved_flags = 0;
+ 
+-    spin_lock_irqsave(&board->notification_read_spinlock, irq_saved_flags);
+-    err = hailo_pcie_read_firmware_notification(&board->pcie_resources, &board->notification_cache);
+-    spin_unlock_irqrestore(&board->notification_read_spinlock, irq_saved_flags);
++    spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags);
++    err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache);
++    spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags);
+ 
+     if (err < 0) {
+         hailo_err(board, "Failed reading firmware notification");
+     }
+     else {
++        // TODO: HRT-14502 move interrupt handling to nnc
+         rcu_read_lock();
+-        list_for_each_entry_rcu(notif_wait_cursor, &board->notification_wait_list, notification_wait_list)
++        list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list)
+         {
+             complete(&notif_wait_cursor->notification_completion);
+         }
+@@ -374,7 +322,7 @@ irqreturn_t hailo_irqhandler(int irq, vo
+ 
+         // wake fw_control if needed
+         if (irq_source.interrupt_bitmask & FW_CONTROL) {
+-            complete(&board->fw_control.completion);
++            complete(&board->nnc.fw_control.completion);
+         }
+ 
+         // wake driver_down if needed
+@@ -392,7 +340,14 @@ irqreturn_t hailo_irqhandler(int irq, vo
+         }
+ 
+         if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) {
+-            complete_all(&board->soc_connect_accepted);
++            complete_all(&board->soc.control_resp_ready);
++        }
++
++        if (irq_source.interrupt_bitmask & SOC_CLOSED_IRQ) {
++            hailo_info(board, "hailo_irqhandler - SOC_CLOSED_IRQ\n");
++            // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. 
++            hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX],
++                0xFFFFFFFF);
+         }
+ 
+         if (0 != irq_source.vdma_channels_bitmap) {
+@@ -404,170 +359,11 @@ irqreturn_t hailo_irqhandler(int irq, vo
+     return return_value;
+ }
+ 
+-static long hailo_get_notification_wait_thread(struct hailo_pcie_board *pBoard, struct file *filp,
+-    struct hailo_notification_wait **current_waiting_thread)
+-{
+-    struct hailo_notification_wait *cursor = NULL;
+-    // note: safe to access without rcu because the notification_wait_list is closed only on file release
+-    list_for_each_entry(cursor, &pBoard->notification_wait_list, notification_wait_list)
+-    {
+-        if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
+-            *current_waiting_thread = cursor;
+-            return 0;
+-        }
+-    }
+-
+-    return -EFAULT;
+-}
+-
+-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp)
+-{
+-    struct hailo_notification_wait *new_notification_wait = NULL;
+-    if (!(new_notification_wait = kmalloc(sizeof(*new_notification_wait), GFP_KERNEL))) {
+-        hailo_err(board, "Failed to allocate notification wait structure.\n");
+-        return -ENOMEM;
+-    }
+-    new_notification_wait->tgid = current->tgid;
+-    new_notification_wait->filp = filp;
+-    new_notification_wait->is_disabled = false;
+-    init_completion(&new_notification_wait->notification_completion);
+-    list_add_rcu(&new_notification_wait->notification_wait_list, &board->notification_wait_list);
+-    return 0;
+-}
+-
+-static long hailo_read_notification_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg, struct file *filp,
+-    bool* should_up_board_mutex)
+-{
+-    long err = 0;
+-    struct hailo_notification_wait *current_waiting_thread = NULL;
+-    struct hailo_d2h_notification *notification = &pBoard->notification_to_user;
+-    unsigned long irq_saved_flags;
+-
+-    err = hailo_get_notification_wait_thread(pBoard, filp, &current_waiting_thread);
+-    if (0 != err) {
+-        goto l_exit;
+-    }
+-    up(&pBoard->mutex);
+-
+-    if (0 > (err = wait_for_completion_interruptible(&current_waiting_thread->notification_completion))) {
+-        hailo_info(pBoard,
+-            "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n",
+-            err, current_waiting_thread->tgid);
+-        *should_up_board_mutex = false;
+-        goto l_exit;
+-    }
+-
+-    if (down_interruptible(&pBoard->mutex)) {
+-        hailo_info(pBoard, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n");
+-        *should_up_board_mutex = false;
+-        err = -ERESTARTSYS;
+-        goto l_exit;
+-    }
+-
+-    // Check if was disabled
+-    if (current_waiting_thread->is_disabled) {
+-        hailo_info(pBoard, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid);
+-        err = -EINVAL;
+-        goto l_exit;
+-    }
+-
+-    reinit_completion(&current_waiting_thread->notification_completion);
+-    
+-    spin_lock_irqsave(&pBoard->notification_read_spinlock, irq_saved_flags);
+-    notification->buffer_len = pBoard->notification_cache.buffer_len;
+-    memcpy(notification->buffer, pBoard->notification_cache.buffer, notification->buffer_len);
+-    spin_unlock_irqrestore(&pBoard->notification_read_spinlock, irq_saved_flags);
+-
+-    if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) {
+-        hailo_err(pBoard, "HAILO_READ_NOTIFICATION copy_to_user fail\n");
+-        err = -ENOMEM;
+-        goto l_exit;
+-    }
+-
+-l_exit:
+-    return err;
+-}
+-
+-static long hailo_disable_notification(struct hailo_pcie_board *pBoard, struct file *filp)
+-{
+-    struct hailo_notification_wait *cursor = NULL;
+-
+-    hailo_info(pBoard, "HAILO_DISABLE_NOTIFICATION: disable notification");
+-    rcu_read_lock();
+-    list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) {
+-        if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
+-            cursor->is_disabled = true;
+-            complete(&cursor->notification_completion);
+-            break;
+-        }
+-    }
+-    rcu_read_unlock();
+-
+-    return 0;
+-}
+-
+-static int hailo_fw_control(struct hailo_pcie_board *pBoard, unsigned long arg, bool* should_up_board_mutex)
+-{
+-    struct hailo_fw_control *command = &pBoard->fw_control.command;
+-    long completion_result = 0;
+-    int err = 0;
+-
+-    up(&pBoard->mutex);
+-    *should_up_board_mutex = false;
+-
+-    if (down_interruptible(&pBoard->fw_control.mutex)) {
+-        hailo_info(pBoard, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid);
+-        return -ERESTARTSYS;
+-    }
+-
+-    if (copy_from_user(command, (void __user*)arg, sizeof(*command))) {
+-        hailo_err(pBoard, "hailo_fw_control, copy_from_user fail\n");
+-        err = -ENOMEM;
+-        goto l_exit;
+-    }
+-
+-    reinit_completion(&pBoard->fw_control.completion);
+-
+-    err = hailo_pcie_write_firmware_control(&pBoard->pcie_resources, command);
+-    if (err < 0) {
+-        hailo_err(pBoard, "Failed writing fw control to pcie\n");
+-        goto l_exit;
+-    }
+-
+-    // Wait for response
+-    completion_result = wait_for_completion_interruptible_timeout(&pBoard->fw_control.completion, msecs_to_jiffies(command->timeout_ms));
+-    if (completion_result <= 0) {
+-        if (0 == completion_result) {
+-            hailo_err(pBoard, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms);
+-            err = -ETIMEDOUT;
+-        } else {
+-            hailo_info(pBoard, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result);
+-            err = -EINTR;
+-        }
+-        goto l_exit;
+-    }
+-
+-    err = hailo_pcie_read_firmware_control(&pBoard->pcie_resources, command);
+-    if (err < 0) {
+-        hailo_err(pBoard, "Failed reading fw control from pcie\n");
+-        goto l_exit;
+-    }
+-
+-    if (copy_to_user((void __user*)arg, command, sizeof(*command))) {
+-        hailo_err(pBoard, "hailo_fw_control, copy_to_user fail\n");
+-        err = -ENOMEM;
+-        goto l_exit;
+-    }
+-
+-l_exit:
+-    up(&pBoard->fw_control.mutex);
+-    return err;
+-}
+-
+ static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg)
+ {
+     struct hailo_device_properties props = {
+         .desc_max_page_size = board->desc_max_page_size,
++        .board_type = board->pcie_resources.board_type,
+         .allocation_mode = board->allocation_mode,
+         .dma_type = HAILO_DMA_TYPE_PCIE,
+         .dma_engines_count = board->vdma.vdma_engines_count,
+@@ -618,24 +414,6 @@ static long hailo_general_ioctl(struct h
+     }
+ }
+ 
+-static long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
+-    struct file *filp, bool *should_up_board_mutex)
+-{
+-    switch (cmd) {
+-    case HAILO_FW_CONTROL:
+-        return hailo_fw_control(board, arg, should_up_board_mutex);
+-    case HAILO_READ_NOTIFICATION:
+-        return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex);
+-    case HAILO_DISABLE_NOTIFICATION:
+-        return hailo_disable_notification(board, filp);
+-    case HAILO_READ_LOG:
+-        return hailo_read_log_ioctl(board, arg);
+-    default:
+-        hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
+-        return -ENOTTY;
+-    }
+-}
+-
+ long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg)
+ {
+     long err = 0;
+@@ -694,7 +472,7 @@ long hailo_pcie_fops_unlockedioctl(struc
+             hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd));
+             err = -EINVAL;
+         } else {
+-            err = hailo_soc_ioctl(board, &context->vdma_context, &board->vdma, cmd, arg);
++            err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg);
+         }
+         break;
+     case HAILO_NNC_IOCTL_MAGIC:
+--- a/drivers/media/pci/hailo/src/fops.h
++++ b/drivers/media/pci/hailo/src/fops.h
+@@ -1,16 +1,17 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_PCI_FOPS_H_
+ #define _HAILO_PCI_FOPS_H_
+ 
++#include "pcie.h"
++
+ int hailo_pcie_fops_open(struct inode* inode, struct file* filp);
+ int hailo_pcie_fops_release(struct inode* inode, struct file* filp);
+ long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg);
+ int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma);
+-int hailo_pcie_driver_down(struct hailo_pcie_board *board);
+ void hailo_pcie_ep_init(struct hailo_pcie_board *board);
+ 
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/nnc.c
+@@ -0,0 +1,299 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW.
++ * The device supports sending controls, receiving notification and reading the FW log.
++ */
++
++#include "nnc.h"
++#include "hailo_ioctl_common.h"
++
++#include "utils/logs.h"
++#include "utils/compact.h"
++
++#include <linux/uaccess.h>
++
++#if !defined(HAILO_EMULATOR)
++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5)
++#else /* !defined(HAILO_EMULATOR) */
++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000)
++#endif /* !defined(HAILO_EMULATOR) */
++
++void hailo_nnc_init(struct hailo_pcie_nnc *nnc)
++{
++    sema_init(&nnc->fw_control.mutex, 1);
++    spin_lock_init(&nnc->notification_read_spinlock);
++    init_completion(&nnc->fw_control.completion);
++    INIT_LIST_HEAD(&nnc->notification_wait_list);
++    memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache));
++}
++
++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc)
++{
++    struct hailo_notification_wait *cursor = NULL;
++
++    // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed
++    rcu_read_lock();
++    list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) {
++        cursor->is_disabled = true;
++        complete(&cursor->notification_completion);
++    }
++    rcu_read_unlock();
++}
++
++static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex)
++{
++    struct hailo_fw_control *command = &board->nnc.fw_control.command;
++    long completion_result = 0;
++    int err = 0;
++
++    up(&board->mutex);
++    *should_up_board_mutex = false;
++
++    if (down_interruptible(&board->nnc.fw_control.mutex)) {
++        hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid);
++        return -ERESTARTSYS;
++    }
++
++    if (copy_from_user(command, (void __user*)arg, sizeof(*command))) {
++        hailo_err(board, "hailo_fw_control, copy_from_user fail\n");
++        err = -ENOMEM;
++        goto l_exit;
++    }
++
++    reinit_completion(&board->nnc.fw_control.completion);
++
++    err = hailo_pcie_write_firmware_control(&board->pcie_resources, command);
++    if (err < 0) {
++        hailo_err(board, "Failed writing fw control to pcie\n");
++        goto l_exit;
++    }
++
++    // Wait for response
++    completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms));
++    if (completion_result <= 0) {
++        if (0 == completion_result) {
++            hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms);
++            err = -ETIMEDOUT;
++        } else {
++            hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result);
++            err = -EINTR;
++        }
++        goto l_exit;
++    }
++
++    err = hailo_pcie_read_firmware_control(&board->pcie_resources, command);
++    if (err < 0) {
++        hailo_err(board, "Failed reading fw control from pcie\n");
++        goto l_exit;
++    }
++
++    if (copy_to_user((void __user*)arg, command, sizeof(*command))) {
++        hailo_err(board, "hailo_fw_control, copy_to_user fail\n");
++        err = -ENOMEM;
++        goto l_exit;
++    }
++
++l_exit:
++    up(&board->nnc.fw_control.mutex);
++    return err;
++}
++
++static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp,
++    struct hailo_notification_wait **current_waiting_thread)
++{
++    struct hailo_notification_wait *cursor = NULL;
++    // note: safe to access without rcu because the notification_wait_list is closed only on file release
++    list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list)
++    {
++        if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
++            *current_waiting_thread = cursor;
++            return 0;
++        }
++    }
++
++    return -EFAULT;
++}
++
++static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp,
++    bool* should_up_board_mutex)
++{
++    long err = 0;
++    struct hailo_notification_wait *current_waiting_thread = NULL;
++    struct hailo_d2h_notification *notification = &board->nnc.notification_to_user;
++    unsigned long irq_saved_flags;
++
++    err = hailo_get_notification_wait_thread(board, filp, &current_waiting_thread);
++    if (0 != err) {
++        goto l_exit;
++    }
++    up(&board->mutex);
++
++    if (0 > (err = wait_for_completion_interruptible(&current_waiting_thread->notification_completion))) {
++        hailo_info(board,
++            "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n",
++            err, current_waiting_thread->tgid);
++        *should_up_board_mutex = false;
++        goto l_exit;
++    }
++
++    if (down_interruptible(&board->mutex)) {
++        hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n");
++        *should_up_board_mutex = false;
++        err = -ERESTARTSYS;
++        goto l_exit;
++    }
++
++    // Check if was disabled
++    if (current_waiting_thread->is_disabled) {
++        hailo_info(board, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid);
++        err = -EINVAL;
++        goto l_exit;
++    }
++
++    reinit_completion(&current_waiting_thread->notification_completion);
++
++    spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags);
++    notification->buffer_len = board->nnc.notification_cache.buffer_len;
++    memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len);
++    spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags);
++
++    if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) {
++        hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n");
++        err = -ENOMEM;
++        goto l_exit;
++    }
++
++l_exit:
++    return err;
++}
++
++static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp)
++{
++    struct hailo_notification_wait *cursor = NULL;
++
++    hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification");
++    rcu_read_lock();
++    list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) {
++        if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
++            cursor->is_disabled = true;
++            complete(&cursor->notification_completion);
++            break;
++        }
++    }
++    rcu_read_unlock();
++
++    return 0;
++}
++
++static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg)
++{
++    long err = 0;
++    struct hailo_read_log_params params;
++
++    if (copy_from_user(&params, (void __user*)arg, sizeof(params))) {
++        hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n");
++        return -ENOMEM;
++    }
++
++    if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, &params))) {
++        hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
++        return err;
++    }
++
++    if (copy_to_user((void*)arg, &params, sizeof(params))) {
++        return -ENOMEM;
++    }
++
++    return 0;
++}
++
++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
++    struct file *filp, bool *should_up_board_mutex)
++{
++    switch (cmd) {
++    case HAILO_FW_CONTROL:
++        return hailo_fw_control(board, arg, should_up_board_mutex);
++    case HAILO_READ_NOTIFICATION:
++        return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex);
++    case HAILO_DISABLE_NOTIFICATION:
++        return hailo_disable_notification(board, filp);
++    case HAILO_READ_LOG:
++        return hailo_read_log_ioctl(board, arg);
++    default:
++        hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++        return -ENOTTY;
++    }
++}
++
++
++static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp)
++{
++    struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL);
++    if (!wait) {
++        hailo_err(board, "Failed to allocate notification wait structure.\n");
++        return -ENOMEM;
++    }
++    wait->tgid = current->tgid;
++    wait->filp = filp;
++    wait->is_disabled = false;
++    init_completion(&wait->notification_completion);
++    list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list);
++    return 0;
++}
++
++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++    return add_notification_wait(board, context->filp);
++}
++
++static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp)
++{
++    struct hailo_notification_wait *cur = NULL, *next = NULL;
++    list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) {
++        if (cur->filp == filp) {
++            list_del_rcu(&cur->notification_wait_list);
++            synchronize_rcu();
++            kfree(cur);
++        }
++    }
++}
++
++int hailo_nnc_driver_down(struct hailo_pcie_board *board)
++{
++    long completion_result = 0;
++    int err = 0;
++
++    reinit_completion(&board->driver_down.reset_completed);
++
++    hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
++
++    // Wait for response
++    completion_result =
++        wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS));
++    if (completion_result <= 0) {
++        if (0 == completion_result) {
++            hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS);
++            err = -ETIMEDOUT;
++        } else {
++            hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n",
++                completion_result);
++            err = completion_result;
++        }
++        goto l_exit;
++    }
++
++l_exit:
++    return err;
++}
++
++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++    clear_notification_wait_list(board, context->filp);
++
++    if (context->filp == board->vdma.used_by_filp) {
++        hailo_nnc_driver_down(board);
++    }
++}
+\ No newline at end of file
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/nnc.h
+@@ -0,0 +1,22 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#ifndef _HAILO_PCI_NNC_H_
++#define _HAILO_PCI_NNC_H_
++
++#include "pcie.h"
++
++void hailo_nnc_init(struct hailo_pcie_nnc *nnc);
++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc);
++
++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
++    struct file *filp, bool *should_up_board_mutex);
++
++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context);
++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context);
++
++int hailo_nnc_driver_down(struct hailo_pcie_board *board);
++
++#endif /* _HAILO_PCI_NNC_H_ */
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.c
++++ /dev/null
+@@ -1,155 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+- **/
+-#include "pci_soc_ioctl.h"
+-
+-#include "utils.h"
+-#include "vdma_common.h"
+-#include "utils/logs.h"
+-#include "vdma/memory.h"
+-
+-#define PCI_SOC_VDMA_ENGINE_INDEX           (0)
+-#define PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS (10000)
+-
+-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+-    struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg)
+-{
+-    switch (cmd) {
+-    case HAILO_SOC_CONNECT:
+-        return hailo_soc_connect_ioctl(board, context, controller, arg);
+-    case HAILO_SOC_CLOSE:
+-        return hailo_soc_close_ioctl(board, controller, arg);
+-    default:
+-        hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
+-        return -ENOTTY;
+-    }
+-}
+-
+-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+-    struct hailo_vdma_controller *controller, unsigned long arg)
+-{
+-    struct hailo_soc_connect_params params;
+-    struct hailo_vdma_channel *input_channel = NULL;
+-    struct hailo_vdma_channel *output_channel = NULL;
+-    struct hailo_vdma_engine *vdma_engine = NULL;
+-    struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL;
+-    struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL;
+-    uint8_t depth = 0;
+-    int err = 0;
+-    long completion_result = 0;
+-
+-    if (copy_from_user(&params, (void *)arg, sizeof(params))) {
+-        hailo_err(board, "copy_from_user fail\n");
+-        return -ENOMEM;
+-    }
+-
+-    // TODO: have pci_ep choose the channel indexes the soc will use - for now use 0 and 16
+-    params.input_channel_index = 0;
+-    params.output_channel_index = 16;
+-
+-    reinit_completion(&board->soc_connect_accepted);
+-    hailo_soc_write_soc_connect(&board->pcie_resources);
+-
+-    // Wait for completion
+-    completion_result = wait_for_completion_interruptible_timeout(&board->soc_connect_accepted,
+-        msecs_to_jiffies(PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS));
+-    if (0 > completion_result) {
+-        if (0 == completion_result) {
+-            hailo_err(board, "Timeout waiting for connect to be accepted (timeout_ms=%d)\n", PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS);
+-            return -ETIMEDOUT;
+-        } else {
+-            hailo_info(board, "soc connect failed with err=%ld (process was interrupted or killed)\n",
+-                completion_result);
+-            return -EINTR;
+-        }
+-    }
+-
+-    vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
+-    input_channel = &vdma_engine->channels[params.input_channel_index];
+-    output_channel = &vdma_engine->channels[params.output_channel_index];
+-
+-    input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.input_desc_handle);
+-    output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.output_desc_handle);
+-    if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) {
+-        hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n");
+-        return -EINVAL;
+-    }
+-
+-    // Make sure channels that we are accepting are not already enabled
+-    if (0 != (vdma_engine->enabled_channels & params.input_channel_index) ||
+-        0 != (vdma_engine->enabled_channels & params.output_channel_index)) {
+-        hailo_dev_err(&board->pDev->dev, "Trying to accept already enabled channels\n");
+-        return -EINVAL;
+-    }
+-
+-    if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) ||
+-        !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) {
+-        hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n");
+-        return -EINVAL;
+-    }
+-
+-    // configure and start input channel
+-    depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count);
+-    // DMA Direction is only to get channel index - so 
+-    err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth,
+-        board->vdma.hw->ddr_data_id);
+-    if (err < 0) {
+-        hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index);
+-        return -EINVAL;
+-    }
+-    
+-    // configure and start output channel
+-    depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count);
+-    // DMA Direction is only to get channel index - so 
+-    err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth,
+-        board->vdma.hw->ddr_data_id);
+-    if (err < 0) {
+-        hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index);
+-        // Close input channel
+-        hailo_vdma_stop_channel(input_channel->host_regs);
+-        return -EINVAL;
+-    }
+-
+-    if (copy_to_user((void *)arg, &params, sizeof(params))) {
+-        hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n");
+-        return -ENOMEM;
+-    }
+-
+-    return 0;
+-}
+-
+-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg)
+-{
+-    struct hailo_soc_close_params params;
+-    struct hailo_vdma_channel *input_channel = NULL;
+-    struct hailo_vdma_channel *output_channel = NULL;
+-    struct hailo_vdma_engine *vdma_engine = NULL;
+-
+-    if (copy_from_user(&params, (void *)arg, sizeof(params))) {
+-        hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n");
+-        return -ENOMEM;
+-    }
+-
+-    vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
+-
+-    if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) {
+-        hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index);
+-        return -EINVAL;
+-    }
+-
+-    if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) {
+-        hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index);
+-        return -EINVAL;
+-    }
+-
+-    input_channel = &vdma_engine->channels[params.input_channel_index];
+-    output_channel = &vdma_engine->channels[params.output_channel_index];
+-
+-    // Close channels
+-    hailo_vdma_stop_channel(input_channel->host_regs);
+-    hailo_vdma_stop_channel(output_channel->host_regs);
+-
+-    hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
+-    return 0;
+-}
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pcie.c
++++ b/drivers/media/pci/hailo/src/pcie.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include <linux/version.h>
+@@ -22,6 +22,8 @@
+ 
+ #include "hailo_ioctl_common.h"
+ #include "pcie.h"
++#include "nnc.h"
++#include "soc.h"
+ #include "fops.h"
+ #include "sysfs.h"
+ #include "utils/logs.h"
+@@ -40,11 +42,12 @@ enum hailo_allocate_driver_buffer_driver
+     HAILO_FORCE_BUFFER_FROM_DRIVER = 2,
+ };
+ 
+-//Debug flag
++// Debug flag
+ static int force_desc_page_size = 0;
+ static bool g_is_power_mode_enabled = true;
+ static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER;
+ static bool force_hailo15_legacy_mode = false;
++static bool force_boot_linux_from_eemc = false;
+ 
+ #define DEVICE_NODE_NAME "hailo"
+ static int char_major = 0;
+@@ -206,7 +209,7 @@ static int hailo_pcie_disable_aspm(struc
+     /* Double-check ASPM control.  If not disabled by the above, the
+      * BIOS is preventing that from happening (or CONFIG_PCIEASPM is
+      * not enabled); override by writing PCI config space directly.
+-     */                       
++     */
+     err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc);
+     if (err < 0) {
+         hailo_err(board, "Couldn't read LNKCTL capability\n");
+@@ -288,101 +291,59 @@ static void hailo_pcie_remove_board(stru
+     up(&g_hailo_add_board_mutex);
+ }
+ 
+-static int hailo_write_config(struct hailo_pcie_resources *resources, struct device *dev,
+-    const struct hailo_config_constants *config_consts)
+-{
+-    const struct firmware *config = NULL;
+-    int err = 0;
+-
+-    if (NULL == config_consts->filename) {
+-        // Config not supported for platform
+-        return 0;
+-    }
+-
+-    err = request_firmware_direct(&config, config_consts->filename, dev);
+-    if (err < 0) {
+-        hailo_dev_info(dev, "Config %s not found\n", config_consts->filename);
+-        return 0;
+-    }
+-
+-    hailo_dev_notice(dev, "Writing config %s\n", config_consts->filename);
+-
+-    err = hailo_pcie_write_config_common(resources, config->data, config->size, config_consts);
+-    if (err < 0) {
+-        if (-EINVAL == err) {
+-            hailo_dev_warn(dev, "Config size %zu is bigger than max %zu\n", config->size, config_consts->max_size);
+-        }
+-        release_firmware(config);
+-        return err;
+-    }
+-
+-    release_firmware(config);
+-    return 0;
+-}
+-
+ static bool wait_for_firmware_completion(struct completion *fw_load_completion)
+ {
+     return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS)));
+ }
+ 
+-static int hailo_load_firmware(struct hailo_pcie_resources *resources,
++static int hailo_load_soc_firmware(struct hailo_pcie_resources *resources,
+     struct device *dev, struct completion *fw_load_completion)
+ {
+-    const struct firmware *firmware = NULL;
+-    int err = 0;
+     u32 boot_status = 0;
++    int err = 0;
++    u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE;
+ 
+     if (hailo_pcie_is_firmware_loaded(resources)) {
+-        hailo_dev_warn(dev, "Firmware was already loaded\n");
++        hailo_dev_warn(dev, "Firmware batch was already loaded\n");
+         return 0;
+     }
+ 
+-    reinit_completion(fw_load_completion);
+-
+-    err = hailo_write_config(resources, dev, hailo_pcie_get_board_config_constants(resources->board_type));
+-    if (err < 0) {
+-        hailo_dev_err(dev, "Failed writing board config");
+-        return err;
+-    }
++    init_completion(fw_load_completion);
+ 
+-    err = hailo_write_config(resources, dev, hailo_pcie_get_user_config_constants(resources->board_type));
++    err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE);
+     if (err < 0) {
+-        hailo_dev_err(dev, "Failed writing fw config");
++        hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+         return err;
+     }
+ 
+-    // read firmware file
+-    err = request_firmware_direct(&firmware, hailo_pcie_get_fw_filename(resources->board_type), dev);
+-    if (err < 0) {
+-        hailo_dev_warn(dev, "Firmware file not found (/lib/firmware/%s), please upload the firmware manually \n",
+-            hailo_pcie_get_fw_filename(resources->board_type));
+-        return 0;
++    if (!wait_for_firmware_completion(fw_load_completion)) {
++        boot_status = hailo_get_boot_status(resources);
++        hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
++        return -ETIMEDOUT;
+     }
++    reinit_completion(fw_load_completion);
+ 
+-    err = hailo_pcie_write_firmware(resources, firmware->data, firmware->size);
++    err = hailo_pcie_write_firmware_batch(dev, resources, second_stage);
+     if (err < 0) {
+-        hailo_dev_err(dev, "Failed writing firmware. err %d\n", err);
+-        release_firmware(firmware);
++        hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+         return err;
+     }
+ 
+-    release_firmware(firmware);
+-
+     if (!wait_for_firmware_completion(fw_load_completion)) {
+         boot_status = hailo_get_boot_status(resources);
+         hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+         return -ETIMEDOUT;
+     }
+ 
+-    hailo_dev_notice(dev, "Firmware was loaded successfully\n");
++    hailo_dev_notice(dev, "Firmware Batch loaded successfully\n");
++
+     return 0;
+ }
+ 
+-static int hailo_load_firmware_batch(struct hailo_pcie_resources *resources,
++static int hailo_load_nnc_firmware(struct hailo_pcie_resources *resources,
+     struct device *dev, struct completion *fw_load_completion)
+ {
+     u32 boot_status = 0;
+-    u32 pcie_finished = 1;
+     int err = 0;
+ 
+     if (hailo_pcie_is_firmware_loaded(resources)) {
+@@ -398,31 +359,13 @@ static int hailo_load_firmware_batch(str
+         return err;
+     }
+ 
+-    hailo_trigger_firmware_boot(resources);
+-
+     if (!wait_for_firmware_completion(fw_load_completion)) {
+         boot_status = hailo_get_boot_status(resources);
+         hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+         return -ETIMEDOUT;
+     }
+-    reinit_completion(fw_load_completion);
+ 
+-    err = hailo_pcie_write_firmware_batch(dev, resources, SECOND_STAGE);
+-    if (err < 0) {
+-        hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+-        return err;
+-    }
+-
+-    // TODO: HRT-13838 - Remove, move address to compat, make write_memory static
+-    write_memory(resources, 0x84000000, (void*)&pcie_finished, sizeof(pcie_finished));
+-
+-    if (!wait_for_firmware_completion(fw_load_completion)) {
+-        boot_status = hailo_get_boot_status(resources);
+-        hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+-        return -ETIMEDOUT;
+-    }
+-
+-    hailo_dev_notice(dev, "Firmware Batch loaded successfully\n");
++    hailo_dev_notice(dev, "Firmware loaded successfully\n");
+ 
+     return 0;
+ }
+@@ -439,15 +382,13 @@ static int hailo_activate_board(struct h
+         return err;
+     }
+ 
+-    switch (board->pcie_resources.board_type) {
+-    case HAILO_BOARD_TYPE_HAILO10H:
+-        err = hailo_load_firmware_batch(&board->pcie_resources, &board->pDev->dev,
++    switch (board->pcie_resources.accelerator_type) {
++    case HAILO_ACCELERATOR_TYPE_SOC:
++        err = hailo_load_soc_firmware(&board->pcie_resources, &board->pDev->dev,
+             &board->fw_loaded_completion);
+         break;
+-    case HAILO_BOARD_TYPE_HAILO10H_LEGACY:
+-    case HAILO_BOARD_TYPE_PLUTO:
+-    case HAILO_BOARD_TYPE_HAILO8:
+-        err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev,
++    case HAILO_ACCELERATOR_TYPE_NNC:
++        err = hailo_load_nnc_firmware(&board->pcie_resources, &board->pDev->dev,
+             &board->fw_loaded_completion);
+         break;
+     default:
+@@ -464,6 +405,7 @@ static int hailo_activate_board(struct h
+ 
+     if (power_mode_enabled()) {
+         // Setting the device to low power state, until the user opens the device
++        hailo_info(board, "Power change state  to PCI_D3hot\n");
+         err = pci_set_power_state(board->pDev, PCI_D3hot);
+         if (err < 0) {
+             hailo_err(board, "Set power state failed %d\n", err);
+@@ -755,21 +697,17 @@ static int hailo_pcie_probe(struct pci_d
+ 
+     pBoard->interrupts_enabled = false;
+     init_completion(&pBoard->fw_loaded_completion);
+-    init_completion(&pBoard->soc_connect_accepted);
+ 
+     sema_init(&pBoard->mutex, 1);
+     atomic_set(&pBoard->ref_count, 0);
+     INIT_LIST_HEAD(&pBoard->open_files_list);
+ 
+-    sema_init(&pBoard->fw_control.mutex, 1);
+-    spin_lock_init(&pBoard->notification_read_spinlock);
+-    init_completion(&pBoard->fw_control.completion);
++    // Init both soc and nnc, since the interrupts are shared.
++    hailo_nnc_init(&pBoard->nnc);
++    hailo_soc_init(&pBoard->soc);
+ 
+     init_completion(&pBoard->driver_down.reset_completed);
+ 
+-    INIT_LIST_HEAD(&pBoard->notification_wait_list);
+-
+-    memset(&pBoard->notification_cache, 0, sizeof(pBoard->notification_cache));
+     memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params));
+ 
+     err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev,
+@@ -832,7 +770,6 @@ probe_exit:
+ static void hailo_pcie_remove(struct pci_dev* pDev)
+ {
+     struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev);
+-    struct hailo_notification_wait *cursor = NULL;
+ 
+     pci_notice(pDev, "Remove: Releasing board\n");
+ 
+@@ -864,13 +801,7 @@ static void hailo_pcie_remove(struct pci
+ 
+         pci_set_drvdata(pDev, NULL);
+ 
+-        // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed
+-        rcu_read_lock();
+-        list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) {
+-            cursor->is_disabled = true;
+-            complete(&cursor->notification_completion);
+-        }
+-        rcu_read_unlock();
++        hailo_nnc_finalize(&pBoard->nnc);
+ 
+         up(&pBoard->mutex);
+ 
+@@ -889,6 +820,15 @@ static void hailo_pcie_remove(struct pci
+ 
+ }
+ 
++inline int driver_down(struct hailo_pcie_board *board)
++{
++    if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++        return hailo_nnc_driver_down(board);
++    } else {
++        return hailo_soc_driver_down(board);
++    }
++}
++
+ #ifdef CONFIG_PM_SLEEP
+ static int hailo_pcie_suspend(struct device *dev)
+ {
+@@ -899,17 +839,16 @@ static int hailo_pcie_suspend(struct dev
+     // lock board to wait for any pending operations
+     down(&board->mutex);
+ 
+-    // Disable all interrupts. All interrupts from Hailo chip would be masked.
+-    hailo_disable_interrupts(board);
+-
+-    // Close all vDMA channels
+     if (board->vdma.used_by_filp != NULL) {
+-        err = hailo_pcie_driver_down(board);
++        err = driver_down(board);
+         if (err < 0) {
+             dev_notice(dev, "Error while trying to call FW to close vdma channels\n");
+         }
+     }
+ 
++    // Disable all interrupts. All interrupts from Hailo chip would be masked.
++    hailo_disable_interrupts(board);
++
+     // Un validate all activae file contexts so every new action would return error to the user.
+     list_for_each_entry(cur, &board->open_files_list, open_files_list) {
+         cur->is_valid = false;
+@@ -919,8 +858,8 @@ static int hailo_pcie_suspend(struct dev
+     up(&board->mutex);
+ 
+     dev_notice(dev, "PM's suspend\n");
+-    // Continue system suspend
+-    return err;
++    // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly)
++    return 0;
+ }
+ 
+ static int hailo_pcie_resume(struct device *dev)
+@@ -930,10 +869,10 @@ static int hailo_pcie_resume(struct devi
+ 
+     if ((err = hailo_activate_board(board)) < 0) {
+         dev_err(dev, "Failed activating board %d\n", err);
+-        return err;
+     }
+ 
+     dev_notice(dev, "PM's resume\n");
++    // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly)
+     return 0;
+ }
+ #endif /* CONFIG_PM_SLEEP */
+@@ -954,7 +893,7 @@ static void hailo_pci_reset_prepare(stru
+         down(&board->mutex);
+         if (board->vdma.used_by_filp != NULL) {
+             // Try to close all vDMA channels before reset
+-            err = hailo_pcie_driver_down(board);
++            err = driver_down(board);
+             if (err < 0) {
+                 pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err);
+             }
+@@ -1088,6 +1027,9 @@ MODULE_PARM_DESC(force_desc_page_size, "
+ module_param(force_hailo15_legacy_mode, bool, S_IRUGO);
+ MODULE_PARM_DESC(force_hailo15_legacy_mode, "Forces work with Hailo15 in legacy mode(relevant for emulators)");
+ 
++module_param(force_boot_linux_from_eemc, bool, S_IRUGO);
++MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)");
++
+ MODULE_AUTHOR("Hailo Technologies Ltd.");
+ MODULE_DESCRIPTION("Hailo PCIe driver");
+ MODULE_LICENSE("GPL v2");
+--- a/drivers/media/pci/hailo/src/pcie.h
++++ b/drivers/media/pci/hailo/src/pcie.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_PCI_PCIE_H_
+@@ -41,6 +41,19 @@ struct hailo_fw_boot {
+ };
+ 
+ 
++struct hailo_pcie_nnc {
++    struct hailo_fw_control_info fw_control;
++
++    spinlock_t notification_read_spinlock;
++    struct list_head notification_wait_list;
++    struct hailo_d2h_notification notification_cache;
++    struct hailo_d2h_notification notification_to_user;
++};
++
++struct hailo_pcie_soc {
++    struct completion control_resp_ready;
++};
++
+ // Context for each open file handle
+ // TODO: store board and use as actual context
+ struct hailo_file_context {
+@@ -48,6 +61,7 @@ struct hailo_file_context {
+     struct file *filp;
+     struct hailo_vdma_file_context vdma_context;
+     bool is_valid;
++    u32 soc_used_channels_bitmap;
+ };
+ 
+ struct hailo_pcie_board {
+@@ -57,21 +71,17 @@ struct hailo_pcie_board {
+     atomic_t ref_count;
+     struct list_head open_files_list;
+     struct hailo_pcie_resources pcie_resources;
+-    struct hailo_fw_control_info fw_control;
++    struct hailo_pcie_nnc nnc;
++    struct hailo_pcie_soc soc;
+     struct hailo_pcie_driver_down_info driver_down;
+     struct semaphore mutex;
+     struct hailo_vdma_controller vdma;
+-    spinlock_t notification_read_spinlock;
+-    struct list_head notification_wait_list;
+-    struct hailo_d2h_notification notification_cache;
+-    struct hailo_d2h_notification notification_to_user;
++
+     struct hailo_memory_transfer_params memory_transfer_params;
+     u32 desc_max_page_size;
+     enum hailo_allocation_mode allocation_mode;
+     struct completion fw_loaded_completion;
+     bool interrupts_enabled;
+-    // Only needed in accelerator type soc
+-    struct completion soc_connect_accepted;
+ };
+ 
+ bool power_mode_enabled(void);
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/soc.c
+@@ -0,0 +1,244 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and
++ * some application processor (pci_ep).
++ */
++
++#include "soc.h"
++
++#include "vdma_common.h"
++#include "utils/logs.h"
++#include "vdma/memory.h"
++
++#include <linux/uaccess.h>
++
++#define PCI_SOC_VDMA_ENGINE_INDEX           (0)
++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000)
++#define PCI_SOC_INPUT_CHANNEL_BITMASK       (0x000000FF)
++
++void hailo_soc_init(struct hailo_pcie_soc *soc)
++{
++    init_completion(&soc->control_resp_ready);
++}
++
++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++    struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg)
++{
++    switch (cmd) {
++    case HAILO_SOC_CONNECT:
++        return hailo_soc_connect_ioctl(board, context, controller, arg);
++    case HAILO_SOC_CLOSE:
++        return hailo_soc_close_ioctl(board, controller, context, arg);
++    default:
++        hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++        return -ENOTTY;
++    }
++}
++
++static int soc_control(struct hailo_pcie_board *board,
++    const struct hailo_pcie_soc_request *request,
++    struct hailo_pcie_soc_response *response)
++{
++    int ret = 0;
++    reinit_completion(&board->soc.control_resp_ready);
++
++    hailo_pcie_soc_write_request(&board->pcie_resources, request);
++
++    ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready,
++        msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS));
++    if (ret <= 0) {
++        if (0 == ret) {
++            hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS);
++            return -ETIMEDOUT;
++        } else {
++            hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n",
++                ret);
++            return ret;
++        }
++    }
++
++    hailo_pcie_soc_read_response(&board->pcie_resources, response);
++    
++    if (response->status < 0) {
++        hailo_err(board, "soc control failed with status=%d\n", response->status);
++        return response->status;
++    }
++
++    if (response->control_code != request->control_code) {
++        hailo_err(board, "Invalid response control code %d (expected %d)\n",
++            response->control_code, request->control_code);
++        return -EINVAL;
++    }
++
++    return 0;
++}
++
++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++    struct hailo_vdma_controller *controller, unsigned long arg)
++{
++    struct hailo_pcie_soc_request request = {0};
++    struct hailo_pcie_soc_response response = {0};
++    struct hailo_soc_connect_params params;
++    struct hailo_vdma_channel *input_channel = NULL;
++    struct hailo_vdma_channel *output_channel = NULL;
++    struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
++    struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL;
++    struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL;
++    uint8_t depth = 0;
++    int err = 0;
++
++    if (copy_from_user(&params, (void *)arg, sizeof(params))) {
++        hailo_err(board, "copy_from_user fail\n");
++        return -ENOMEM;
++    }
++
++    request = (struct hailo_pcie_soc_request) {
++        .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT,
++        .connect = {
++            .port = params.port_number
++        }
++    };
++    err = soc_control(board, &request, &response);
++    if (err < 0) {
++        return err;
++    }
++
++    params.input_channel_index = response.connect.input_channel_index;
++    params.output_channel_index = response.connect.output_channel_index;
++
++    if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) {
++        hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index);
++        return -EINVAL;
++    }
++
++    if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) {
++        hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index);
++        return -EINVAL;
++    }
++
++    input_channel = &vdma_engine->channels[params.input_channel_index];
++    output_channel = &vdma_engine->channels[params.output_channel_index];
++
++    input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle);
++    output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle);
++    if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) {
++        hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n");
++        return -EINVAL;
++    }
++
++    if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) ||
++        !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) {
++        hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n");
++        return -EINVAL;
++    }
++
++    // configure and start input channel
++    depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count);
++    // DMA Direction is only to get channel index - so 
++    err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth,
++        board->vdma.hw->ddr_data_id);
++    if (err < 0) {
++        hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index);
++        return -EINVAL;
++    }
++
++    // Store the input channels state in bitmap (open)
++    hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap);
++    
++    // configure and start output channel
++    depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count);
++    // DMA Direction is only to get channel index - so 
++    err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth,
++        board->vdma.hw->ddr_data_id);
++    if (err < 0) {
++        hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index);
++        // Close input channel
++        hailo_vdma_stop_channel(input_channel->host_regs);
++        return -EINVAL;
++    }
++
++    // Store the output channels state in bitmap (open)
++    hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap);
++
++    if (copy_to_user((void *)arg, &params, sizeof(params))) {
++        hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n");
++        return -ENOMEM;
++    }
++
++    return 0;
++}
++
++static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap)
++{
++    struct hailo_pcie_soc_request request = {0};
++    struct hailo_pcie_soc_response response = {0};
++    struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
++    struct hailo_vdma_channel *channel = NULL;
++    u8 channel_index = 0;
++
++    hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap);
++    for_each_vdma_channel(engine, channel, channel_index) {
++        if (hailo_test_bit(channel_index, &channels_bitmap)) {
++            hailo_vdma_stop_channel(channel->host_regs);
++        }
++    }
++
++    request = (struct hailo_pcie_soc_request) {
++        .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE,
++        .close = {
++            .channels_bitmap = channels_bitmap
++        }
++    };
++    return soc_control(board, &request, &response);
++}
++
++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, 
++    struct hailo_file_context *context, unsigned long arg)
++{
++    struct hailo_soc_close_params params;
++    u32 channels_bitmap = 0;
++    int err = 0;
++
++    if (copy_from_user(&params, (void *)arg, sizeof(params))) {
++        hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n");
++        return -ENOMEM;
++    }
++
++    // TOOD: check channels are connected
++
++    channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index);
++
++    err = close_channels(board, channels_bitmap);
++    if (0 != err) {
++        hailo_dev_err(&board->pDev->dev, "Error closing channels\n");
++        return err;
++    }
++
++    // Store the channel state in bitmap (closed)
++    hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap);
++    hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap);
++
++    return err;
++}
++
++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++    // Nothing to init yet
++    return 0;
++}
++
++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++    // close only channels connected by this (by bitmap)
++    if (context->soc_used_channels_bitmap != 0) {
++        close_channels(board, context->soc_used_channels_bitmap);
++    }
++}
++
++int hailo_soc_driver_down(struct hailo_pcie_board *board)
++{
++    return close_channels(board, 0xFFFFFFFF);
++}
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.h
++++ /dev/null
+@@ -1,19 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+- **/
+-
+-#ifndef _HAILO_PCI_SOC_IOCTL_H_
+-#define _HAILO_PCI_SOC_IOCTL_H_
+-
+-#include "vdma/ioctl.h"
+-#include "pcie.h"
+-
+-
+-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+-    struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg);
+-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+-    struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg);
+-
+-#endif // _HAILO_PCI_SOC_IOCTL_H_
+\ No newline at end of file
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/soc.h
+@@ -0,0 +1,26 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#ifndef _HAILO_PCI_SOC_IOCTL_H_
++#define _HAILO_PCI_SOC_IOCTL_H_
++
++#include "vdma/ioctl.h"
++#include "pcie.h"
++
++
++void hailo_soc_init(struct hailo_pcie_soc *soc);
++
++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++    struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg);
++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++    struct hailo_vdma_controller *controller, unsigned long arg);
++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg);
++
++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context);
++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context);
++
++int hailo_soc_driver_down(struct hailo_pcie_board *board);
++
++#endif // _HAILO_PCI_SOC_IOCTL_H_
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/sysfs.c
++++ b/drivers/media/pci/hailo/src/sysfs.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "sysfs.h"
+--- a/drivers/media/pci/hailo/src/sysfs.h
++++ b/drivers/media/pci/hailo/src/sysfs.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_PCI_SYSFS_H_
+--- a/drivers/media/pci/hailo/src/utils.c
++++ /dev/null
+@@ -1,26 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+- **/
+-
+-#include <linux/version.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/pci.h>
+-
+-#include "pcie.h"
+-#include "utils.h"
+-#include "utils/logs.h"
+-
+-
+-void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp)
+-{
+-    struct hailo_notification_wait *cur = NULL, *next = NULL;
+-    list_for_each_entry_safe(cur, next, &pBoard->notification_wait_list, notification_wait_list) {
+-        if (cur->filp == filp) {
+-            list_del_rcu(&cur->notification_wait_list);
+-            synchronize_rcu();
+-            kfree(cur);
+-        }
+-    }
+-}
+--- a/drivers/media/pci/hailo/utils/compact.h
++++ b/drivers/media/pci/hailo/utils/compact.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_PCI_COMPACT_H_
+--- a/drivers/media/pci/hailo/utils/fw_common.h
++++ b/drivers/media/pci/hailo/utils/fw_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_LINUX_COMMON_H_
+--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "integrated_nnc_utils.h"
+@@ -43,11 +43,19 @@ int hailo_ioremap_shmem(struct platform_
+     void __iomem * remap_ptr;
+ 
+     shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index);
++    if (!shmem) {
++        hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index);
++        return -ENODEV;
++    }
++
+     ret = of_address_to_resource(shmem, 0, &res);
+     if (ret) {
+         hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index);
++        of_node_put(shmem);
+         return ret;
+     }
++
++    // Decrement the refcount of the node
+     of_node_put(shmem);
+ 
+     remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
+--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _INTEGRATED_NNC_UTILS_H_
+--- a/drivers/media/pci/hailo/utils/logs.c
++++ b/drivers/media/pci/hailo/utils/logs.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "logs.h"
+--- a/drivers/media/pci/hailo/utils/logs.h
++++ b/drivers/media/pci/hailo/utils/logs.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _COMMON_LOGS_H_
+--- a/drivers/media/pci/hailo/vdma/ioctl.c
++++ b/drivers/media/pci/hailo/vdma/ioctl.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #include "ioctl.h"
+@@ -12,7 +12,7 @@
+ #include <linux/uaccess.h>
+ 
+ 
+-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg)
++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context)
+ {
+     struct hailo_vdma_enable_channels_params input;
+     struct hailo_vdma_engine *engine = NULL;
+@@ -40,12 +40,15 @@ long hailo_vdma_enable_channels_ioctl(st
+         hailo_vdma_update_interrupts_mask(controller, engine_index);
+         hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n",
+             engine_index, channels_bitmap);
++
++        // Update the context with the enabled channels bitmap
++        context->enabled_channels_bitmap[engine_index] |= channels_bitmap;
+     }
+ 
+     return 0;
+ }
+ 
+-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg)
++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context)
+ {
+     struct hailo_vdma_disable_channels_params input;
+     struct hailo_vdma_engine *engine = NULL;
+@@ -77,6 +80,9 @@ long hailo_vdma_disable_channels_ioctl(s
+ 
+         hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n",
+             engine_index, channels_bitmap);
++
++        // Update the context with the disabled channels bitmap
++        context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap;
+     }
+ 
+     // Wake up threads waiting
+@@ -204,7 +210,7 @@ long hailo_vdma_buffer_map_ioctl(struct
+         return -EFAULT;
+     }
+ 
+-    hailo_dev_info(controller->dev, "address %lx tgid %d size: %zu\n",
++    hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n",
+         buf_info.user_address, current->tgid, buf_info.size);
+ 
+     direction = get_dma_direction(buf_info.data_direction);
+@@ -231,7 +237,7 @@ long hailo_vdma_buffer_map_ioctl(struct
+     }
+ 
+     list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list);
+-    hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n",
++    hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n",
+         buf_info.user_address, buf_info.mapped_handle);
+     return 0;
+ }
+@@ -247,7 +253,7 @@ long hailo_vdma_buffer_unmap_ioctl(struc
+         return -EFAULT;
+     }
+ 
+-    hailo_dev_info(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle);
++    hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle);
+ 
+     mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle);
+     if (mapped_buffer == NULL) {
+--- a/drivers/media/pci/hailo/vdma/ioctl.h
++++ b/drivers/media/pci/hailo/vdma/ioctl.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #ifndef _HAILO_VDMA_IOCTL_H_
+@@ -8,8 +8,8 @@
+ 
+ #include "vdma/vdma.h"
+ 
+-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context);
++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context);
+ long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg,
+     struct semaphore *mutex, bool *should_up_board_mutex);
+ 
+--- a/drivers/media/pci/hailo/vdma/memory.c
++++ b/drivers/media/pci/hailo/vdma/memory.c
+@@ -1,11 +1,12 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #define pr_fmt(fmt) "hailo: " fmt
+ 
+ #include "memory.h"
++#include "utils.h"
+ #include "utils/compact.h"
+ 
+ #include <linux/slab.h>
+@@ -316,6 +317,11 @@ int hailo_desc_list_create(struct device
+     size_t buffer_size = 0;
+     const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB  (from the VDMA registers documentation)
+ 
++    if (MAX_POWER_OF_2_VALUE < descriptors_count) {
++        dev_err(dev, "Invalid descriptors count %u\n", descriptors_count);
++        return -EINVAL;
++    }
++
+     buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor);
+     buffer_size = ALIGN(buffer_size, align);
+ 
+@@ -323,7 +329,7 @@ int hailo_desc_list_create(struct device
+         &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO);
+     if (descriptors->kernel_address == NULL) {
+         dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory "
+-            "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n",
++            "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n",
+             descriptors_count, buffer_size);
+         return -ENOMEM;
+     }
+@@ -333,6 +339,8 @@ int hailo_desc_list_create(struct device
+ 
+     descriptors->desc_list.desc_list = descriptors->kernel_address;
+     descriptors->desc_list.desc_count = descriptors_count;
++    // No need to check the return value of get_nearest_powerof_2 because we already checked the input
++    descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1);
+     descriptors->desc_list.desc_page_size = desc_page_size;
+     descriptors->desc_list.is_circular = is_circular;
+ 
+--- a/drivers/media/pci/hailo/vdma/memory.h
++++ b/drivers/media/pci/hailo/vdma/memory.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ /**
+  * vDMA memory utility (including allocation and mappings)
+--- a/drivers/media/pci/hailo/vdma/vdma.c
++++ b/drivers/media/pci/hailo/vdma/vdma.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ 
+ #define pr_fmt(fmt) "hailo: " fmt
+@@ -105,6 +105,9 @@ void hailo_vdma_file_context_init(struct
+     INIT_LIST_HEAD(&context->descriptors_buffer_list);
+     INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list);
+     INIT_LIST_HEAD(&context->continuous_buffer_list);
++
++    BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE,
++        "Unexpected amount of VDMA channels per engine");
+ }
+ 
+ void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller,
+@@ -119,21 +122,22 @@ void hailo_vdma_file_context_finalize(st
+ {
+     size_t engine_index = 0;
+     struct hailo_vdma_engine *engine = NULL;
+-    const u32 channels_bitmap = 0xFFFFFFFF; // disable all channel interrupts
+     unsigned long irq_saved_flags = 0;
+     // In case of FLR, the vdma registers will be NULL
+     const bool is_device_up = (NULL != controller->dev);
+ 
+-    if (filp == controller->used_by_filp) {
+-        for_each_vdma_engine(controller, engine, engine_index) {
+-            hailo_vdma_engine_disable_channels(engine, channels_bitmap);
++    for_each_vdma_engine(controller, engine, engine_index) {
++        if (context->enabled_channels_bitmap[engine_index]) {
++            hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index, 
++            context->enabled_channels_bitmap[engine_index]);
++            hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]);
+ 
+             if (is_device_up) {
+                 hailo_vdma_update_interrupts_mask(controller, engine_index);
+             }
+ 
+             spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
+-            hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap);
++            hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]);
+             spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+         }
+     }
+@@ -148,10 +152,21 @@ void hailo_vdma_file_context_finalize(st
+     }
+ }
+ 
++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, 
++    u32 channels_bitmap)
++{
++    unsigned long irq_saved_flags = 0;
++
++    spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
++    hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap);
++    spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
++
++    wake_up_interruptible_all(&controller->interrupts_wq);
++}
++
+ void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller,
+     size_t engine_index, u32 channels_bitmap)
+ {
+-    unsigned long irq_saved_flags = 0;
+     struct hailo_vdma_engine *engine = NULL;
+ 
+     BUG_ON(engine_index >= controller->vdma_engines_count);
+@@ -159,11 +174,7 @@ void hailo_vdma_irq_handler(struct hailo
+ 
+     hailo_vdma_engine_push_timestamps(engine, channels_bitmap);
+ 
+-    spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
+-    hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap);
+-    spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+-
+-    wake_up_interruptible_all(&controller->interrupts_wq);
++    hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap);
+ }
+ 
+ long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller,
+@@ -171,9 +182,9 @@ long hailo_vdma_ioctl(struct hailo_vdma_
+ {
+     switch (cmd) {
+     case HAILO_VDMA_ENABLE_CHANNELS:
+-        return hailo_vdma_enable_channels_ioctl(controller, arg);
++        return hailo_vdma_enable_channels_ioctl(controller, arg, context);
+     case HAILO_VDMA_DISABLE_CHANNELS:
+-        return hailo_vdma_disable_channels_ioctl(controller, arg);
++        return hailo_vdma_disable_channels_ioctl(controller, arg, context);
+     case HAILO_VDMA_INTERRUPTS_WAIT:
+         return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex);
+     case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS:
+--- a/drivers/media/pci/hailo/vdma/vdma.h
++++ b/drivers/media/pci/hailo/vdma/vdma.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+  **/
+ /**
+  * Hailo vdma engine definitions
+@@ -130,6 +130,7 @@ struct hailo_vdma_file_context {
+     struct list_head descriptors_buffer_list;
+     struct list_head vdma_low_memory_buffer_list;
+     struct list_head continuous_buffer_list;
++    u32 enabled_channels_bitmap[MAX_VDMA_ENGINES];
+ };
+ 
+ 
+@@ -145,6 +146,8 @@ void hailo_vdma_file_context_init(struct
+ void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context,
+     struct hailo_vdma_controller *controller, struct file *filp);
+ 
++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, 
++    u32 channels_bitmap);
+ void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index,
+     u32 channels_bitmap);
+ 

+ 108 - 0
target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch

@@ -0,0 +1,108 @@
+From dbf12796d1368286672529d7b03f81066a8c36f3 Mon Sep 17 00:00:00 2001
+From: Iker Pedrosa <[email protected]>
+Date: Mon, 18 Nov 2024 10:55:33 +0100
+Subject: [PATCH] dtoverlays: enable SPI CS active-high
+
+The documentation isn't very clear explaining how to enable SPI CS
+active-high and it takes a long time to understand it. Adding a specific
+overlay as a simple example on how to invert this signal can help
+understand the solution.
+
+Link: https://forums.raspberrypi.com/viewtopic.php?t=378222
+Signed-off-by: Iker Pedrosa <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  8 +++
+ .../overlays/spi0-1cs-inverted-overlay.dts    | 59 +++++++++++++++++++
+ 3 files changed, 68 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -259,6 +259,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	spi-rtc.dtbo \
+ 	spi0-0cs.dtbo \
+ 	spi0-1cs.dtbo \
++	spi0-1cs-inverted.dtbo \
+ 	spi0-2cs.dtbo \
+ 	spi1-1cs.dtbo \
+ 	spi1-2cs.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4438,6 +4438,14 @@ Params: cs0_pin                 GPIO pin
+                                 it for other uses.
+ 
+ 
++Name:   spi0-1cs-inverted
++Info:   Only use one CS pin for SPI0 and set to active-high
++Load:   dtoverlay=spi0-1cs-inverted,<param>=<val>
++Params: cs0_pin                 GPIO pin for CS0 (default 8)
++        no_miso                 Don't claim and use the MISO pin (9), freeing
++                                it for other uses.
++
++
+ Name:   spi0-2cs
+ Info:   Change the CS pins for SPI0
+ Load:   dtoverlay=spi0-2cs,<param>=<val>
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts
+@@ -0,0 +1,59 @@
++/dts-v1/;
++/plugin/;
++
++/*
++ * There are some devices that need an inverted Chip Select (CS) to select the
++ * device signal, as an example the AZDelivery 12864 display. That means that
++ * the CS polarity is active-high. To invert the CS signal the DT needs to set
++ * the cs-gpio to GPIO_ACTIVE_HIGH (0) in the controller and set the
++ * spi-cs-high in the peripheral property. On top of that, since this is a
++ * display the DT also needs to specify the write-only property.
++*/
++
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++	compatible = "brcm,bcm2835";
++
++	fragment@0 {
++		target = <&spi0_cs_pins>;
++		frag0: __overlay__ {
++			brcm,pins = <8>;
++		};
++	};
++
++	fragment@1 {
++		target = <&spi0>;
++		frag1: __overlay__ {
++			cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
++			status = "okay";
++		};
++	};
++
++	fragment@2 {
++		target = <&spidev1>;
++		__overlay__ {
++			status = "disabled";
++		};
++	};
++
++	fragment@3 {
++		target = <&spi0_pins>;
++		__dormant__ {
++			brcm,pins = <10 11>;
++		};
++	};
++
++	fragment@4 {
++		target = <&spidev0>;
++		__overlay__ {
++			spi-cs-high;
++		};
++	};
++
++	__overrides__ {
++		cs0_pin  = <&frag0>,"brcm,pins:0",
++			   <&frag1>,"cs-gpios:4";
++		no_miso = <0>,"=3";
++	};
++};

+ 64 - 0
target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch

@@ -0,0 +1,64 @@
+From 57b528e557890f25e010b6bc7356b5a716c79db2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 12 Nov 2024 17:58:52 +0000
+Subject: [PATCH] drm/vc4: hvs: Defer updating the enable_bg_fill until vblank
+
+The register to enable/disable background fill was being set
+from atomic flush, however that will be applied immediately and
+can be a while before the vblank. If it was required for the
+current frame but not for the next one, that can result in
+corruption for part of the current frame.
+
+Store the state in vc4_hvs, and update it on vblank.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h |  2 ++
+ drivers/gpu/drm/vc4/vc4_hvs.c | 18 ++++++++++--------
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -339,6 +339,8 @@ struct vc4_hvs {
+ 		unsigned int enabled: 1;
+ 	} eof_irq[HVS_NUM_CHANNELS];
+ 
++	bool bg_fill[HVS_NUM_CHANNELS];
++
+ 	unsigned long max_core_rate;
+ 
+ 	/* Memory manager for CRTCs to allocate space in the display
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1470,14 +1470,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ 		/* This sets a black background color fill, as is the case
+ 		 * with other DRM drivers.
+ 		 */
+-		if (enable_bg_fill)
+-			HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+-				  HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
+-				  SCALER6(DISPX_CTRL1_BGENB));
+-		else
+-			HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+-				  HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
+-				  ~SCALER6(DISPX_CTRL1_BGENB));
++		hvs->bg_fill[channel] = enable_bg_fill;
+ 	} else {
+ 		/* we can actually run with a lower core clock when background
+ 		 * fill is enabled on VC4_GEN_5 so leave it enabled always.
+@@ -1662,6 +1655,15 @@ static irqreturn_t vc6_hvs_eof_irq_handl
+ 		if (hvs->eof_irq[i].desc != irq)
+ 			continue;
+ 
++		if (hvs->bg_fill[i])
++			HVS_WRITE(SCALER6_DISPX_CTRL1(i),
++				  HVS_READ(SCALER6_DISPX_CTRL1(i)) |
++				  SCALER6(DISPX_CTRL1_BGENB));
++		else
++			HVS_WRITE(SCALER6_DISPX_CTRL1(i),
++				  HVS_READ(SCALER6_DISPX_CTRL1(i)) &
++				  ~SCALER6(DISPX_CTRL1_BGENB));
++
+ 		vc4_hvs_schedule_dlist_sweep(hvs, i);
+ 		return IRQ_HANDLED;
+ 	}

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1359-misc-rp1-pio-Add-FIFO.patch → target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch

@@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  
 --- a/drivers/misc/rp1-pio.c
 +++ b/drivers/misc/rp1-pio.c
-@@ -479,6 +479,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi
+@@ -476,6 +476,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi
  }
  EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl);
  
@@ -56,7 +56,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param)
  {
  	struct rp1_gpio_init_args *args = param;
-@@ -851,6 +873,8 @@ struct handler_info {
+@@ -848,6 +870,8 @@ struct handler_info {
  	HANDLER(SM_PUT, sm_put),
  	HANDLER(SM_GET, sm_get),
  	HANDLER(SM_SET_DMACTRL, sm_set_dmactrl),

+ 31 - 0
target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch

@@ -0,0 +1,31 @@
+From fa6ad4bcad4e8db18493a4af640b4b5c95434e70 Mon Sep 17 00:00:00 2001
+From: Just a nerd <[email protected]>
+Date: Wed, 20 Nov 2024 14:08:48 +0000
+Subject: [PATCH] overlays: Enable Raspberry Touch 2 rotation with overlay
+
+See: https://github.com/raspberrypi/linux/pull/6480
+Signed-off-by: foonerd <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README                                | 1 +
+ arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5249,6 +5249,7 @@ Params: sizex                   Touchscr
+         invy                    Touchscreen inverted y axis
+         swapxy                  Touchscreen swapped x y axis
+         disable_touch           Disables the touch screen overlay driver
++        rotation                Display rotation {0,90,180,270} (default 0)
+         dsi0                    Use DSI0 and i2c_csi_dsi0 (rather than
+                                 the default DSI1 and i2c_csi_dsi).
+ 
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts
+@@ -118,5 +118,6 @@
+ 		invy = <0>, "+11";
+ 		swapxy = <&gt911>,"touchscreen-swapped-x-y?";
+ 		disable_touch = <&gt911>, "status=disabled";
++		rotation = <&dsi_panel>, "rotation:0";
+ 	};
+ };

+ 3 - 3
target/linux/bcm27xx/patches-6.6/950-1357-rpi-pio-add-missing.patch → target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch

@@ -13,7 +13,7 @@ Signed-off-by: Phil Elwell <[email protected]>
 
 --- a/include/linux/pio_rp1.h
 +++ b/include/linux/pio_rp1.h
-@@ -245,7 +245,7 @@ static inline bool pio_can_add_program_a
+@@ -247,7 +247,7 @@ static inline bool pio_can_add_program_a
  	return !rp1_pio_can_add_program(client, &args);
  }
  
@@ -22,7 +22,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  {
  	struct rp1_pio_add_program_args args;
  	int offset;
-@@ -365,7 +365,7 @@ static inline int pio_sm_set_config(stru
+@@ -367,7 +367,7 @@ static inline int pio_sm_set_config(stru
  	return rp1_pio_sm_set_config(client, &args);
  }
  
@@ -31,7 +31,7 @@ Signed-off-by: Phil Elwell <[email protected]>
  {
  	struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false };
  
-@@ -375,7 +375,7 @@ int pio_sm_exec(struct rp1_pio_client *c
+@@ -377,7 +377,7 @@ int pio_sm_exec(struct rp1_pio_client *c
  	return rp1_pio_sm_exec(client, &args);
  }
  

+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1358-misc-rpi-pio-back-port-some.patch → target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch


+ 1167 - 0
target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch

@@ -0,0 +1,1167 @@
+From 8a6f640708627ac8ebf79f88793038933f169198 Mon Sep 17 00:00:00 2001
+From: Giedrius <[email protected]>
+Date: Thu, 21 Nov 2024 08:04:02 +0000
+Subject: [PATCH] Adding Pimidi kernel module.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Giedrius Trainavičius <[email protected]>
+---
+ sound/drivers/Kconfig  |   10 +
+ sound/drivers/Makefile |    2 +
+ sound/drivers/pimidi.c | 1113 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1125 insertions(+)
+ create mode 100644 sound/drivers/pimidi.c
+
+--- a/sound/drivers/Kconfig
++++ b/sound/drivers/Kconfig
+@@ -263,4 +263,14 @@ config SND_AC97_POWER_SAVE_DEFAULT
+ 
+ 	  See SND_AC97_POWER_SAVE for more details.
+ 
++config SND_PIMIDI
++	tristate "Pimidi driver"
++	depends on SND_SEQUENCER && CRC8
++	select SND_RAWMIDI
++	help
++	  Say Y here to include support for Blokas Pimidi.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called snd-pimidi.
++
+ endif	# SND_DRIVERS
+--- a/sound/drivers/Makefile
++++ b/sound/drivers/Makefile
+@@ -9,6 +9,7 @@ snd-aloop-objs := aloop.o
+ snd-mtpav-objs := mtpav.o
+ snd-mts64-objs := mts64.o
+ snd-pcmtest-objs := pcmtest.o
++snd-pimidi-objs := pimidi.o
+ snd-portman2x4-objs := portman2x4.o
+ snd-serial-u16550-objs := serial-u16550.o
+ snd-serial-generic-objs := serial-generic.o
+@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-s
+ obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o
+ obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+ obj-$(CONFIG_SND_MTS64) += snd-mts64.o
++obj-$(CONFIG_SND_PIMIDI) += snd-pimidi.o
+ obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
+ 
+ obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
+--- /dev/null
++++ b/sound/drivers/pimidi.c
+@@ -0,0 +1,1113 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Pimidi Linux kernel module.
++ * Copyright (C) 2017-2024  Vilniaus Blokas UAB, https://blokas.io/
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the
++ * License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/completion.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/irq.h>
++#include <linux/irqdesc.h>
++#include <linux/bitops.h>
++#include <linux/of_irq.h>
++#include <linux/kfifo.h>
++#include <linux/list.h>
++#include <linux/workqueue.h>
++#include <linux/gpio.h>
++#include <linux/gpio/consumer.h>
++#include <linux/interrupt.h>
++#include <linux/mutex.h>
++#include <linux/refcount.h>
++#include <linux/crc8.h>
++#include <linux/delay.h>
++
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/rawmidi.h>
++#include <sound/asequencer.h>
++#include <sound/info.h>
++
++#define PIMIDI_LOG_IMPL(instance, log_func, msg, ...) log_func("pimidi(%s)[%c]: " msg "\n", \
++	__func__, (instance) ? (instance)->d + '0' : 'G', ## __VA_ARGS__)
++
++#ifdef PIMIDI_DEBUG
++#	define printd(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert, __VA_ARGS__)
++#	define printd_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert_ratelimited, __VA_ARGS__)
++#	define printd_g(...) printd((struct pimidi_instance *)NULL, __VA_ARGS__)
++#else
++#	define printd(instance, ...) do {} while (0)
++#	define printd_rl(instance, ...) do {} while (0)
++#	define printd_g(...) do {} while (0)
++#endif
++
++#define printe(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err, __VA_ARGS__)
++#define printe_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err_ratelimited, __VA_ARGS__)
++#define printi(instance, ...) PIMIDI_LOG_IMPL(instance, pr_info, __VA_ARGS__)
++#define printw(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn, __VA_ARGS__)
++#define printw_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn_ratelimited, __VA_ARGS__)
++
++#define printe_g(...) printe((struct pimidi_instance *)NULL, __VA_ARGS__)
++#define printi_g(...) printi((struct pimidi_instance *)NULL, __VA_ARGS__)
++
++DECLARE_CRC8_TABLE(pimidi_crc8_table);
++enum { PIMIDI_CRC8_POLYNOMIAL = 0x83 };
++enum { PIMIDI_MAX_DEVICES = 4 };
++enum { PIMIDI_MAX_PACKET_SIZE = 17 };
++enum { PIMIDI_PORTS = 2 };
++
++struct pimidi_shared {
++	// lock protects the shared reset_gpio and devices list.
++	struct mutex            lock;
++	struct gpio_desc        *reset_gpio;
++	struct workqueue_struct *work_queue;
++	struct list_head        devices;
++};
++
++static struct pimidi_shared pimidi_global = {
++	.devices = LIST_HEAD_INIT(pimidi_global.devices),
++};
++
++struct pimidi_version_t {
++	u8 hwrev;
++	u8 major;
++	u8 minor;
++	u8 build;
++};
++
++enum { PIMIDI_IN_FIFO_SIZE = 4096 };
++
++struct pimidi_midi_port {
++	// in_lock protects the input substream.
++	struct mutex                      in_lock;
++	// out_lock protects the output substream.
++	struct mutex                      out_lock;
++	DECLARE_KFIFO(in_fifo, uint8_t, PIMIDI_IN_FIFO_SIZE);
++	unsigned int                      last_output_at;
++	unsigned int                      output_buffer_used_in_millibytes;
++	struct work_struct                in_handler;
++	struct delayed_work               out_handler;
++	unsigned long                     enabled_streams;
++	unsigned int                      tx_cnt;
++	unsigned int                      rx_cnt;
++};
++
++struct pimidi_instance {
++	struct list_head                  list;
++	struct i2c_client                 *i2c_client;
++	struct pimidi_version_t           version;
++	char                              serial[11];
++	char                              d;
++	struct gpio_desc                  *data_ready_gpio;
++
++	struct work_struct                drdy_handler;
++
++	// comm_lock serializes I2C communication.
++	struct mutex                      comm_lock;
++	char                              *rx_buf;
++	size_t                            rx_len;
++	int                               rx_status;
++	struct completion                 *rx_completion;
++
++	struct snd_rawmidi                *rawmidi;
++	struct pimidi_midi_port           midi_port[PIMIDI_PORTS];
++	bool                              stopping;
++};
++
++static struct snd_rawmidi_substream *pimidi_find_substream(struct snd_rawmidi *rawmidi,
++							   int stream,
++							   int number
++	)
++{
++	struct snd_rawmidi_substream *substream;
++
++	list_for_each_entry(substream, &rawmidi->streams[stream].substreams, list) {
++		if (substream->number == number)
++			return substream;
++	}
++	return NULL;
++}
++
++static void pimidi_midi_in_handler(struct pimidi_instance *instance, int port)
++{
++	int i, n, err;
++
++	printd(instance, "(%d)", port);
++
++	struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++	if (!test_bit(SNDRV_RAWMIDI_STREAM_INPUT, &midi_port->enabled_streams)) {
++		printd(instance, "Input not enabled for %d", port);
++		return;
++	}
++
++	u8 data[512];
++
++	n = kfifo_out_peek(&midi_port->in_fifo, data, sizeof(data));
++	printd(instance, "Peeked %d MIDI bytes", n);
++
++	mutex_lock(&midi_port->in_lock);
++	struct snd_rawmidi_substream *substream =
++		pimidi_find_substream(instance->rawmidi,
++				      SNDRV_RAWMIDI_STREAM_INPUT,
++				      port);
++
++	err = snd_rawmidi_receive(substream, data, n);
++	if (err > 0)
++		midi_port->rx_cnt += err;
++	mutex_unlock(&midi_port->in_lock);
++
++	for (i = 0; i < err; ++i)
++		kfifo_skip(&midi_port->in_fifo);
++
++	if (n != err)
++		printw_rl(instance,
++			  "Not all MIDI data consumed for port %d: %d / %d", port, err, n);
++
++	if (!kfifo_is_empty(&midi_port->in_fifo) && !instance->stopping)
++		queue_work(pimidi_global.work_queue, &midi_port->in_handler);
++
++	printd(instance, "Done");
++}
++
++static void pimidi_midi_in_handler_0(struct work_struct *work)
++{
++	pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[0].in_handler),
++			       0);
++}
++
++static void pimidi_midi_in_handler_1(struct work_struct *work)
++{
++	pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[1].in_handler),
++			       1);
++}
++
++static void pimidi_midi_out_handler(struct pimidi_instance *instance, int port)
++{
++	printd(instance, "(%d)", port);
++	if (!test_bit(SNDRV_RAWMIDI_STREAM_OUTPUT, &instance->midi_port[port].enabled_streams)) {
++		printd(instance, "Output not enabled for %d", port);
++		return;
++	}
++
++	struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++	struct snd_rawmidi_substream *substream =
++		pimidi_find_substream(instance->rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, port);
++
++	mutex_lock(&midi_port->out_lock);
++
++	enum { MIDI_MILLI_BYTES_PER_JIFFY = 3125000 / HZ };
++	enum { MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES =
++		(512 - PIMIDI_MAX_PACKET_SIZE - 1) * 1000 };
++
++	unsigned int now = jiffies;
++	unsigned int millibytes_became_available =
++		(MIDI_MILLI_BYTES_PER_JIFFY) * (now - midi_port->last_output_at);
++
++	midi_port->output_buffer_used_in_millibytes =
++		midi_port->output_buffer_used_in_millibytes <=
++		millibytes_became_available ? 0 : midi_port->output_buffer_used_in_millibytes -
++		millibytes_became_available;
++
++	unsigned int output_buffer_available =
++		(MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES
++		- midi_port->output_buffer_used_in_millibytes)
++		/ 1000;
++
++	u8 buffer[PIMIDI_MAX_PACKET_SIZE];
++	int n, batch, err;
++
++	for (batch = 0; batch < 3; ++batch) {
++		if (output_buffer_available == 0)
++			printd(instance, "Buffer full");
++
++		printd(instance, "Buffer available: %u (%u +%u, %u -> %u, dt %u) (%u) @ %u",
++		       output_buffer_available, midi_port->output_buffer_used_in_millibytes,
++		       millibytes_became_available, midi_port->last_output_at, now,
++		       now - midi_port->last_output_at, midi_port->tx_cnt, HZ);
++		midi_port->last_output_at = now;
++
++		n = output_buffer_available
++			? snd_rawmidi_transmit_peek(substream, buffer + 1,
++						    min(output_buffer_available,
++							sizeof(buffer) - 2))
++			: 0;
++		if (n > 0) {
++			printd(instance, "Peeked: %d", n);
++			snd_rawmidi_transmit_ack(substream, n);
++
++			buffer[0] = (port << 4) | n;
++			buffer[n + 1] = ~crc8(pimidi_crc8_table, buffer, n + 1, CRC8_INIT_VALUE);
++
++#ifdef PIMIDI_DEBUG
++			pr_debug("%s[%d]: Sending %d bytes:", __func__, instance->d, n + 2);
++			int i;
++
++			for (i = 0; i < n + 2; ++i)
++				pr_cont(" %02x", buffer[i]);
++
++			pr_cont("\n");
++#endif
++			mutex_lock(&instance->comm_lock);
++			err = i2c_master_send(instance->i2c_client, buffer, n + 2);
++			mutex_unlock(&instance->comm_lock);
++
++			if (err < 0) {
++				printe(instance,
++				       "Error occurred when sending MIDI data over I2C! (%d)",
++				       err);
++				goto cleanup;
++			}
++
++			midi_port->tx_cnt += n;
++			midi_port->output_buffer_used_in_millibytes += n * 1000;
++			output_buffer_available -= n;
++		} else if (n < 0) {
++			err = n;
++			printe(instance, "snd_rawmidi_transmit_peek returned error %d!", err);
++			goto cleanup;
++		} else {
++			break;
++		}
++	}
++
++	printd(instance, "Checking if empty %p", substream);
++	if (!snd_rawmidi_transmit_empty(substream) && !instance->stopping) {
++		unsigned int delay = 1;
++
++		if (output_buffer_available == 0)
++			delay = 125000 / MIDI_MILLI_BYTES_PER_JIFFY;
++		printd(instance, "Queue more work after %u jiffies", delay);
++		mod_delayed_work(pimidi_global.work_queue, &midi_port->out_handler, delay);
++	}
++
++cleanup:
++	mutex_unlock(&midi_port->out_lock);
++	printd(instance, "Done");
++}
++
++static void pimidi_midi_out_handler_0(struct work_struct *work)
++{
++	pimidi_midi_out_handler(container_of(work, struct pimidi_instance,
++					     midi_port[0].out_handler.work), 0);
++}
++
++static void pimidi_midi_out_handler_1(struct work_struct *work)
++{
++	pimidi_midi_out_handler(container_of(work, struct pimidi_instance,
++					     midi_port[1].out_handler.work), 1);
++}
++
++static void pimidi_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
++{
++	struct pimidi_instance *instance = substream->rmidi->private_data;
++
++	printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up);
++
++	if (up == 0) {
++		clear_bit(substream->stream,
++			  &instance->midi_port[substream->number].enabled_streams);
++	} else {
++		set_bit(substream->stream,
++			&instance->midi_port[substream->number].enabled_streams);
++		if (!delayed_work_pending(&instance->midi_port[substream->number].out_handler)) {
++			printd(instance, "Queueing work");
++			queue_delayed_work(pimidi_global.work_queue,
++					   &instance->midi_port[substream->number].out_handler, 0);
++		}
++	}
++}
++
++static void pimidi_midi_output_drain(struct snd_rawmidi_substream *substream)
++{
++	struct pimidi_instance *instance = substream->rmidi->private_data;
++
++	printd(instance, "(%d, %d)", substream->stream, substream->number);
++
++	printd(instance, "Begin draining!");
++
++	queue_delayed_work(pimidi_global.work_queue,
++			   &instance->midi_port[substream->number].out_handler, 0);
++
++	unsigned long deadline = jiffies + 5 * HZ;
++
++	do {
++		printd(instance, "Before flush");
++		while (delayed_work_pending(&instance->midi_port[substream->number].out_handler))
++			flush_delayed_work(&instance->midi_port[substream->number].out_handler);
++		printd(instance, "Flushed");
++	} while (!snd_rawmidi_transmit_empty(substream) && time_before(jiffies, deadline));
++
++	printd(instance, "Done!");
++}
++
++static int pimidi_midi_output_close(struct snd_rawmidi_substream *substream)
++{
++	struct pimidi_instance *instance = substream->rmidi->private_data;
++	struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number];
++
++	mutex_lock(&midi_port->out_lock);
++	clear_bit(substream->stream, &midi_port->enabled_streams);
++	mutex_unlock(&midi_port->out_lock);
++	return 0;
++}
++
++static int pimidi_midi_input_close(struct snd_rawmidi_substream *substream)
++{
++	struct pimidi_instance *instance = substream->rmidi->private_data;
++	struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number];
++
++	mutex_lock(&midi_port->in_lock);
++	clear_bit(substream->stream, &midi_port->enabled_streams);
++	mutex_unlock(&midi_port->in_lock);
++	return 0;
++}
++
++static void pimidi_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
++{
++	struct pimidi_instance *instance = substream->rmidi->private_data;
++
++	printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up);
++
++	if (up == 0) {
++		clear_bit(substream->stream,
++			  &instance->midi_port[substream->number].enabled_streams);
++		cancel_work_sync(&instance->midi_port[substream->number].in_handler);
++	} else {
++		set_bit(substream->stream,
++			&instance->midi_port[substream->number].enabled_streams);
++		if (!instance->stopping)
++			queue_work(pimidi_global.work_queue,
++				   &instance->midi_port[substream->number].in_handler);
++	}
++}
++
++static void pimidi_get_port_info(struct snd_rawmidi *rmidi, int number,
++				 struct snd_seq_port_info *seq_port_info)
++{
++	printd_g("%p, %d, %p", rmidi, number, seq_port_info);
++	seq_port_info->type =
++		SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
++		SNDRV_SEQ_PORT_TYPE_HARDWARE |
++		SNDRV_SEQ_PORT_TYPE_PORT;
++	strscpy(seq_port_info->name, number == 0 ? "a" : "b",
++		sizeof(seq_port_info->name));
++	seq_port_info->midi_voices = 0;
++}
++
++static const struct snd_rawmidi_global_ops pimidi_midi_ops = {
++	.get_port_info = pimidi_get_port_info,
++};
++
++static int pimidi_midi_open(struct snd_rawmidi_substream *substream)
++{
++	printd_g("(%p) stream=%d number=%d", substream, substream->stream, substream->number);
++	return 0;
++}
++
++static const struct snd_rawmidi_ops pimidi_midi_output_ops = {
++	.open = pimidi_midi_open,
++	.close = pimidi_midi_output_close,
++	.trigger = pimidi_midi_output_trigger,
++	.drain = pimidi_midi_output_drain,
++};
++
++static const struct snd_rawmidi_ops pimidi_midi_input_ops = {
++	.open = pimidi_midi_open,
++	.close = pimidi_midi_input_close,
++	.trigger = pimidi_midi_input_trigger,
++};
++
++static int pimidi_register(struct pimidi_instance *instance)
++{
++	int err = 0;
++
++	mutex_lock(&pimidi_global.lock);
++	printd(instance, "Registering...");
++	if (!pimidi_global.reset_gpio) {
++		printd_g("Getting reset pin.");
++		pimidi_global.reset_gpio = gpiod_get(&instance->i2c_client->dev, "reset",
++						     GPIOD_OUT_LOW);
++		if (IS_ERR(pimidi_global.reset_gpio)) {
++			err = PTR_ERR(pimidi_global.reset_gpio);
++			printe_g("gpiod_get failed: %d", err);
++			pimidi_global.reset_gpio = NULL;
++			mutex_unlock(&pimidi_global.lock);
++			return err;
++		}
++	}
++	list_add_tail(&instance->list, &pimidi_global.devices);
++	mutex_unlock(&pimidi_global.lock);
++	return err;
++}
++
++static void pimidi_unregister(struct pimidi_instance *instance)
++{
++	mutex_lock(&pimidi_global.lock);
++	printd(instance, "Unregistering...");
++	list_del(&instance->list);
++	if (list_empty(&pimidi_global.devices)) {
++		printd_g("Releasing reset pin");
++		gpiod_put(pimidi_global.reset_gpio);
++		pimidi_global.reset_gpio = NULL;
++	}
++	mutex_unlock(&pimidi_global.lock);
++}
++
++static void pimidi_perform_reset(void)
++{
++	mutex_lock(&pimidi_global.lock);
++
++	printd_g("Performing reset.");
++
++	struct list_head *p;
++
++	list_for_each(p, &pimidi_global.devices) {
++		struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++		printd(instance, "Pausing...");
++		instance->stopping = true;
++		disable_irq(instance->i2c_client->irq);
++		cancel_work(&instance->drdy_handler);
++
++		int i;
++
++		for (i = 0; i < PIMIDI_PORTS; ++i) {
++			cancel_work(&instance->midi_port[i].in_handler);
++			cancel_delayed_work(&instance->midi_port[i].out_handler);
++		}
++
++		drain_workqueue(pimidi_global.work_queue);
++	}
++
++	printd_g("Reset = low");
++	gpiod_set_value(pimidi_global.reset_gpio, 1);
++
++	list_for_each(p, &pimidi_global.devices) {
++		struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++		if (gpiod_is_active_low(instance->data_ready_gpio))
++			gpiod_toggle_active_low(instance->data_ready_gpio);
++		gpiod_direction_output(instance->data_ready_gpio, 1);
++		printd(instance, "DRDY high");
++	}
++
++	usleep_range(1000, 5000);
++	printd_g("Reset = high");
++	gpiod_set_value(pimidi_global.reset_gpio, 0);
++	msleep(30);
++
++	int i;
++
++	for (i = 0; i < PIMIDI_MAX_DEVICES; ++i) {
++		usleep_range(1000, 3000);
++		list_for_each(p, &pimidi_global.devices) {
++			struct pimidi_instance *instance = list_entry(p, struct pimidi_instance,
++								      list);
++
++			if (instance->d < i)
++				continue;
++			printd(instance, "DRDY -> %d", !gpiod_get_value(instance->data_ready_gpio));
++			gpiod_set_value(instance->data_ready_gpio,
++					!gpiod_get_value(instance->data_ready_gpio));
++		}
++	}
++	usleep_range(16000, 20000);
++
++	list_for_each(p, &pimidi_global.devices) {
++		struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++		if (!gpiod_is_active_low(instance->data_ready_gpio))
++			gpiod_toggle_active_low(instance->data_ready_gpio);
++
++		printd(instance, "DRDY input");
++		gpiod_direction_input(instance->data_ready_gpio);
++
++		printd(instance, "Resume...");
++		instance->stopping = false;
++		enable_irq(instance->i2c_client->irq);
++	}
++
++	printd_g("Reset done.");
++	usleep_range(16000, 20000);
++
++	mutex_unlock(&pimidi_global.lock);
++}
++
++static int pimidi_read_version(struct pimidi_version_t *version, struct pimidi_instance *instance)
++{
++	memset(version, 0, sizeof(*version));
++
++	const char cmd[4] = { 0xb2, 0x01, 0x01, 0x95 };
++
++	char result[9];
++
++	memset(result, 0, sizeof(result));
++
++	DECLARE_COMPLETION_ONSTACK(done);
++
++	mutex_lock(&instance->comm_lock);
++	int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd));
++
++	if (err < 0) {
++		mutex_unlock(&instance->comm_lock);
++		return err;
++	}
++	instance->rx_buf = result;
++	instance->rx_len = sizeof(result);
++	instance->rx_completion = &done;
++	mutex_unlock(&instance->comm_lock);
++
++	printd(instance, "Waiting for drdy");
++	wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u));
++	printd(instance, "Done waiting");
++
++	if (!completion_done(&done)) {
++		mutex_lock(&instance->comm_lock);
++		instance->rx_buf = NULL;
++		instance->rx_len = 0;
++		instance->rx_status = -ETIMEDOUT;
++		instance->rx_completion = NULL;
++		mutex_unlock(&instance->comm_lock);
++		return -ETIMEDOUT;
++	}
++
++	if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, sizeof(result),
++						       CRC8_INIT_VALUE))
++		return -EIO;
++
++	const char expected[4] = { 0xb7, 0x81, 0x01, 0x00 };
++
++	if (memcmp(result, expected, sizeof(expected)) != 0)
++		return -EPROTO;
++
++	u32 v = ntohl(*(uint32_t *)(result + 4));
++
++	version->hwrev = v >> 24;
++	version->major = (v & 0x00ff0000) >> 16;
++	version->minor = (v & 0x0000ff00) >> 8;
++	version->build = v & 0x000000ff;
++
++	return 0;
++}
++
++static int pimidi_read_serial(char serial[11], struct pimidi_instance *instance)
++{
++	memset(serial, 0, sizeof(char[11]));
++
++	const char cmd[4] = { 0xb2, 0x03, 0x04, 0x97 };
++
++	char result[PIMIDI_MAX_PACKET_SIZE];
++
++	memset(result, 0, sizeof(result));
++
++	DECLARE_COMPLETION_ONSTACK(done);
++
++	mutex_lock(&instance->comm_lock);
++	int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd));
++
++	if (err < 0) {
++		mutex_unlock(&instance->comm_lock);
++		return err;
++	}
++	instance->rx_buf = result;
++	instance->rx_len = sizeof(result);
++	instance->rx_completion = &done;
++	mutex_unlock(&instance->comm_lock);
++
++	printd(instance, "Waiting for drdy");
++	wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u));
++	printd(instance, "Done waiting");
++
++	if (!completion_done(&done)) {
++		mutex_lock(&instance->comm_lock);
++		instance->rx_buf = NULL;
++		instance->rx_len = 0;
++		instance->rx_status = -ETIMEDOUT;
++		instance->rx_completion = NULL;
++		mutex_unlock(&instance->comm_lock);
++		printe(instance, "Timed out");
++		return -ETIMEDOUT;
++	}
++
++	if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result,
++						       (result[0] & 0x0f) + 2, CRC8_INIT_VALUE))
++		return -EIO;
++
++	const char expected[4] = { 0xbd, 0x83, 0x04, 0x0a };
++
++	if (memcmp(result, expected, sizeof(expected)) != 0) {
++		printe(instance, "Unexpected response: %02x %02x %02x %02x", result[0], result[1],
++		       result[2], result[3]);
++		return -EPROTO;
++	}
++
++	memcpy(serial, result + 4, 10);
++
++	if (strspn(serial, "\xff") == 10)
++		strscpy(serial, "(unset)", 8);
++
++	return 0;
++}
++
++static void pimidi_handle_midi_data(struct pimidi_instance *instance, int port, const uint8_t *data,
++				    unsigned int n)
++{
++	printd(instance, "Handling MIDI data for port %d (%u bytes)", port, n);
++	if (n == 0)
++		return;
++
++	struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++	kfifo_in(&midi_port->in_fifo, data, n);
++
++	if (!instance->stopping)
++		queue_work(pimidi_global.work_queue, &midi_port->in_handler);
++
++	printd(instance, "Done");
++}
++
++static void pimidi_drdy_continue(struct pimidi_instance *instance)
++{
++	if (instance->stopping) {
++		printd(instance, "Refusing to queue work / enable IRQ due to stopping.");
++		return;
++	}
++
++	if (gpiod_get_value(instance->data_ready_gpio)) {
++		printd_rl(instance, "Queue work due to DRDY line still low");
++		queue_work(pimidi_global.work_queue, &instance->drdy_handler);
++	} else {
++		printd_rl(instance, "Enabling irq for more data");
++		enable_irq(gpiod_to_irq(instance->data_ready_gpio));
++	}
++}
++
++static void pimidi_drdy_handler(struct work_struct *work)
++{
++	struct pimidi_instance *instance = container_of(work, struct pimidi_instance, drdy_handler);
++
++	printd(instance, "(%p)", work);
++
++	mutex_lock(&instance->comm_lock);
++	if (!instance->rx_completion) {
++		u8 data[PIMIDI_MAX_PACKET_SIZE];
++		int n = i2c_master_recv(instance->i2c_client, data, 3);
++
++		if (n < 0) {
++			printe(instance, "Error reading from device: %d", n);
++			mutex_unlock(&instance->comm_lock);
++			pimidi_drdy_continue(instance);
++			return;
++		}
++
++		if (data[0] == 0xfe) {
++			printe_rl(instance, "Invalid packet 0x%02x 0x%02x 0x%02x", data[0], data[1],
++				  data[2]);
++			mutex_unlock(&instance->comm_lock);
++			pimidi_drdy_continue(instance);
++			return;
++		}
++
++		int len = (data[0] & 0x0f) + 2;
++
++		if (len > n) {
++			printd(instance, "Need %d more bytes", len - n);
++			int err = i2c_master_recv(instance->i2c_client, data + n, len - n);
++
++			if (err < 0) {
++				printe(instance, "Error reading remainder from device: %d", err);
++				mutex_unlock(&instance->comm_lock);
++				pimidi_drdy_continue(instance);
++				return;
++#ifdef PIMIDI_DEBUG
++			} else {
++				pr_debug("Recv_2:");
++				int i;
++
++				for (i = n; i < len; ++i)
++					pr_cont(" %02x", data[i]);
++				pr_cont("\n");
++#endif
++			}
++		}
++
++		if (CRC8_GOOD_VALUE(pimidi_crc8_table) == crc8(pimidi_crc8_table, data, len,
++							       CRC8_INIT_VALUE)) {
++			switch (data[0] & 0xf0) {
++			case 0x00:
++				pimidi_handle_midi_data(instance, 0, data + 1, len - 2);
++				break;
++			case 0x10:
++				pimidi_handle_midi_data(instance, 1, data + 1, len - 2);
++				break;
++			default:
++				printd(instance, "Unhandled command %02x", data[0]);
++				break;
++			}
++		} else {
++			printe(instance, "I2C rx corruption detected.");
++			pr_info("Packet [%d]:", len);
++			int i;
++
++			for (i = 0; i < len; ++i)
++				pr_cont(" %02x", data[i]);
++			pr_cont("\n");
++		}
++
++		mutex_unlock(&instance->comm_lock);
++	} else {
++		printd(instance, "Completing drdy");
++		instance->rx_status = i2c_master_recv(instance->i2c_client, instance->rx_buf, 3);
++		printd(instance, "Recv_1 %02x %02x %02x", instance->rx_buf[0], instance->rx_buf[1],
++		       instance->rx_buf[2]);
++		if (instance->rx_len > 3 && instance->rx_status == 3) {
++			instance->rx_status = i2c_master_recv(instance->i2c_client,
++							      instance->rx_buf + 3,
++							      instance->rx_len - 3);
++			if (instance->rx_status >= 0)
++				instance->rx_status += 3;
++#ifdef PIMIDI_DEBUG
++			pr_debug("Recv_2:");
++			int i;
++
++			for (i = 3; i < instance->rx_len; ++i)
++				pr_cont(" %02x", instance->rx_buf[i]);
++			pr_cont("\n");
++#endif
++		}
++		struct completion *done = instance->rx_completion;
++
++		instance->rx_buf = NULL;
++		instance->rx_len = 0;
++		instance->rx_completion = NULL;
++		complete_all(done);
++		mutex_unlock(&instance->comm_lock);
++	}
++
++	pimidi_drdy_continue(instance);
++}
++
++static irqreturn_t pimidi_drdy_interrupt_handler(int irq, void *dev_id)
++{
++	struct pimidi_instance *instance = (struct pimidi_instance *)dev_id;
++
++	if (instance->stopping) {
++		printd(instance, "DRDY interrupt, but stopping, ignoring...");
++		return IRQ_HANDLED;
++	}
++
++	printd(instance, "DRDY interrupt, masking");
++	disable_irq_nosync(irq);
++
++	printd(instance, "Queue work due to DRDY interrupt");
++	queue_work(pimidi_global.work_queue, &instance->drdy_handler);
++
++	return IRQ_HANDLED;
++}
++
++static void pimidi_proc_stat_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++	const unsigned int *d = entry->private_data;
++
++	snd_iprintf(buffer, "%u\n", *d);
++}
++
++static void pimidi_proc_serial_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++	struct pimidi_instance *instance = entry->private_data;
++
++	snd_iprintf(buffer, "%s\n", instance->serial);
++}
++
++static void pimidi_proc_version_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++	struct pimidi_instance *instance = entry->private_data;
++
++	snd_iprintf(buffer, "%u.%u.%u\n", instance->version.major, instance->version.minor,
++		    instance->version.build);
++}
++
++static void pimidi_proc_hwrev_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++	struct pimidi_instance *instance = entry->private_data;
++
++	snd_iprintf(buffer, "%u\n", instance->version.hwrev);
++}
++
++static int pimidi_i2c_probe(struct i2c_client *client)
++{
++	struct snd_card *card = NULL;
++	int err, d, i;
++
++	d = client->addr - 0x20;
++
++	if (d < 0 || d >= 8) {
++		printe_g("Unexpected device address: %d", client->addr);
++		err = -EINVAL;
++		goto finalize;
++	}
++
++	err = snd_card_new(&client->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE,
++			   sizeof(struct pimidi_instance), &card);
++
++	if (err) {
++		printe_g("snd_card_new failed: %d", err);
++		return err;
++	}
++
++	struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data;
++
++	instance->i2c_client = client;
++	instance->d = d;
++
++	struct snd_rawmidi *rawmidi;
++
++	err = snd_rawmidi_new(card, card->shortname, 0, 2, 2, &rawmidi);
++	if (err < 0) {
++		printe(instance, "snd_rawmidi_new failed: %d", err);
++		goto finalize;
++	}
++
++	instance->rawmidi = rawmidi;
++	strscpy(rawmidi->name, "pimidi", sizeof(rawmidi->name));
++
++	rawmidi->info_flags =
++		SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
++	rawmidi->private_data = instance;
++	rawmidi->ops = &pimidi_midi_ops;
++
++	snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &pimidi_midi_output_ops);
++	snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pimidi_midi_input_ops);
++
++	instance->data_ready_gpio = devm_gpiod_get(&client->dev, "data-ready", GPIOD_OUT_HIGH);
++	if (IS_ERR(instance->data_ready_gpio)) {
++		err = PTR_ERR(instance->data_ready_gpio);
++		printe(instance, "devm_gpiod_get failed: %d", err);
++		goto finalize;
++	}
++
++	err = pimidi_register(instance);
++	if (err < 0) {
++		printe(instance, "pimidi_register failed: %d", err);
++		goto finalize;
++	}
++
++	pimidi_perform_reset();
++
++	INIT_WORK(&instance->drdy_handler, pimidi_drdy_handler);
++	mutex_init(&instance->comm_lock);
++
++	err = devm_request_irq(&client->dev, client->irq, pimidi_drdy_interrupt_handler,
++			       IRQF_SHARED | IRQF_TRIGGER_LOW, "data_ready_int", instance);
++
++	if (err != 0) {
++		printe(instance, "data_available IRQ request failed! %d", err);
++		goto finalize;
++	}
++
++	err = pimidi_read_version(&instance->version, instance);
++	if (err < 0) {
++		printe(instance, "pimidi_read_version failed: %d", err);
++		goto finalize;
++	}
++
++	err = pimidi_read_serial(instance->serial, instance);
++	if (err < 0) {
++		printe(instance, "pimidi_read_serial failed: %d", err);
++		goto finalize;
++	} else if (instance->serial[0] != 'P' || instance->serial[1] != 'M' ||
++		   strlen(instance->serial) != 10) {
++		printe(instance, "Unexpected serial number: %s", instance->serial);
++		err = -EIO;
++		goto finalize;
++	}
++
++	printi(instance, "pimidi%d hw:%d version %u.%u.%u-%u, serial %s",
++	       d,
++	       card->number,
++	       instance->version.major,
++	       instance->version.minor,
++	       instance->version.build,
++	       instance->version.hwrev,
++	       instance->serial
++	       );
++
++	strscpy(card->driver, "snd-pimidi", sizeof(card->driver));
++	snprintf(card->shortname, sizeof(card->shortname), "pimidi%d", d);
++	snprintf(card->longname, sizeof(card->longname), "pimidi%d %s", d, instance->serial);
++	snprintf(card->id, sizeof(card->id), "pimidi%d", d);
++
++	snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 0)->name,
++		 10u, "pimidi%d-a", d);
++	snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT,  0)->name,
++		 10u, "pimidi%d-a", d);
++	snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 1)->name,
++		 10u, "pimidi%d-b", d);
++	snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT,  1)->name,
++		 10u, "pimidi%d-b", d);
++
++	err = snd_card_ro_proc_new(card, "a-tx", &instance->midi_port[0].tx_cnt,
++				   pimidi_proc_stat_show);
++	err = snd_card_ro_proc_new(card, "a-rx", &instance->midi_port[0].rx_cnt,
++				   pimidi_proc_stat_show);
++	err = snd_card_ro_proc_new(card, "b-tx", &instance->midi_port[1].tx_cnt,
++				   pimidi_proc_stat_show);
++	err = snd_card_ro_proc_new(card, "b-rx", &instance->midi_port[1].rx_cnt,
++				   pimidi_proc_stat_show);
++	err = snd_card_ro_proc_new(card, "serial", instance, pimidi_proc_serial_show);
++	err = snd_card_ro_proc_new(card, "version", instance, pimidi_proc_version_show);
++	err = snd_card_ro_proc_new(card, "hwrev", instance, pimidi_proc_hwrev_show);
++	if (err < 0) {
++		printe(instance, "snd_card_ro_proc_new failed: %d", err);
++		goto finalize;
++	}
++
++	err = snd_card_register(card);
++	if (err < 0) {
++		printe(instance, "snd_card_register failed: %d", err);
++		goto finalize;
++	}
++
++finalize:
++	if (err) {
++		instance->stopping = true;
++		cancel_work_sync(&instance->drdy_handler);
++		mutex_destroy(&instance->comm_lock);
++		pimidi_unregister(instance);
++		snd_card_free(card);
++		return err;
++	}
++
++	for (i = 0; i < PIMIDI_PORTS; ++i) {
++		struct pimidi_midi_port *port = &instance->midi_port[i];
++
++		mutex_init(&port->in_lock);
++		mutex_init(&port->out_lock);
++		INIT_WORK(&port->in_handler,
++			  i == 0 ? pimidi_midi_in_handler_0 : pimidi_midi_in_handler_1);
++		INIT_DELAYED_WORK(&port->out_handler,
++				  i == 0 ? pimidi_midi_out_handler_0 : pimidi_midi_out_handler_1);
++		INIT_KFIFO(port->in_fifo);
++		port->last_output_at = jiffies;
++	}
++
++	i2c_set_clientdata(client, card);
++	return 0;
++}
++
++static void pimidi_i2c_remove(struct i2c_client *client)
++{
++	printd_g("(%p)", client);
++
++	int i;
++	struct snd_card *card = i2c_get_clientdata(client);
++
++	if (card) {
++		printi_g("Unloading hw:%d %s", card->number, card->longname);
++		struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data;
++
++		instance->stopping = true;
++		i2c_set_clientdata(client, NULL);
++		devm_free_irq(&client->dev, client->irq, instance);
++		cancel_work_sync(&instance->drdy_handler);
++
++		for (i = 0; i < PIMIDI_PORTS; ++i) {
++			cancel_work_sync(&instance->midi_port[i].in_handler);
++			cancel_delayed_work_sync(&instance->midi_port[i].out_handler);
++			mutex_destroy(&instance->midi_port[i].out_lock);
++			mutex_destroy(&instance->midi_port[i].in_lock);
++			kfifo_free(&instance->midi_port[i].in_fifo);
++		}
++
++		mutex_destroy(&instance->comm_lock);
++		pimidi_unregister(instance);
++		snd_card_free(card);
++	}
++}
++
++static const struct i2c_device_id pimidi_i2c_ids[] = {
++	{ "pimidi", 0 },
++	{}
++};
++MODULE_DEVICE_TABLE(i2c, pimidi_i2c_ids);
++
++static const struct of_device_id pimidi_i2c_dt_ids[] = {
++	{ .compatible = "blokaslabs,pimidi", },
++	{}
++};
++MODULE_DEVICE_TABLE(of, pimidi_i2c_dt_ids);
++
++static struct i2c_driver pimidi_i2c_driver = {
++	.driver = {
++		.name = "pimidi",
++		.owner = THIS_MODULE,
++		.of_match_table = of_match_ptr(pimidi_i2c_dt_ids),
++	},
++	.probe = pimidi_i2c_probe,
++	.remove = pimidi_i2c_remove,
++	.id_table = pimidi_i2c_ids,
++};
++
++int pimidi_module_init(void)
++{
++	int err = 0;
++
++	mutex_init(&pimidi_global.lock);
++
++	INIT_LIST_HEAD(&pimidi_global.devices);
++
++	pimidi_global.work_queue = create_singlethread_workqueue("pimidi");
++	if (!pimidi_global.work_queue) {
++		err = -ENOMEM;
++		goto cleanup;
++	}
++
++	err = i2c_add_driver(&pimidi_i2c_driver);
++	if (err < 0)
++		goto cleanup;
++
++	crc8_populate_msb(pimidi_crc8_table, PIMIDI_CRC8_POLYNOMIAL);
++
++	return 0;
++
++cleanup:
++	mutex_destroy(&pimidi_global.lock);
++	return err;
++}
++
++void pimidi_module_exit(void)
++{
++	i2c_del_driver(&pimidi_i2c_driver);
++	mutex_lock(&pimidi_global.lock);
++	if (pimidi_global.reset_gpio) {
++		gpiod_put(pimidi_global.reset_gpio);
++		pimidi_global.reset_gpio = NULL;
++	}
++	mutex_unlock(&pimidi_global.lock);
++
++	destroy_workqueue(pimidi_global.work_queue);
++	pimidi_global.work_queue = NULL;
++
++	mutex_destroy(&pimidi_global.lock);
++}
++
++module_init(pimidi_module_init);
++module_exit(pimidi_module_exit);
++
++MODULE_AUTHOR("Giedrius Trainavi\xc4\x8dius <[email protected]>");
++MODULE_DESCRIPTION("MIDI driver for Blokas Pimidi, https://blokas.io/");
++MODULE_LICENSE("GPL");
++
++/* vim: set ts=8 sw=8 noexpandtab: */

+ 100 - 0
target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch

@@ -0,0 +1,100 @@
+From 75ab92b077602734458f0a77e19a3599be29b93b Mon Sep 17 00:00:00 2001
+From: Giedrius <[email protected]>
+Date: Thu, 21 Nov 2024 08:05:49 +0000
+Subject: [PATCH] Adding pimidi-overlay.dts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Giedrius Trainavičius <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  8 +++
+ arch/arm/boot/dts/overlays/pimidi-overlay.dts | 54 +++++++++++++++++++
+ 3 files changed, 63 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pimidi-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -203,6 +203,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	pifi-dac-zero.dtbo \
+ 	pifi-mini-210.dtbo \
+ 	piglow.dtbo \
++	pimidi.dtbo \
+ 	pineboards-hat-ai.dtbo \
+ 	pineboards-hatdrive-poe-plus.dtbo \
+ 	piscreen.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3701,6 +3701,14 @@ Load:   dtoverlay=piglow
+ Params: <None>
+ 
+ 
++Name:   pimidi
++Info:   Configures the Blokas Labs Pimidi card
++Load:   dtoverlay=pimidi,<param>=<val>
++Params: sel                     The position used for the sel rotary switch.
++                                Each unit in the stack must be set on a unique
++                                position. If param is omitted, sel=0 is assumed.
++
++
+ Name:   pineboards-hat-ai
+ Info:   Pineboards Hat Ai! overlay for the Google Coral Edge TPU
+ Load:   dtoverlay=pineboards-hat-ai
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts
+@@ -0,0 +1,54 @@
++/*
++ * Pimidi Linux kernel module.
++ * Copyright (C) 2017-2024  Vilniaus Blokas UAB, https://blokas.io/
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the
++ * License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/ {
++	compatible = "brcm,bcm2835";
++
++	fragment@0 {
++		target = <&i2c_arm>;
++		__overlay__ {
++			status = "okay";
++			clock-frequency=<1000000>;
++
++			pimidi_ctrl: pimidi_ctrl@20 {
++				compatible = "blokaslabs,pimidi";
++
++				reg = <0x20>;
++				status = "okay";
++
++				interrupt-parent = <&gpio>;
++				interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
++				interrupt-names = "data_ready";
++				interrupt-controller;
++				#interrupt-cells = <2>;
++
++				data-ready-gpios = <&gpio 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
++				reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
++			};
++		};
++	};
++
++	__overrides__ {
++		sel = <&pimidi_ctrl>,"reg:0{0=0x20,1=0x21,2=0x22,3=0x23}",
++			<&pimidi_ctrl>,"data-ready-gpios:4{0=23,1=5,2=6,3=27}",
++			<&pimidi_ctrl>,"interrupts:0{0=23,1=5,2=6,3=27}";
++	};
++};

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch → target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch

@@ -1,7 +1,7 @@
-From fc5ed9d9bf0411523220bab60304da6d23257a64 Mon Sep 17 00:00:00 2001
+From a1e4b72997dc3ef423b6f510bfead470475750d4 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <[email protected]>
 Date: Thu, 1 Nov 2018 17:31:37 +0000
-Subject: [PATCH 0297/1085] staging: vchiq_arm: Add 36-bit address support
+Subject: [PATCH] staging: vchiq_arm: Add 36-bit address support
 
 Conditional on a new compatible string, change the pagelist encoding
 such that the top 24 bits are the pfn, leaving 8 bits for run length

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch → target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch

@@ -1,7 +1,7 @@
-From d4712f611e6d60dd9cf09df581f5df6fad6a2207 Mon Sep 17 00:00:00 2001
+From 1129091b2d95273d930acf2926a569b90512a248 Mon Sep 17 00:00:00 2001
 From: Phil Elwell <[email protected]>
 Date: Tue, 21 Jul 2020 17:34:09 +0100
-Subject: [PATCH 0298/1085] staging: vchiq_arm: children inherit DMA config
+Subject: [PATCH] staging: vchiq_arm: children inherit DMA config
 
 Although it is no longer necessary for vchiq's children to have a
 different DMA configuration to the parent, they do still need to

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch → target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch

@@ -1,7 +1,7 @@
-From 9f328c347fc9a5495b8383aa2bae1d3bc242a2ab Mon Sep 17 00:00:00 2001
+From 2d26a598ceceaea8a6837146c741eb742bbd4baa Mon Sep 17 00:00:00 2001
 From: detule <[email protected]>
 Date: Tue, 2 Oct 2018 04:10:08 -0400
-Subject: [PATCH 0299/1085] staging: vchiq_arm: Usa a DMA pool for small bulks
+Subject: [PATCH] staging: vchiq_arm: Usa a DMA pool for small bulks
 
 During a bulk transfer we request a DMA allocation to hold the
 scatter-gather list.  Most of the time, this allocation is small

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch → target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch

@@ -1,7 +1,7 @@
-From 79f24f7454a416fed9106c75ea9b3be480465dda Mon Sep 17 00:00:00 2001
+From 5b29221e96d1ba60a78d5c804a20fa35a6d0517a Mon Sep 17 00:00:00 2001
 From: Phil Elwell <[email protected]>
 Date: Fri, 29 Apr 2022 09:19:10 +0100
-Subject: [PATCH 0365/1085] staging: vchiq_arm: Add log_level module params
+Subject: [PATCH] staging: vchiq_arm: Add log_level module params
 
 Add module parameters to control the logging levels for the various
 vchiq logging categories.

+ 25 - 0
target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch

@@ -0,0 +1,25 @@
+From 8691544f688bd3ae9b6db0845a75ce230fc9e90f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Thu, 21 Nov 2024 15:54:58 +0000
+Subject: [PATCH] media: i2c: imx477: Fix link frequency menu
+
+"media: i2c: imx477: Add options for slightly modifying the link freq"
+created a link frequency menu with 2 items in instead of one.
+Correct this.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/media/i2c/imx477.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2051,7 +2051,7 @@ static int imx477_init_controls(struct i
+ 	/* LINK_FREQ is also read only */
+ 	imx477->link_freq =
+ 		v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops,
+-				       V4L2_CID_LINK_FREQ, 1, 0,
++				       V4L2_CID_LINK_FREQ, 0, 0,
+ 				       &link_freqs[imx477->link_freq_idx]);
+ 	if (imx477->link_freq)
+ 		imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;

+ 1 - 1
target/linux/bcm27xx/patches-6.6/950-1357-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch → target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch

@@ -14,7 +14,7 @@ Signed-off-by: Phil Elwell <[email protected]>
 
 --- a/include/linux/pio_rp1.h
 +++ b/include/linux/pio_rp1.h
-@@ -318,7 +318,7 @@ static inline int pio_sm_unclaim(struct
+@@ -320,7 +320,7 @@ static inline int pio_sm_unclaim(struct
  	if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES))
  		return -EINVAL;
  

+ 0 - 0
target/linux/bcm27xx/patches-6.6/950-1360-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch → target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch


+ 147 - 0
target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch

@@ -0,0 +1,147 @@
+From 008c93b47b9b965368eb5bbfbef60b816931e0ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 20 Nov 2024 13:58:08 +0000
+Subject: [PATCH] drm: vc4: dsi: Handle the different command FIFO widths
+
+DSI0 and DSI1 have different widths for the command FIFO (24bit
+vs 32bit), but the driver was assuming the 32bit width of DSI1
+in all cases.
+DSI0 also wants the data packed as 24bit big endian, so the
+formatting code needs updating.
+
+Handle the difference via the variant structure.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_dsi.c | 64 ++++++++++++++++++++++++-----------
+ 1 file changed, 44 insertions(+), 20 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_dsi.c
++++ b/drivers/gpu/drm/vc4/vc4_dsi.c
+@@ -44,7 +44,6 @@
+ 
+ #define DSI_CMD_FIFO_DEPTH  16
+ #define DSI_PIX_FIFO_DEPTH 256
+-#define DSI_PIX_FIFO_WIDTH   4
+ 
+ #define DSI0_CTRL		0x00
+ 
+@@ -170,11 +169,15 @@
+ #define DSI1_DISP1_CTRL		0x2c
+ /* Format of the data written to TXPKT_PIX_FIFO. */
+ # define DSI_DISP1_PFORMAT_MASK		VC4_MASK(2, 1)
+-# define DSI_DISP1_PFORMAT_SHIFT	1
+-# define DSI_DISP1_PFORMAT_16BIT	0
+-# define DSI_DISP1_PFORMAT_24BIT	1
+-# define DSI_DISP1_PFORMAT_32BIT_LE	2
+-# define DSI_DISP1_PFORMAT_32BIT_BE	3
++# define DSI1_DISP1_PFORMAT_SHIFT	1
++# define DSI0_DISP1_PFORMAT_16BIT	0
++# define DSI0_DISP1_PFORMAT_16BIT_ADJ	1
++# define DSI0_DISP1_PFORMAT_24BIT	2
++# define DSI0_DISP1_PFORMAT_32BIT_LE	3 /* NB Invalid, but required for macros to work */
++# define DSI1_DISP1_PFORMAT_16BIT	0
++# define DSI1_DISP1_PFORMAT_24BIT	1
++# define DSI1_DISP1_PFORMAT_32BIT_LE	2
++# define DSI1_DISP1_PFORMAT_32BIT_BE	3
+ 
+ /* DISP1 is always command mode. */
+ # define DSI_DISP1_ENABLE		BIT(0)
+@@ -553,6 +556,7 @@ struct vc4_dsi_variant {
+ 	unsigned int port;
+ 
+ 	bool broken_axi_workaround;
++	unsigned int cmd_fifo_width;
+ 
+ 	const char *debugfs_name;
+ 	const struct debugfs_reg32 *regs;
+@@ -1151,10 +1155,16 @@ static void vc4_dsi_bridge_pre_enable(st
+ 	/* Set up DISP1 for transferring long command payloads through
+ 	 * the pixfifo.
+ 	 */
+-	DSI_PORT_WRITE(DISP1_CTRL,
+-		       VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE,
+-				     DSI_DISP1_PFORMAT) |
+-		       DSI_DISP1_ENABLE);
++	if (dsi->variant->cmd_fifo_width == 4)
++		DSI_PORT_WRITE(DISP1_CTRL,
++			       VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_32BIT_LE),
++					     DSI_DISP1_PFORMAT) |
++			       DSI_DISP1_ENABLE);
++	else
++		DSI_PORT_WRITE(DISP1_CTRL,
++			       VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_24BIT),
++					     DSI_DISP1_PFORMAT) |
++			       DSI_DISP1_ENABLE);
+ 
+ 	/* Bring AFE out of reset. */
+ 	DSI_PORT_WRITE(PHY_AFEC0,
+@@ -1235,9 +1245,9 @@ static ssize_t vc4_dsi_transfer(struct v
+ 			pix_fifo_len = 0;
+ 		} else {
+ 			cmd_fifo_len = (packet.payload_length %
+-					DSI_PIX_FIFO_WIDTH);
++					dsi->variant->cmd_fifo_width);
+ 			pix_fifo_len = ((packet.payload_length - cmd_fifo_len) /
+-					DSI_PIX_FIFO_WIDTH);
++					dsi->variant->cmd_fifo_width);
+ 		}
+ 
+ 		WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH);
+@@ -1255,14 +1265,25 @@ static ssize_t vc4_dsi_transfer(struct v
+ 
+ 	for (i = 0; i < cmd_fifo_len; i++)
+ 		DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]);
+-	for (i = 0; i < pix_fifo_len; i++) {
+-		const u8 *pix = packet.payload + cmd_fifo_len + i * 4;
++	if (dsi->variant->cmd_fifo_width == 4) {
++		for (i = 0; i < pix_fifo_len; i++) {
++			const u8 *pix = packet.payload + cmd_fifo_len + i * 4;
++
++			DSI_PORT_WRITE(TXPKT_PIX_FIFO,
++				       pix[0] |
++				       pix[1] << 8 |
++				       pix[2] << 16 |
++				       pix[3] << 24);
++		}
++	} else {
++		for (i = 0; i < pix_fifo_len; i++) {
++			const u8 *pix = packet.payload + cmd_fifo_len + i * 3;
+ 
+-		DSI_PORT_WRITE(TXPKT_PIX_FIFO,
+-			       pix[0] |
+-			       pix[1] << 8 |
+-			       pix[2] << 16 |
+-			       pix[3] << 24);
++			DSI_PORT_WRITE(TXPKT_PIX_FIFO,
++				       pix[2] |
++				       pix[1] << 8 |
++				       pix[0] << 16);
++		}
+ 	}
+ 
+ 	if (msg->flags & MIPI_DSI_MSG_USE_LPM)
+@@ -1516,6 +1537,7 @@ static const struct drm_encoder_funcs vc
+ 
+ static const struct vc4_dsi_variant bcm2711_dsi1_variant = {
+ 	.port			= 1,
++	.cmd_fifo_width		= 4,
+ 	.debugfs_name		= "dsi1_regs",
+ 	.regs			= dsi1_regs,
+ 	.nregs			= ARRAY_SIZE(dsi1_regs),
+@@ -1523,6 +1545,7 @@ static const struct vc4_dsi_variant bcm2
+ 
+ static const struct vc4_dsi_variant bcm2835_dsi0_variant = {
+ 	.port			= 0,
++	.cmd_fifo_width		= 3,
+ 	.debugfs_name		= "dsi0_regs",
+ 	.regs			= dsi0_regs,
+ 	.nregs			= ARRAY_SIZE(dsi0_regs),
+@@ -1530,6 +1553,7 @@ static const struct vc4_dsi_variant bcm2
+ 
+ static const struct vc4_dsi_variant bcm2835_dsi1_variant = {
+ 	.port			= 1,
++	.cmd_fifo_width		= 4,
+ 	.broken_axi_workaround	= true,
+ 	.debugfs_name		= "dsi1_regs",
+ 	.regs			= dsi1_regs,

+ 30 - 0
target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch

@@ -0,0 +1,30 @@
+From eafaa6015fc0ed676f6115905e7c4145d23f5b7d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Tue, 26 Nov 2024 15:53:24 +0000
+Subject: [PATCH] dts: bcm2712-rpi: For CM5IO, i2c_csi_dsi needs to be
+ CAM/DISP1
+
+Noted setting up a display on CM5IO. Add
+"dtoverlay=vc4-kms-dsi-ili7881-7inch" fails as it tries to
+find the regulator/backlight/touch on i2c_csi_dsi, which pointed
+at i2c_csi_dsi0 by default.
+
+Adding the dsi0 override updated to point at dsi0, and pointed
+the i2c at i2c_csi_dsi0, which all works.
+
+The default with i2c_csi_dsi needs to be consistent in using
+dsi1/csi1 and the corresponding i2c interface (i2c_csi_dsi1).
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
+@@ -11,4 +11,4 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f
+ 	symlink = "i2c-6";
+ };
+ 
+-i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable
++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable

+ 25 - 0
target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch

@@ -0,0 +1,25 @@
+From d128c123754e9dd03ad72c16851a1652331d6da1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 27 Nov 2024 10:24:47 +0000
+Subject: [PATCH] dts: bcm2712-rpi-cm5: Remove inaccessible USB_OC_N
+
+Although VBUS_EN on GPIO42 appears on the CM5's 100-way headers,
+USB_OC_N on GPIO43 does not. Remove the signal name to avoid further
+confusion and disappointment.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -718,7 +718,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ 		"-", // GPIO40
+ 		"-", // GPIO41
+ 		"USB_VBUS_EN", // GPIO42
+-		"USB_OC_N", // GPIO43
++		"-", // GPIO43
+ 		"RP1_STAT_LED", // GPIO44
+ 		"FAN_PWM", // GPIO45
+ 		"-", // GPIO46

+ 33 - 0
target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch

@@ -0,0 +1,33 @@
+From 77389e715039b1feac9c6261727600892cc12fdb Mon Sep 17 00:00:00 2001
+From: Michael Heimpold <[email protected]>
+Date: Fri, 29 Nov 2024 14:10:04 +0100
+Subject: [PATCH] overlays: qca7000: replace URL with textual hint
+
+The deep link into the website is not that stable, so let's
+replace it with a textual description where to find the
+product information.
+
+Signed-off-by: Michael Heimpold <[email protected]>
+---
+ arch/arm/boot/dts/overlays/qca7000-overlay.dts       | 2 +-
+ arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/qca7000-overlay.dts
++++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts
+@@ -1,5 +1,5 @@
+ // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK
+-// Visit: https://chargebyte.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details
++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details
+ 
+ /dts-v1/;
+ /plugin/;
+--- a/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts
++++ b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts
+@@ -1,5 +1,5 @@
+ // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK
+-// Visit: https://in-tech-smartcharging.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details
++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details
+ 
+ /dts-v1/;
+ /plugin/;

+ 24 - 0
target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch

@@ -0,0 +1,24 @@
+From 178f1c2747c3920723242f26ba290785d45bffae Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Mon, 11 Nov 2024 16:38:01 +0000
+Subject: [PATCH] dt-bindings: net: cdns,macb: Add compatible for Raspberry Pi
+ RP1
+
+The Raspberry Pi RP1 chip has the Cadence GEM ethernet
+controller, so add a compatible string for it.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ Documentation/devicetree/bindings/net/cdns,macb.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
+@@ -54,6 +54,7 @@ properties:
+           - cdns,np4-macb             # NP4 SoC devices
+           - microchip,sama7g5-emac    # Microchip SAMA7G5 ethernet interface
+           - microchip,sama7g5-gem     # Microchip SAMA7G5 gigabit ethernet interface
++          - raspberrypi,rp1-gem       # Raspberry Pi RP1 gigabit ethernet interface
+           - sifive,fu540-c000-gem     # SiFive FU540-C000 SoC
+           - cdns,emac                 # Generic
+           - cdns,gem                  # Generic

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch

@@ -1,8 +1,8 @@
 From f9f0024bd9bf04a58b64bae356be4c04022d23bc Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Mon, 11 Nov 2024 16:40:07 +0000
-Subject: [PATCH 1423/1482] net: macb: Add support for Raspberry Pi RP1
- ethernet controller
+Subject: [PATCH] net: macb: Add support for Raspberry Pi RP1 ethernet
+ controller
 
 The RP1 chip has the Cadence GEM block, but wants the tx_clock
 to always run at 125MHz, in the same way as sama7g5.

+ 1 - 2
target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch

@@ -1,8 +1,7 @@
 From 33c225f622d596034a9261316666089a92aa6834 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Mon, 25 Nov 2024 12:30:06 +0000
-Subject: [PATCH 1424/1482] rp1: clk: Only set PLL_SEC_RST in
- rp1_pll_divider_off
+Subject: [PATCH] rp1: clk: Only set PLL_SEC_RST in rp1_pll_divider_off
 
 Rather than clearing all the bits in rp1_pll_divider_off
 and setting PLL_SEC_RST, retain the status of all the other

+ 1 - 2
target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch

@@ -1,8 +1,7 @@
 From eb836a6a299322a8e2b9627cccd23c7a76d068ba Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Fri, 8 Nov 2024 17:36:13 +0000
-Subject: [PATCH 1425/1482] rp1: clk: Rationalise the use of the
- CLK_IS_CRITICAL flag
+Subject: [PATCH] rp1: clk: Rationalise the use of the CLK_IS_CRITICAL flag
 
 The clock setup had been copied from clk-bcm2835 which had to cope
 with the firmware having configured clocks, so there were flags

+ 4 - 4
target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch

@@ -1,7 +1,7 @@
 From 0b4af929b7125abd3a262577b380c7c81ee9b1c5 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Mon, 11 Nov 2024 15:18:14 +0000
-Subject: [PATCH 1426/1482] dt: arm64: Fixup RP1 ethernet DT configuration
+Subject: [PATCH] dt: arm64: Fixup RP1 ethernet DT configuration
 
 Configure RP1's ethernet block to do the correct thing.
 clk_eth is intended to be fixed at 125MHz, so use a new compatible,
@@ -14,7 +14,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
 
 --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
 +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -24,6 +24,7 @@
+@@ -32,6 +32,7 @@
  					  // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers
  					  <&rp1_clocks RP1_PLL_SYS>,
  					  <&rp1_clocks RP1_PLL_SYS_SEC>,
@@ -22,7 +22,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
  					  <&rp1_clocks RP1_PLL_AUDIO>,
  					  <&rp1_clocks RP1_PLL_AUDIO_SEC>,
  					  <&rp1_clocks RP1_CLK_SYS>,
-@@ -38,6 +39,7 @@
+@@ -46,6 +47,7 @@
  					       <1536000000>, // RP1_PLL_AUDIO_CORE
  					       <200000000>,  // RP1_PLL_SYS
  					       <125000000>,  // RP1_PLL_SYS_SEC
@@ -30,7 +30,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
  					       <61440000>,   // RP1_PLL_AUDIO
  					       <192000000>,  // RP1_PLL_AUDIO_SEC
  					       <200000000>,  // RP1_CLK_SYS
-@@ -968,12 +970,14 @@
+@@ -976,12 +978,14 @@
  
  		rp1_eth: ethernet@100000 {
  			reg = <0xc0 0x40100000  0x0 0x4000>;

+ 1 - 1
target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch

@@ -1,7 +1,7 @@
 From d4e41ed9954fa86c4774f98d393aa401c81a68e7 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Wed, 13 Nov 2024 13:10:27 +0000
-Subject: [PATCH 1427/1482] clk: rp1: Add RP1_CLK_DMA.
+Subject: [PATCH] clk: rp1: Add RP1_CLK_DMA.
 
 The DMA block has a clock, but wasn't defined in the driver. This
 resulted in the parent being disabled as unused, and then DMA

+ 1 - 1
target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch

@@ -1,7 +1,7 @@
 From 9049e4df2c54b5e620f855f66db3a18c9f2e181f Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Fri, 8 Nov 2024 17:37:08 +0000
-Subject: [PATCH 1428/1482] rp1: clk: Remove CLK_IGNORE_UNUSED flags
+Subject: [PATCH] rp1: clk: Remove CLK_IGNORE_UNUSED flags
 
 There should be no issue in disabling the RP1 clocks as long as
 the kernel knows about all consumers.

+ 3 - 3
target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch

@@ -1,7 +1,7 @@
 From 542d0f7f2e9f90fc0f02f8cb141f7c3fbf46081b Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Mon, 11 Nov 2024 17:11:18 +0000
-Subject: [PATCH 1429/1482] dt: rp1: Use clk_sys for ethernet hclk and pclk
+Subject: [PATCH] dt: rp1: Use clk_sys for ethernet hclk and pclk
 
 hclk and pclk of the MAC are connected to clk_sys, so define
 them as being connected accordingly, rather than having fake
@@ -14,7 +14,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
 
 --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
 +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -974,7 +974,8 @@
+@@ -982,7 +982,8 @@
  			#address-cells = <1>;
  			#size-cells = <0>;
  			interrupts = <RP1_INT_ETH IRQ_TYPE_LEVEL_HIGH>;
@@ -24,7 +24,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
  				  &rp1_clocks RP1_CLK_ETH_TSU
  				  &rp1_clocks RP1_CLK_ETH>;
  			clock-names = "pclk", "hclk", "tsu_clk", "tx_clk";
-@@ -1195,18 +1196,6 @@
+@@ -1230,18 +1231,6 @@
  		clock-output-names = "xosc";
  		clock-frequency = <50000000>;
  	};

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch

@@ -1,7 +1,7 @@
 From efecbda4014b490e042c7fd090942b32316f9345 Mon Sep 17 00:00:00 2001
 From: Dave Stevenson <[email protected]>
 Date: Wed, 13 Nov 2024 13:11:33 +0000
-Subject: [PATCH 1430/1482] dt: rp1: Link RP1 DMA to the associated clock
+Subject: [PATCH] dt: rp1: Link RP1 DMA to the associated clock
 
 This makes the kernel representation of the clock structure
 match reality.
@@ -13,7 +13,7 @@ Signed-off-by: Dave Stevenson <[email protected]>
 
 --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
 +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -1061,7 +1061,7 @@
+@@ -1081,7 +1081,7 @@
  			reg = <0xc0 0x40188000  0x0 0x1000>;
  			compatible = "snps,axi-dma-1.01a";
  			interrupts = <RP1_INT_DMA IRQ_TYPE_LEVEL_HIGH>;

+ 23 - 0
target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch

@@ -0,0 +1,23 @@
+From eb035f3ad7da1324d310ef83b42398f47d5bafe7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <[email protected]>
+Date: Fri, 1 Nov 2024 19:42:17 +0000
+Subject: [PATCH] raspberrypi-firmware: Add the RPI firmware UART APIs
+
+Add VideoCore mailbox definitions for the new RPi firmware UART.
+
+Signed-off-by: Tim Gover <[email protected]>
+---
+ include/soc/bcm2835/raspberrypi-firmware.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -98,6 +98,8 @@ 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_SW_UART =                            0x0003008a,
++	RPI_FIRMWARE_SET_SW_UART =                            0x0003808a,
+ 
+ 	/* Dispmanx TAGS */
+ 	RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,

+ 22 - 0
target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch

@@ -0,0 +1,22 @@
+From b8a0e563fd181205565a0edaaebc82b1abf0c5be Mon Sep 17 00:00:00 2001
+From: Tim Gover <[email protected]>
+Date: Fri, 1 Nov 2024 19:43:21 +0000
+Subject: [PATCH] serial: core: Add the Raspberry Pi firmware UART id
+
+Assign a new serial core number for the RPi firmware UART.
+
+Signed-off-by: Tim Gover <[email protected]>
+---
+ include/uapi/linux/serial_core.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -245,4 +245,7 @@
+ /* Sunplus UART */
+ #define PORT_SUNPLUS	123
+ 
++/* RPi firmware UART */
++#define PORT_RPI_FW	124
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */

+ 630 - 0
target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch

@@ -0,0 +1,630 @@
+From 2548d954d78bca44c5cf430f8ea6de7c771312d7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <[email protected]>
+Date: Wed, 28 Aug 2024 09:46:50 +0100
+Subject: [PATCH] serial: tty: Add a driver for the RPi firmware UART
+
+On Raspberry Pi 4 and earlier models the firmware provides
+a low speed (up to 115200 baud) bit-bashed UART on arbitrary
+GPIOs using the second VPU core.
+
+The firmware driver is designed to support 19200 baud. Higher
+rates up to 115200 seem to work but there may be more jitter.
+
+This can be useful for debug or managing additional low
+speed peripherals if the hardware PL011 and 8250 hardware
+UARTs are already used for console / bluetooth.
+
+The firmware driver requires a fixed core clock frequency
+and also requires the VPU PWM audio driver to be disabled
+(dtparam=audio=off)
+
+Runtime configuration is handled via the vc-mailbox APIs
+with the FIFO buffers being allocated in uncached VPU
+addressable memory. The FIFO pointers are stored in spare
+VideoCore multi-core sync registers in order to reduce the number
+of uncached SDRAM accesses thereby reducing jitter.
+
+Signed-off-by: Tim Gover <[email protected]>
+---
+ drivers/tty/serial/Kconfig       |  11 +
+ drivers/tty/serial/Makefile      |   1 +
+ drivers/tty/serial/rpi-fw-uart.c | 563 +++++++++++++++++++++++++++++++
+ 3 files changed, 575 insertions(+)
+ create mode 100644 drivers/tty/serial/rpi-fw-uart.c
+
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1578,6 +1578,17 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
+ 	  but you can alter that using a kernel command line option such as
+ 	  "console=ttyNVTx".
+ 
++config SERIAL_RPI_FW
++	tristate "Raspberry Pi Firmware software UART support"
++	depends on ARM_AMBA || COMPILE_TEST
++	select SERIAL_CORE
++	help
++	  This selects the Raspberry Pi firmware UART. This is a bit-bashed
++	  implementation running on the Raspbery Pi VPU core.
++	  This is not supported on Raspberry Pi 5 or newer platforms.
++
++	  If unsure, say N.
++
+ endmenu
+ 
+ config SERIAL_MCTRL_GPIO
+--- a/drivers/tty/serial/Makefile
++++ b/drivers/tty/serial/Makefile
+@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += mi
+ obj-$(CONFIG_SERIAL_SIFIVE)	+= sifive.o
+ obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o
+ obj-$(CONFIG_SERIAL_SUNPLUS)	+= sunplus-uart.o
++obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o
+ 
+ # GPIOLIB helpers for modem control lines
+ obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
+--- /dev/null
++++ b/drivers/tty/serial/rpi-fw-uart.c
+@@ -0,0 +1,563 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2024, Raspberry Pi Ltd.  All rights reserved.
++ */
++
++#include <linux/console.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/gpio/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/serial.h>
++#include <linux/serial_core.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++#include <linux/dma-mapping.h>
++
++#define RPI_FW_UART_RX_FIFO_RD	0xb0
++#define RPI_FW_UART_RX_FIFO_WR	0xb4
++#define RPI_FW_UART_TX_FIFO_RD	0xb8
++#define RPI_FW_UART_TX_FIFO_WR	0xbc
++
++#define RPI_FW_UART_FIFO_SIZE		32
++#define RPI_FW_UART_FIFO_SIZE_MASK	(RPI_FW_UART_FIFO_SIZE - 1)
++
++#define RPI_FW_UART_MIN_VERSION	3
++
++struct rpi_fw_uart_params {
++	u32 start;
++	u32 baud;
++	u32 data_bits;
++	u32 stop_bits;
++	u32 gpio_rx;
++	u32 gpio_tx;
++	u32 flags;
++	u32 fifosize;
++	u32 rx_buffer;
++	u32 tx_buffer;
++	u32 version;
++	u32 fifo_reg_base;
++};
++
++struct rpi_fw_uart {
++	struct uart_driver	driver;
++	struct uart_port	port;
++	struct rpi_firmware	*firmware;
++	struct gpio_desc	*rx_gpiod;
++	struct gpio_desc	*tx_gpiod;
++	unsigned int		rx_gpio;
++	unsigned int		tx_gpio;
++	unsigned int		baud;
++	unsigned int		data_bits;
++	unsigned int		stop_bits;
++	unsigned char __iomem	*base;
++	size_t			dma_buffer_size;
++
++	struct hrtimer		trigger_start_rx;
++	ktime_t			rx_poll_delay;
++	void			*rx_buffer;
++	dma_addr_t		rx_buffer_dma_addr;
++	int			rx_stop;
++
++	void			*tx_buffer;
++	dma_addr_t		tx_buffer_dma_addr;
++};
++
++static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	u32 rd, wr;
++
++	rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
++	wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);
++	return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd;
++}
++
++static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	u32 rd, wr;
++
++	if (!rfu->tx_buffer)
++		return 1;
++
++	rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
++	wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);
++
++	return rd == wr;
++}
++
++unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	u32 rd, wr;
++
++	if (!rfu->rx_buffer)
++		return 1;
++
++	rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD);
++	wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR);
++
++	return rd == wr;
++}
++
++static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port)
++{
++	return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0;
++}
++
++static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++	/*
++	 * No hardware flow control, firmware automatically configures
++	 * TX to output high and RX to input low.
++	 */
++	dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl);
++}
++
++static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port)
++{
++	/* No hardware flow control */
++	return TIOCM_CTS;
++}
++
++static void rpi_fw_uart_stop(struct uart_port *port)
++{
++	struct rpi_fw_uart_params msg = {.start = 0};
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++	hrtimer_cancel(&rfu->trigger_start_rx);
++
++	if (rpi_firmware_property(rfu->firmware,
++				RPI_FIRMWARE_SET_SW_UART,
++				&msg, sizeof(msg)))
++		dev_warn(port->dev,
++			 "Failed to shutdown rpi-fw uart. Firmware not configured?");
++}
++
++static void rpi_fw_uart_stop_tx(struct uart_port *port)
++{
++	/* No supported by the current firmware APIs. */
++}
++
++static void rpi_fw_uart_stop_rx(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++	rfu->rx_stop = 1;
++}
++
++static unsigned int rpi_fw_write(struct uart_port *port, const char *s,
++				unsigned int count)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	u8 *out = rfu->tx_buffer;
++	unsigned int consumed = 0;
++
++	while (consumed < count && !rpi_fw_uart_tx_is_full(port)) {
++		u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR)
++			& RPI_FW_UART_FIFO_SIZE_MASK;
++		out[wp] = s[consumed++];
++		wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
++		writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR);
++	}
++	return consumed;
++}
++
++/* Called with port.lock taken */
++static void rpi_fw_uart_start_tx(struct uart_port *port)
++{
++	struct circ_buf *xmit;
++
++	xmit = &port->state->xmit;
++	for (;;) {
++		unsigned int consumed;
++		unsigned long count = CIRC_CNT_TO_END(xmit->head, xmit->tail,
++				UART_XMIT_SIZE);
++		if (!count)
++			break;
++
++		consumed = rpi_fw_write(port, &xmit->buf[xmit->tail], count);
++		uart_xmit_advance(port, consumed);
++	}
++	uart_write_wakeup(port);
++}
++
++/* Called with port.lock taken */
++static void rpi_fw_uart_start_rx(struct uart_port *port)
++{
++	struct tty_port *tty_port = &port->state->port;
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	int count = 0;
++
++	/*
++	 * RX is polled, read up to a full buffer of data before trying again
++	 * so that this can be interrupted if the firmware is filling the
++	 * buffer too fast
++	 */
++	while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) {
++		const u8 *in = rfu->rx_buffer;
++		u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD)
++			& RPI_FW_UART_FIFO_SIZE_MASK;
++
++		tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL);
++		rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
++		writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD);
++		count++;
++	}
++	if (count)
++		tty_flip_buffer_push(tty_port);
++}
++
++static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t)
++{
++	unsigned long flags;
++	struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart,
++					      trigger_start_rx);
++
++	spin_lock_irqsave(&rfu->port.lock, flags);
++	if (rfu->rx_stop) {
++		spin_unlock_irqrestore(&rfu->port.lock, flags);
++		return HRTIMER_NORESTART;
++	}
++
++	rpi_fw_uart_start_rx(&rfu->port);
++	spin_unlock_irqrestore(&rfu->port.lock, flags);
++	hrtimer_forward_now(t, rfu->rx_poll_delay);
++	return HRTIMER_RESTART;
++}
++
++static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl)
++{
++	dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl);
++}
++
++static int rpi_fw_uart_configure(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++	struct rpi_fw_uart_params msg;
++	unsigned long flags;
++	int rc;
++
++	rpi_fw_uart_stop(port);
++
++	memset(&msg, 0, sizeof(msg));
++	msg.start = 1;
++	msg.gpio_rx = rfu->rx_gpio;
++	msg.gpio_tx = rfu->tx_gpio;
++	msg.data_bits = rfu->data_bits;
++	msg.stop_bits = rfu->stop_bits;
++	msg.baud = rfu->baud;
++	msg.fifosize = RPI_FW_UART_FIFO_SIZE;
++	msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr;
++	msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr;
++
++	rfu->rx_poll_delay = ms_to_ktime(50);
++
++	/*
++	 * Reconfigures the firmware UART with the new settings. On the first
++	 * call retrieve the addresses of the FIFO buffers. The buffers are
++	 * allocated at startup and are not de-allocated.
++	 * NB rpi_firmware_property can block
++	 */
++	rc = rpi_firmware_property(rfu->firmware,
++				RPI_FIRMWARE_SET_SW_UART,
++				&msg, sizeof(msg));
++	if (rc)
++		goto fail;
++
++	rc = rpi_firmware_property(rfu->firmware,
++			RPI_FIRMWARE_GET_SW_UART,
++			&msg, sizeof(msg));
++	if (rc)
++		goto fail;
++
++	dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version,
++		msg.fifo_reg_base);
++
++	dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
++			msg.start, msg.baud, msg.data_bits, msg.stop_bits,
++			msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
++
++	if (msg.fifosize != port->fifosize) {
++		dev_err(port->dev, "Expected fifo size %u actual %u",
++				port->fifosize, msg.fifosize);
++		rc = -EINVAL;
++		goto fail;
++	}
++
++	if (!msg.start) {
++		dev_err(port->dev, "Firmware service not running\n");
++		rc = -EINVAL;
++	}
++
++	spin_lock_irqsave(&rfu->port.lock, flags);
++	rfu->rx_stop = 0;
++	hrtimer_start(&rfu->trigger_start_rx,
++		      rfu->rx_poll_delay, HRTIMER_MODE_REL);
++	spin_unlock_irqrestore(&rfu->port.lock, flags);
++	return 0;
++fail:
++	dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?");
++	return rc;
++}
++
++static void rpi_fw_uart_free_buffers(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++	if (rfu->rx_buffer)
++		dma_free_coherent(port->dev, rfu->dma_buffer_size,
++				rfu->rx_buffer, GFP_ATOMIC);
++
++	if (rfu->tx_buffer)
++		dma_free_coherent(port->dev, rfu->dma_buffer_size,
++				rfu->tx_buffer, GFP_ATOMIC);
++
++	rfu->rx_buffer = NULL;
++	rfu->tx_buffer = NULL;
++	rfu->rx_buffer_dma_addr = 0;
++	rfu->tx_buffer_dma_addr = 0;
++}
++
++static int rpi_fw_uart_alloc_buffers(struct uart_port *port)
++{
++	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++	if (rfu->tx_buffer)
++		return 0;
++
++	rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE);
++
++	rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
++		&rfu->rx_buffer_dma_addr, GFP_ATOMIC);
++
++	if (!rfu->rx_buffer)
++		goto alloc_fail;
++
++	rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
++		&rfu->tx_buffer_dma_addr, GFP_ATOMIC);
++
++	if (!rfu->tx_buffer)
++		goto alloc_fail;
++
++	dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n",
++		rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr,
++		rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr);
++	return 0;
++
++alloc_fail:
++	dev_err(port->dev, "%s uart buffer allocation failed\n", __func__);
++	rpi_fw_uart_free_buffers(port);
++	return -ENOMEM;
++}
++
++static int rpi_fw_uart_startup(struct uart_port *port)
++{
++	int rc;
++
++	rc = rpi_fw_uart_alloc_buffers(port);
++	if (rc)
++		dev_err(port->dev, "Failed to start\n");
++	return rc;
++}
++
++static void rpi_fw_uart_shutdown(struct uart_port *port)
++{
++	rpi_fw_uart_stop(port);
++	rpi_fw_uart_free_buffers(port);
++}
++
++static void rpi_fw_uart_set_termios(struct uart_port *port,
++				       struct ktermios *new,
++				       const struct ktermios *old)
++{
++	struct rpi_fw_uart *rfu =
++		container_of(port, struct rpi_fw_uart, port);
++	rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200);
++	rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1;
++
++	rpi_fw_uart_configure(port);
++}
++
++static const struct uart_ops rpi_fw_uart_ops = {
++	.tx_empty = rpi_fw_uart_tx_empty,
++	.set_mctrl = rpi_fw_uart_set_mctrl,
++	.get_mctrl = rpi_fw_uart_get_mctrl,
++	.stop_rx = rpi_fw_uart_stop_rx,
++	.stop_tx = rpi_fw_uart_stop_tx,
++	.start_tx = rpi_fw_uart_start_tx,
++	.break_ctl = rpi_fw_uart_break_ctl,
++	.startup = rpi_fw_uart_startup,
++	.shutdown = rpi_fw_uart_shutdown,
++	.set_termios = rpi_fw_uart_set_termios,
++};
++
++static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name)
++{
++	struct of_phandle_args of_args = { 0 };
++	bool is_bcm28xx;
++
++	/* This really shouldn't fail, given that we have a gpiod */
++	if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args))
++		return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n");
++
++	is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") ||
++		     of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio");
++	of_node_put(of_args.np);
++	if (!is_bcm28xx || of_args.args_count != 2)
++		return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n");
++
++	return of_args.args[0];
++}
++
++static int rpi_fw_uart_probe(struct platform_device *pdev)
++{
++	struct device_node *firmware_node;
++	struct device *dev = &pdev->dev;
++	struct rpi_firmware *firmware;
++	struct uart_port *port;
++	struct rpi_fw_uart *rfu;
++	struct rpi_fw_uart_params msg;
++	int version_major;
++	int err;
++
++	dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node);
++
++	/*
++	 * We can be probed either through the an old-fashioned
++	 * platform device registration or through a DT node that is a
++	 * child of the firmware node. Handle both cases.
++	 */
++	if (dev->of_node)
++		firmware_node = of_parse_phandle(dev->of_node, "firmware", 0);
++	else
++		firmware_node = of_find_compatible_node(NULL, NULL,
++				"raspberrypi,bcm2835-firmware");
++	if (!firmware_node) {
++		dev_err(dev, "Missing firmware node\n");
++		return -ENOENT;
++	}
++
++	firmware = devm_rpi_firmware_get(dev, firmware_node);
++	of_node_put(firmware_node);
++	if (!firmware)
++		return -EPROBE_DEFER;
++
++	rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL);
++	if (!rfu)
++		return -ENOMEM;
++
++	rfu->firmware = firmware;
++
++	err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART,
++			&msg, sizeof(msg));
++	if (err) {
++		dev_err(dev, "VC firmware does not support rpi-fw-uart\n");
++		return err;
++	}
++
++	version_major = msg.version >> 16;
++	if (msg.version < RPI_FW_UART_MIN_VERSION) {
++		dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n",
++				version_major, RPI_FW_UART_MIN_VERSION);
++		return -EINVAL;
++	}
++
++	rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN);
++	if (IS_ERR(rfu->rx_gpiod))
++		return PTR_ERR(rfu->rx_gpiod);
++
++	rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH);
++	if (IS_ERR(rfu->tx_gpiod))
++		return PTR_ERR(rfu->tx_gpiod);
++
++	rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios");
++	if (rfu->rx_gpio < 0)
++		return rfu->rx_gpio;
++	rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios");
++	if (rfu->tx_gpio < 0)
++		return rfu->tx_gpio;
++
++	rfu->base = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(rfu->base))
++		return PTR_ERR(rfu->base);
++
++	/* setup the driver */
++	rfu->driver.owner = THIS_MODULE;
++	rfu->driver.driver_name = "ttyRFU";
++	rfu->driver.dev_name = "ttyRFU";
++	rfu->driver.nr = 1;
++	rfu->data_bits = 8;
++
++	/* RX is polled */
++	hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++	rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx;
++
++	err = uart_register_driver(&rfu->driver);
++	if (err) {
++		dev_err(dev, "failed to register UART driver: %d\n",
++			err);
++		return err;
++	}
++
++	/* setup the port */
++	port = &rfu->port;
++	spin_lock_init(&port->lock);
++	port->dev = &pdev->dev;
++	port->type = PORT_RPI_FW;
++	port->ops = &rpi_fw_uart_ops;
++	port->fifosize = RPI_FW_UART_FIFO_SIZE;
++	port->iotype = UPIO_MEM;
++	port->flags = UPF_BOOT_AUTOCONF;
++	port->private_data = rfu;
++
++	err = uart_add_one_port(&rfu->driver, port);
++	if (err) {
++		dev_err(dev, "failed to add UART port: %d\n", err);
++		goto unregister_uart;
++	}
++	platform_set_drvdata(pdev, rfu);
++
++	dev_info(dev, "version %d.%d gpios tx %u rx %u\n",
++			msg.version >> 16, msg.version & 0xffff,
++			rfu->tx_gpio, rfu->rx_gpio);
++	return 0;
++
++unregister_uart:
++	uart_unregister_driver(&rfu->driver);
++
++	return err;
++}
++
++static int rpi_fw_uart_remove(struct platform_device *pdev)
++{
++	struct rpi_fw_uart *rfu = platform_get_drvdata(pdev);
++
++	uart_remove_one_port(&rfu->driver, &rfu->port);
++	uart_unregister_driver(&rfu->driver);
++
++	return 0;
++}
++
++static const struct of_device_id rpi_fw_match[] = {
++	{ .compatible = "raspberrypi,firmware-uart" },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, rpi_fw_match);
++
++static struct platform_driver rpi_fw_driver = {
++	.driver = {
++		.name = "rpi_fw-uart",
++		.of_match_table = rpi_fw_match,
++	},
++	.probe = rpi_fw_uart_probe,
++	.remove = rpi_fw_uart_remove,
++};
++module_platform_driver(rpi_fw_driver);
++
++MODULE_AUTHOR("Tim Gover <[email protected]>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver");

+ 95 - 0
target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch

@@ -0,0 +1,95 @@
+From b6b126861062020fb50859c5af71d8846ce43d7c Mon Sep 17 00:00:00 2001
+From: Tim Gover <[email protected]>
+Date: Mon, 4 Nov 2024 13:44:10 +0000
+Subject: [PATCH] dtoverlay: Add an overlay for the Raspberry Pi firmware UART
+
+Add a device-tree overlay to configure the GPIOs for the
+Raspberry Pi firmware UART.
+
+Example config.txt
+dtoverlay=rpi-fw-uart,txd0_pin=20,rxd0_pin=21
+
+Signed-off-by: Phil Elwell <[email protected]>
+Signed-off-by: Tim Gover <[email protected]>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             | 12 ++++++
+ .../boot/dts/overlays/rpi-fw-uart-overlay.dts | 41 +++++++++++++++++++
+ 3 files changed, 54 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -233,6 +233,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ 	rpi-dacpro.dtbo \
+ 	rpi-digiampplus.dtbo \
+ 	rpi-ft5406.dtbo \
++	rpi-fw-uart.dtbo \
+ 	rpi-poe.dtbo \
+ 	rpi-poe-plus.dtbo \
+ 	rpi-sense.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4141,6 +4141,18 @@ Params: touchscreen-size-x      Touchscr
+         touchscreen-swapped-x-y Swap X and Y cordinates (default 0);
+ 
+ 
++Name:   rpi-fw-uart
++Info:   Configures the firmware software UART driver.
++        This driver requires exclusive usage of the second VPU core. The
++        following config.txt entries should be set when this driver is used.
++        dtparam=audio=off
++        isp_use_vpu0=1
++Load:   dtoverlay=rpi-fw-uart,<param>[=<val>]
++Params: txd0_pin                GPIO pin for TXD0 (any free - default 20)
++
++        rxd0_pin                GPIO pin for RXD0 (any free - default 21)
++
++
+ Name:   rpi-poe
+ Info:   Raspberry Pi PoE HAT fan
+ Load:   dtoverlay=rpi-poe,<param>[=<val>]
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts
+@@ -0,0 +1,41 @@
++// SPDX-License-Identifier: GPL-2.0
++// Overlay for the Raspberry Pi Firmware UART driver
++/dts-v1/;
++/plugin/;
++
++/{
++	compatible = "brcm,bcm2835";
++
++	fragment@0 {
++		target = <&gpio>;
++		__overlay__ {
++			rpi_fw_uart_pins: rpi_fw_uart_pins@4 {
++				brcm,pins = <20 21>;
++				brcm,function = <1 0>; /* output input */
++				brcm,pull = <0 2>; /* none pull-up */
++			};
++		};
++	};
++
++	fragment@1 {
++		target = <&soc>;
++		__overlay__ {
++			rpi_fw_uart: rpi_fw_uart@7e000000 {
++			compatible = "raspberrypi,firmware-uart";
++			reg = <0x7e000000 0x100>; /* VideoCore MS sync regs */
++			firmware = <&firmware>;
++			pinctrl-names = "default";
++			pinctrl-0 = <&rpi_fw_uart_pins>;
++			tx-gpios = <&gpio 20 0>;
++			rx-gpios = <&gpio 21 0>;
++			};
++		};
++	};
++
++	__overrides__ {
++		txd0_pin = <&rpi_fw_uart>,"tx-gpios:4",
++			 <&rpi_fw_uart_pins>, "brcm,pins:0";
++		rxd0_pin = <&rpi_fw_uart>,"rx-gpios:4",
++			 <&rpi_fw_uart_pins>, "brcm,pins:4";
++	};
++};

+ 142 - 0
target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch

@@ -0,0 +1,142 @@
+From 1993b453dc4a62378e90d91e9e0006a6c085f38a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 18 Sep 2024 10:23:41 +0100
+Subject: [PATCH] ARM: dts: Remove duplicate tags
+
+A dts file should have exactly one /dts-v1/ tag, and overlays should
+also have one /plugin/ tag. Through careless inclusion of other files,
+some Device Trees and overlays end up with duplicated tags - this
+commit removes them.
+
+The change is largely cosmetic, unless using an old version of dtc.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts            | 1 -
+ arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi         | 3 ---
+ arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi        | 2 --
+ arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts        | 3 ---
+ arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts       | 3 ---
+ arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts   | 3 ---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts        | 3 ---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts    | 3 ---
+ arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts        | 3 ---
+ arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts | 3 ---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi        | 1 -
+ 11 files changed, 28 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts
+@@ -1,5 +1,4 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/dts-v1/;
+ #include "bcm2711-rpi-4-b.dts"
+ 
+ / {
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -1,7 +1,4 @@
+ // Definitions for I2C based sensors using the Industrial IO or HWMON interface.
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/gpio/gpio.h>
+ 
+ / {
+--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
+@@ -1,8 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ // Partial definitions for IMX290 or IMX327 camera module on VC I2C bus
+ // The compatible string should be set in an overlay that then includes this one
+-/dts-v1/;
+-/plugin/;
+ 
+ #include <dt-bindings/gpio/gpio.h>
+ 
+--- a/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts
+@@ -17,9 +17,6 @@
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+  */
+ 
+-/dts-v1/;
+-/plugin/;
+-
+ #include "pisound-overlay.dts"
+ 
+ &pisound_spi {
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
+@@ -2,9 +2,6 @@
+  * vc4-fkms-v3d-overlay.dts
+  */
+ 
+-/dts-v1/;
+-/plugin/;
+-
+ #include "cma-overlay.dts"
+ 
+ / {
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
+@@ -2,9 +2,6 @@
+  * vc4-fkms-v3d-overlay.dts
+  */
+ 
+-/dts-v1/;
+-/plugin/;
+-
+ #include "cma-overlay.dts"
+ 
+ &frag0 {
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
+@@ -2,9 +2,6 @@
+  * vc4-kms-v3d-overlay.dts
+  */
+ 
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/clock/bcm2835.h>
+ 
+ #include "cma-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
+@@ -2,9 +2,6 @@
+  * vc4-kms-v3d-pi4-overlay.dts
+  */
+ 
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/clock/bcm2835.h>
+ 
+ #include "cma-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts
+@@ -1,6 +1,3 @@
+-/dts-v1/;
+-/plugin/;
+-
+ #include "w1-gpio-overlay.dts"
+ 
+ / {
+--- a/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts
+@@ -1,6 +1,3 @@
+-/dts-v1/;
+-/plugin/;
+-
+ #include "w1-gpio-pullup-overlay.dts"
+ 
+ / {
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
+@@ -1,5 +1,4 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/dts-v1/;
+ 
+ #include "bcm2712-rpi-cm5.dtsi"
+ 

+ 25 - 0
target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch

@@ -0,0 +1,25 @@
+From e33702e5e5fe9fef6ec967961e2e5e1c2285ba36 Mon Sep 17 00:00:00 2001
+From: gtrainavicius <[email protected]>
+Date: Wed, 4 Dec 2024 11:18:14 +0200
+Subject: [PATCH] =?UTF-8?q?Allow=20setting=20I=C2=B2C=20clock=20frequency?=
+ =?UTF-8?q?=20via=20i2c=5Farm=5Fbaudrate=20dtparam=20when=20using=20pimidi?=
+ =?UTF-8?q?=20overlay.?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This change removes the forced 1MHz clock frequency, so it can be overridden using `i2c_arm_baudrate`.
+---
+ arch/arm/boot/dts/overlays/pimidi-overlay.dts | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/pimidi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts
+@@ -26,7 +26,6 @@
+ 		target = <&i2c_arm>;
+ 		__overlay__ {
+ 			status = "okay";
+-			clock-frequency=<1000000>;
+ 
+ 			pimidi_ctrl: pimidi_ctrl@20 {
+ 				compatible = "blokaslabs,pimidi";

+ 48 - 0
target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch

@@ -0,0 +1,48 @@
+From fda47c026dee7acd975ee2c0f7a440d4038cfaa3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Tue, 3 Dec 2024 15:57:01 +0000
+Subject: [PATCH] nvme-pci: Disable Host Memory Buffer usage
+
+Some NVME drives seem to request significant amounts of DMA coherent
+memory - enough to exhaust our standard 64MB CMA allocation.
+
+Try disabling the feature to see what effect it has - drives should
+continue to function without it.
+
+Link: https://github.com/raspberrypi/linux/issues/6504
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/nvme/host/pci.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -1932,6 +1932,7 @@ static void nvme_free_host_mem(struct nv
+ 	dev->nr_host_mem_descs = 0;
+ }
+ 
++#if 0
+ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
+ 		u32 chunk_size)
+ {
+@@ -2000,9 +2001,11 @@ out:
+ 	dev->host_mem_descs = NULL;
+ 	return -ENOMEM;
+ }
++#endif
+ 
+ static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred)
+ {
++#if 0
+ 	u64 min_chunk = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES);
+ 	u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2);
+ 	u64 chunk_size;
+@@ -2015,6 +2018,7 @@ static int nvme_alloc_host_mem(struct nv
+ 			nvme_free_host_mem(dev);
+ 		}
+ 	}
++#endif
+ 
+ 	return -ENOMEM;
+ }

+ 23 - 0
target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch

@@ -0,0 +1,23 @@
+From 0313a0961b685973f7833017479a277e3a4c05a4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 4 Dec 2024 14:40:59 +0000
+Subject: [PATCH] fixup! serial: tty: Add a driver for the RPi firmware UART
+
+Make SERIAL_RPI_FW depend on RASPBERRYPI_FIRMWARE.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/tty/serial/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1580,7 +1580,7 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
+ 
+ config SERIAL_RPI_FW
+ 	tristate "Raspberry Pi Firmware software UART support"
+-	depends on ARM_AMBA || COMPILE_TEST
++	depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
+ 	select SERIAL_CORE
+ 	help
+ 	  This selects the Raspberry Pi firmware UART. This is a bit-bashed

+ 28 - 0
target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch

@@ -0,0 +1,28 @@
+From 0a5be0fe6ba3a981508421131def7eab55d6d75c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 5 Dec 2024 12:08:23 +0000
+Subject: [PATCH] serial: rpi-fw-uart: Demote debug log messages
+
+A dev_info call in rpi_fw_uart_configure causes kernel log output every
+time one opens the UART. Demote it to dev_dbg.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ drivers/tty/serial/rpi-fw-uart.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/rpi-fw-uart.c
++++ b/drivers/tty/serial/rpi-fw-uart.c
+@@ -277,9 +277,9 @@ static int rpi_fw_uart_configure(struct
+ 	dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version,
+ 		msg.fifo_reg_base);
+ 
+-	dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
+-			msg.start, msg.baud, msg.data_bits, msg.stop_bits,
+-			msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
++	dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
++		msg.start, msg.baud, msg.data_bits, msg.stop_bits,
++		msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
+ 
+ 	if (msg.fifosize != port->fifosize) {
+ 		dev_err(port->dev, "Expected fifo size %u actual %u",

+ 55 - 0
target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch

@@ -0,0 +1,55 @@
+From 02dee262a9c7295ea514e9db7b9aa4b239922cb3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Mon, 2 Dec 2024 15:41:21 +0000
+Subject: [PATCH] dtoverlays: Add Arducam override for ov9281
+
+The Arducam module is slow starting up, so add an override
+to slow the regulator down.
+https://forums.raspberrypi.com/viewtopic.php?t=380236
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README             |  2 ++
+ arch/arm/boot/dts/overlays/ov9281-overlay.dts | 13 ++++++++++++-
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3538,6 +3538,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).
++        arducam                 Slow down the regulator for slow Arducam
++                                modules.
+ 
+ 
+ Name:   papirus
+--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts
+@@ -57,6 +57,14 @@
+ 		};
+ 	};
+ 
++	reg_frag: fragment@5 {
++		target = <&cam1_reg>;
++		__dormant__ {
++			startup-delay-us = <20000>;
++			off-on-delay-us = <30000>;
++		};
++	};
++
+ 	__overrides__ {
+ 		rotation = <&cam_node>,"rotation:0";
+ 		orientation = <&cam_node>,"orientation:0";
+@@ -65,7 +73,10 @@
+ 		       <&csi_frag>, "target:0=",<&csi0>,
+ 		       <&clk_frag>, "target:0=",<&cam0_clk>,
+ 		       <&cam_node>, "clocks:0=",<&cam0_clk>,
+-		       <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
++		       <&cam_node>, "avdd-supply:0=",<&cam0_reg>,
++		       <&reg_frag>, "target:0=",<&cam0_reg>;
++		arducam = <0>, "+5";
++
+ 	};
+ };
+ 

+ 118 - 0
target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch

@@ -0,0 +1,118 @@
+From 97638920f1a40e2e0cab363d1e03837ff50c5478 Mon Sep 17 00:00:00 2001
+From: eng33 <[email protected]>
+Date: Thu, 5 Dec 2024 17:19:23 +0800
+Subject: [PATCH] drivers:input:touchscreen: Add support for no irq to ili210x
+ driver
+
+Signed-off-by: eng33 <[email protected]>
+---
+ drivers/input/touchscreen/ili210x.c | 63 ++++++++++++++++++++++++-----
+ 1 file changed, 52 insertions(+), 11 deletions(-)
+
+--- a/drivers/input/touchscreen/ili210x.c
++++ b/drivers/input/touchscreen/ili210x.c
+@@ -67,6 +67,8 @@ struct ili210x {
+ 	u8 version_proto[2];
+ 	u8 ic_mode[2];
+ 	bool stop;
++	struct timer_list poll_timer;
++	struct work_struct poll_work;
+ };
+ 
+ static int ili210x_read_reg(struct i2c_client *client,
+@@ -360,6 +362,34 @@ static irqreturn_t ili210x_irq(int irq,
+ 	return IRQ_HANDLED;
+ }
+ 
++static void ili210x_poll_work(struct work_struct *work)
++{
++	struct ili210x *priv = container_of(work, struct ili210x, poll_work);
++	struct i2c_client *client = priv->client;
++	const struct ili2xxx_chip *chip = priv->chip;
++	u8 touchdata[ILI210X_DATA_SIZE] = { 0 };
++	bool touch;
++	int error;
++
++	error = chip->get_touch_data(client, touchdata);
++	if (error) {
++		dev_err(&client->dev, "Unable to get touch data: %d\n", error);
++		return;
++	}
++
++	touch = ili210x_report_events(priv, touchdata);
++}
++
++static void ili210x_poll_timer_callback(struct timer_list *t)
++{
++	struct ili210x *priv = from_timer(priv, t, poll_timer);
++
++	schedule_work(&priv->poll_work);
++
++	if (!priv->stop)
++		mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD));
++}
++
+ static int ili251x_firmware_update_resolution(struct device *dev)
+ {
+ 	struct i2c_client *client = to_i2c_client(dev);
+@@ -945,11 +975,6 @@ static int ili210x_i2c_probe(struct i2c_
+ 		return -ENODEV;
+ 	}
+ 
+-	if (client->irq <= 0) {
+-		dev_err(dev, "No IRQ!\n");
+-		return -EINVAL;
+-	}
+-
+ 	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ 	if (IS_ERR(reset_gpio))
+ 		return PTR_ERR(reset_gpio);
+@@ -1001,12 +1026,17 @@ static int ili210x_i2c_probe(struct i2c_
+ 		return error;
+ 	}
+ 
+-	error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
+-					  IRQF_ONESHOT, client->name, priv);
+-	if (error) {
+-		dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+-			error);
+-		return error;
++	if (client->irq) {
++		error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
++					IRQF_ONESHOT, client->name, priv);
++		if (error) {
++			dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error);
++			return error;
++		}
++	} else {
++		timer_setup(&priv->poll_timer, ili210x_poll_timer_callback, 0);
++		mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD));
++		INIT_WORK(&priv->poll_work, ili210x_poll_work);
+ 	}
+ 
+ 	error = devm_add_action_or_reset(dev, ili210x_stop, priv);
+@@ -1029,6 +1059,16 @@ static int ili210x_i2c_probe(struct i2c_
+ 	return 0;
+ }
+ 
++static void ili210x_i2c_remove(struct i2c_client *client)
++{
++	struct ili210x *tsdata = i2c_get_clientdata(client);
++
++	if (!client->irq) {
++		del_timer(&tsdata->poll_timer);
++		cancel_work_sync(&tsdata->poll_work);
++	}
++}
++
+ static const struct i2c_device_id ili210x_i2c_id[] = {
+ 	{ "ili210x", (long)&ili210x_chip },
+ 	{ "ili2117", (long)&ili211x_chip },
+@@ -1054,6 +1094,7 @@ static struct i2c_driver ili210x_ts_driv
+ 	},
+ 	.id_table = ili210x_i2c_id,
+ 	.probe = ili210x_i2c_probe,
++	.remove   = ili210x_i2c_remove,
+ };
+ 
+ module_i2c_driver(ili210x_ts_driver);

+ 309 - 0
target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch

@@ -0,0 +1,309 @@
+From 4a89fda8f73df89e009a6188ef07ab97b1d03c7f Mon Sep 17 00:00:00 2001
+From: eng33 <[email protected]>
+Date: Thu, 5 Dec 2024 17:20:22 +0800
+Subject: [PATCH] drivers:gpu:drm:panel: Added waveshare 13.3inch panel(support
+  2/4lane)
+
+Signed-off-by: eng33 <[email protected]>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 155 +++++++++++++++++---
+ 1 file changed, 138 insertions(+), 17 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -32,6 +32,12 @@ struct ws_panel {
+ 	enum drm_panel_orientation orientation;
+ };
+ 
++struct ws_panel_data {
++	const struct drm_display_mode *mode;
++	int lanes;
++	unsigned long mode_flags;
++};
++
+ /* 2.8inch 480x640
+  * https://www.waveshare.com/product/raspberry-pi/displays/2.8inch-dsi-lcd.htm
+  */
+@@ -47,6 +53,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 640 + 150 + 50 + 150,
+ };
+ 
++static const struct ws_panel_data ws_panel_2_8_data = {
++	.mode = &ws_panel_2_8_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 3.4inch 800x800 Round
+  * https://www.waveshare.com/product/displays/lcd-oled/3.4inch-dsi-lcd-c.htm
+  */
+@@ -62,6 +74,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 800 + 8 + 4 + 16,
+ };
+ 
++static const struct ws_panel_data ws_panel_3_4_data = {
++	.mode = &ws_panel_3_4_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 4.0inch 480x800
+  * https://www.waveshare.com/product/raspberry-pi/displays/4inch-dsi-lcd.htm
+  */
+@@ -77,6 +95,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 800 + 20 + 100 + 20,
+ };
+ 
++static const struct ws_panel_data ws_panel_4_0_data = {
++	.mode = &ws_panel_4_0_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 7.0inch C 1024x600
+  * https://www.waveshare.com/product/raspberry-pi/displays/lcd-oled/7inch-dsi-lcd-c-with-case-a.htm
+  */
+@@ -92,6 +116,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 600 + 10 + 10 + 10,
+ };
+ 
++static const struct ws_panel_data ws_panel_7_0_c_data = {
++	.mode = &ws_panel_7_0_c_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 7.9inch 400x1280
+  * https://www.waveshare.com/product/raspberry-pi/displays/7.9inch-dsi-lcd.htm
+  */
+@@ -107,6 +137,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 1280 + 20 + 10 + 20,
+ };
+ 
++static const struct ws_panel_data ws_panel_7_9_data = {
++	.mode = &ws_panel_7_9_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 8.0inch or 10.1inch 1280x800
+  * https://www.waveshare.com/product/raspberry-pi/displays/8inch-dsi-lcd-c.htm
+  * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm
+@@ -123,6 +159,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 800 + 40 + 48 + 40,
+ };
+ 
++static const struct ws_panel_data ws_panel_10_1_data = {
++	.mode = &ws_panel_10_1_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 11.9inch 320x1480
+  * https://www.waveshare.com/product/raspberry-pi/displays/11.9inch-dsi-lcd.htm
+  */
+@@ -138,6 +180,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 1480 + 60 + 60 + 60,
+ };
+ 
++static const struct ws_panel_data ws_panel_11_9_data = {
++	.mode = &ws_panel_11_9_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ static const struct drm_display_mode ws_panel_4_mode = {
+ 	.clock = 50000,
+ 	.hdisplay = 720,
+@@ -150,6 +198,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 720 + 8 + 4 + 16,
+ };
+ 
++static const struct ws_panel_data ws_panel_4_data = {
++	.mode = &ws_panel_4_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 5.0inch 720x1280
+  * https://www.waveshare.com/5inch-dsi-lcd-d.htm
+  */
+@@ -165,6 +219,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 1280 + 20 + 20 + 20,
+ };
+ 
++static const struct ws_panel_data ws_panel_5_0_data = {
++	.mode = &ws_panel_5_0_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 6.25inch 720x1560
+  * https://www.waveshare.com/6.25inch-dsi-lcd.htm
+  */
+@@ -180,6 +240,12 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 1560 + 20 + 20 + 20,
+ };
+ 
++static const struct ws_panel_data ws_panel_6_25_data = {
++	.mode = &ws_panel_6_25_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 8.8inch 480x1920
+  * https://www.waveshare.com/8.8inch-dsi-lcd.htm
+  */
+@@ -195,6 +261,48 @@ static const struct drm_display_mode ws_
+ 	.vtotal = 1920 + 20 + 20 + 20,
+ };
+ 
++static const struct ws_panel_data ws_panel_8_8_data = {
++	.mode = &ws_panel_8_8_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
++static const struct drm_display_mode ws_panel_13_3_4lane_mode = {
++	.clock = 148500,
++	.hdisplay = 1920,
++	.hsync_start = 1920 + 88,
++	.hsync_end = 1920 + 88 + 44,
++	.htotal = 1920 + 88 + 44 + 148,
++	.vdisplay = 1080,
++	.vsync_start = 1080 + 4,
++	.vsync_end = 1080 + 4 + 5,
++	.vtotal = 1080 + 4 + 5 + 36,
++};
++
++static const struct ws_panel_data ws_panel_13_3_4lane_data = {
++	.mode = &ws_panel_13_3_4lane_mode,
++	.lanes = 4,
++	.mode_flags = MIPI_DSI_MODE_VIDEO  | MIPI_DSI_MODE_LPM,
++};
++
++static const struct drm_display_mode ws_panel_13_3_2lane_mode = {
++	.clock = 83333,
++	.hdisplay = 1920,
++	.hsync_start = 1920 + 88,
++	.hsync_end = 1920 + 88 + 44,
++	.htotal = 1920 + 88 + 44 + 148,
++	.vdisplay = 1080,
++	.vsync_start = 1080 + 4,
++	.vsync_end = 1080 + 4 + 5,
++	.vtotal = 1080 + 4 + 5 + 36,
++};
++
++static const struct ws_panel_data ws_panel_13_3_2lane_data = {
++	.mode = &ws_panel_13_3_2lane_mode,
++	.lanes = 2,
++	.mode_flags = MIPI_DSI_MODE_VIDEO  | MIPI_DSI_MODE_LPM,
++};
++
+ static struct ws_panel *panel_to_ts(struct drm_panel *panel)
+ {
+ 	return container_of(panel, struct ws_panel, base);
+@@ -232,7 +340,10 @@ static int ws_panel_enable(struct drm_pa
+ {
+ 	struct ws_panel *ts = panel_to_ts(panel);
+ 
+-	ws_panel_i2c_write(ts, 0xad, 0x01);
++	if (ts->mode == &ws_panel_13_3_2lane_mode)
++		ws_panel_i2c_write(ts, 0xad, 0x02);
++	else
++		ws_panel_i2c_write(ts, 0xad, 0x01);
+ 
+ 	return 0;
+ }
+@@ -328,13 +439,18 @@ static int ws_panel_probe(struct i2c_cli
+ 		.channel = 0,
+ 		.node = NULL,
+ 	};
++	const struct ws_panel_data *_ws_panel_data;
+ 	int ret;
+ 
+ 	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ 	if (!ts)
+ 		return -ENOMEM;
+ 
+-	ts->mode = of_device_get_match_data(dev);
++	_ws_panel_data = of_device_get_match_data(dev);
++	if (!_ws_panel_data)
++		return -EINVAL;
++
++	ts->mode = _ws_panel_data->mode;
+ 	if (!ts->mode)
+ 		return -EINVAL;
+ 
+@@ -396,10 +512,9 @@ static int ws_panel_probe(struct i2c_cli
+ 	 */
+ 	drm_panel_add(&ts->base);
+ 
+-	ts->dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+-			   MIPI_DSI_CLOCK_NON_CONTINUOUS;
++	ts->dsi->mode_flags = _ws_panel_data->mode_flags;
+ 	ts->dsi->format = MIPI_DSI_FMT_RGB888;
+-	ts->dsi->lanes = 2;
++	ts->dsi->lanes = _ws_panel_data->lanes;
+ 
+ 	ret = devm_mipi_dsi_attach(dev, ts->dsi);
+ 
+@@ -432,40 +547,46 @@ static void ws_panel_shutdown(struct i2c
+ static const struct of_device_id ws_panel_of_ids[] = {
+ 	{
+ 		.compatible = "waveshare,2.8inch-panel",
+-		.data = &ws_panel_2_8_mode,
++		.data = &ws_panel_2_8_data,
+ 	}, {
+ 		.compatible = "waveshare,3.4inch-panel",
+-		.data = &ws_panel_3_4_mode,
++		.data = &ws_panel_3_4_data,
+ 	}, {
+ 		.compatible = "waveshare,4.0inch-panel",
+-		.data = &ws_panel_4_0_mode,
++		.data = &ws_panel_4_0_data,
+ 	}, {
+ 		.compatible = "waveshare,7.0inch-c-panel",
+-		.data = &ws_panel_7_0_c_mode,
++		.data = &ws_panel_7_0_c_data,
+ 	}, {
+ 		.compatible = "waveshare,7.9inch-panel",
+-		.data = &ws_panel_7_9_mode,
++		.data = &ws_panel_7_9_data,
+ 	}, {
+ 		.compatible = "waveshare,8.0inch-panel",
+-		.data = &ws_panel_10_1_mode,
++		.data = &ws_panel_10_1_data,
+ 	}, {
+ 		.compatible = "waveshare,10.1inch-panel",
+-		.data = &ws_panel_10_1_mode,
++		.data = &ws_panel_10_1_data,
+ 	}, {
+ 		.compatible = "waveshare,11.9inch-panel",
+-		.data = &ws_panel_11_9_mode,
++		.data = &ws_panel_11_9_data,
+ 	}, {
+ 		.compatible = "waveshare,4inch-panel",
+-		.data = &ws_panel_4_mode,
++		.data = &ws_panel_4_data,
+ 	}, {
+ 		.compatible = "waveshare,5.0inch-panel",
+-		.data = &ws_panel_5_0_mode,
++		.data = &ws_panel_5_0_data,
+ 	}, {
+ 		.compatible = "waveshare,6.25inch-panel",
+-		.data = &ws_panel_6_25_mode,
++		.data = &ws_panel_6_25_data,
+ 	}, {
+ 		.compatible = "waveshare,8.8inch-panel",
+-		.data = &ws_panel_8_8_mode,
++		.data = &ws_panel_8_8_data,
++	}, {
++		.compatible = "waveshare,13.3inch-4lane-panel",
++		.data = &ws_panel_13_3_4lane_data,
++	}, {
++		.compatible = "waveshare,13.3inch-2lane-panel",
++		.data = &ws_panel_13_3_2lane_data,
+ 	}, {
+ 		/* sentinel */
+ 	}

+ 46 - 0
target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch

@@ -0,0 +1,46 @@
+From e442e5c1ab6bff5b5460b4fc949beb72aaf77970 Mon Sep 17 00:00:00 2001
+From: eng33 <[email protected]>
+Date: Thu, 5 Dec 2024 18:11:26 +0800
+Subject: [PATCH] arch:arm:boot:dts:overlays: Added waveshare 13.3inch panel
+ support
+
+Signed-off-by: eng33 <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README                          | 2 ++
+ .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts   | 7 +++++++
+ 2 files changed, 9 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5338,6 +5338,8 @@ Params: 2_8_inch                2.8" 480
+         8_0_inch                8.0" 1280x800
+         10_1_inch               10.1" 1280x800
+         11_9_inch               11.9" 320x1480
++        13_3_inch_4lane         13.3" 1920x1080 4lane
++        13_3_inch_2lane         13.3" 1920x1080 2lane
+         i2c1                    Use i2c-1 with jumper wires from GPIOs 2&3
+         disable_touch           Disable the touch controller
+         rotation                Set the panel orientation property
+--- 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
+@@ -51,6 +51,11 @@
+ 				reg = <0x14>;
+ 				compatible = "goodix,gt911";
+ 			};
++
++			touch2: ilitek@41 {
++				compatible = "ilitek,ili251x";
++				reg = <0x41>;
++			};
+ 		};
+ 	};
+ 
+@@ -120,6 +125,8 @@
+ 				   <&touch>, "touchscreen-inverted-x?",
+ 				   <&touch>, "touchscreen-inverted-y?";
+ 		8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel";
++		13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel";
++		13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel";
+ 		i2c1 = <&i2c_frag>, "target:0=",<&i2c1>,
+ 		       <0>, "-3-4+5";
+ 		disable_touch = <&touch>, "status=disabled";

+ 37 - 0
target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch

@@ -0,0 +1,37 @@
+From 166dfc4399643681f2e4277bf7b7407e926861e5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Mon, 9 Dec 2024 14:58:16 +0000
+Subject: [PATCH] fixup! cgroup: Use kernel command line to disable memory
+ cgroup
+
+cgroup features are distinct from cgroup subsystems - handle them
+correctly.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ kernel/cgroup/cgroup.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/kernel/cgroup/cgroup.c
++++ b/kernel/cgroup/cgroup.c
+@@ -6769,11 +6769,19 @@ static int __init cgroup_enable(char *st
+ 			    strcmp(token, ss->legacy_name))
+ 				continue;
+ 
+-			cgroup_feature_disable_mask &= ~(1 << i);
+ 			static_branch_enable(cgroup_subsys_enabled_key[i]);
+ 			pr_info("Enabling %s control group subsystem\n",
+ 				ss->name);
+ 		}
++
++		for (i = 0; i < OPT_FEATURE_COUNT; i++) {
++			if (strcmp(token, cgroup_opt_feature_names[i]))
++				continue;
++			cgroup_feature_disable_mask &= ~(1 << i);
++			pr_info("Enabling %s control group feature\n",
++				cgroup_opt_feature_names[i]);
++			break;
++		}
+ 	}
+ 	return 1;
+ }

+ 31 - 0
target/linux/bcm27xx/patches-6.6/950-1446-media-i2c-ov9282-Correct-the-exposure-offset.patch

@@ -0,0 +1,31 @@
+From e23afbf2c7aae9264322eee8e5c72ca1887606df Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Mon, 9 Dec 2024 10:43:18 +0000
+Subject: [PATCH] media: i2c: ov9282: Correct the exposure offset
+
+The datasheet lists that "Maximum exposure time is frame
+length -25 row periods, where frame length is set by
+registers {0x380E, 0x380F}".
+However this driver had OV9282_EXPOSURE_OFFSET set to 12
+which allowed that restriction to be violated, and would
+result in very under-exposed images.
+
+Correct the offset.
+
+Fixes: 14ea315bbeb7 ("media: i2c: Add ov9282 camera sensor driver")
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/media/i2c/ov9282.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov9282.c
++++ b/drivers/media/i2c/ov9282.c
+@@ -40,7 +40,7 @@
+ /* Exposure control */
+ #define OV9282_REG_EXPOSURE	0x3500
+ #define OV9282_EXPOSURE_MIN	1
+-#define OV9282_EXPOSURE_OFFSET	12
++#define OV9282_EXPOSURE_OFFSET	25
+ #define OV9282_EXPOSURE_STEP	1
+ #define OV9282_EXPOSURE_DEFAULT	0x0282
+ 

+ 22 - 0
target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch

@@ -0,0 +1,22 @@
+From 448a2db3990534810b45d3e4202df96ab2dc5815 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Tue, 10 Dec 2024 15:28:28 +0000
+Subject: [PATCH] Revert "drm/vc4: hvs: Don't write gamma luts on 2711"
+
+This reverts commit 40c77e93cfdda320f47fc1a00a76ce466d20e976.
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -521,9 +521,6 @@ static void vc4_hvs_lut_load(struct vc4_
+ 	if (!drm_dev_enter(drm, &idx))
+ 		return;
+ 
+-	if (hvs->vc4->gen == VC4_GEN_5)  
+-		return;
+-
+ 	/* The LUT memory is laid out with each HVS channel in order,
+ 	 * each of which takes 256 writes for R, 256 for G, then 256
+ 	 * for B.

+ 29 - 0
target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch

@@ -0,0 +1,29 @@
+From 746662562995125ef7fb2c294300b0bd061b1251 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <[email protected]>
+Date: Tue, 10 Dec 2024 16:39:31 +0000
+Subject: [PATCH] Revert "PCI: Warn if no host bridge NUMA node info"
+
+This warning doesn't mean anyting on our platform and
+the warning causes confusion.
+
+See: https://forums.raspberrypi.com/viewtopic.php?p=2276125#p2276125
+
+This reverts commit ad5086108b9f0361929aa9a79cf959ab5681d249.
+
+Signed-off-by: Dom Cobley <[email protected]>
+---
+ drivers/pci/probe.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -968,9 +968,6 @@ static int pci_register_host_bridge(stru
+ 	else
+ 		pr_info("PCI host bridge to bus %s\n", name);
+ 
+-	if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE)
+-		dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n");
+-
+ 	/* Coalesce contiguous windows */
+ 	resource_list_for_each_entry_safe(window, n, &resources) {
+ 		if (list_is_last(&window->node, &resources))

+ 25 - 0
target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch

@@ -0,0 +1,25 @@
+From 7d294fbff4863e53a64685335b30aed9604cae49 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Tue, 19 Nov 2024 16:11:32 +0000
+Subject: [PATCH] drm: bridge: panel: Connector to allow interlaced modes
+
+When initialized from panel_bridge_attach(), connector should
+allow interlaced modes rather than invariably rejecting them,
+so that other components can validate them.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ drivers/gpu/drm/bridge/panel.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/panel.c
++++ b/drivers/gpu/drm/bridge/panel.c
+@@ -82,6 +82,8 @@ static int panel_bridge_attach(struct dr
+ 		return ret;
+ 	}
+ 
++	connector->interlace_allowed = true;
++
+ 	drm_panel_bridge_set_orientation(connector, bridge);
+ 
+ 	drm_connector_attach_encoder(&panel_bridge->connector,

+ 34 - 0
target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch

@@ -0,0 +1,34 @@
+From 2b0acbe8fd008e09a904b7a3c796a2dc79bf10ea Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <[email protected]>
+Date: Tue, 19 Nov 2024 16:17:40 +0000
+Subject: [PATCH] dts: overlays: vc4-kms-dpi-generic-overlay: Add "interlaced"
+ property
+
+Almost no DPI hardware supports it, but it's useful for RP1 video out.
+
+Signed-off-by: Nick Hollinghurst <[email protected]>
+---
+ arch/arm/boot/dts/overlays/README                          | 1 +
+ arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5099,6 +5099,7 @@ Params: clock-frequency         Display
+         vsync-invert            Vertical sync active low
+         de-invert               Data Enable active low
+         pixclk-invert           Negative edge pixel clock
++        interlaced              Use an interlaced mode (where supported)
+         width-mm                Define the screen width in mm
+         height-mm               Define the screen height in mm
+         rgb565                  Change to RGB565 output on GPIOs 0-19
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
+@@ -59,6 +59,7 @@
+ 		vsync-invert = <&timing>, "vsync-active:0=0";
+ 		de-invert = <&timing>, "de-active:0=0";
+ 		pixclk-invert = <&timing>, "pixelclk-active:0=0";
++		interlaced = <&timing>, "interlaced?";
+ 
+ 		width-mm = <&panel_generic>, "width-mm:0";
+ 		height-mm = <&panel_generic>, "height-mm:0";

+ 2 - 2
target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch

@@ -1,8 +1,8 @@
 From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001
 From: Nick Hollinghurst <[email protected]>
 Date: Tue, 10 Dec 2024 13:23:09 +0000
-Subject: [PATCH 1456/1482] drm: rp1: rp1-dpi: Add interlaced modes and PIO
- program to fix VSYNC
+Subject: [PATCH] drm: rp1: rp1-dpi: Add interlaced modes and PIO program to
+ fix VSYNC
 
 Implement interlaced modes by wobbling the base pointer and VFP width
 for every field. This results in correct pixels but incorrect VSYNC.

+ 0 - 22
target/linux/bcm27xx/patches-6.6/950-1457-pwm-Improve-PWM_PIO_RP1-dependencies.patch

@@ -1,22 +0,0 @@
-From f85f3509692f966ec32e4db499f7e64dc6b6b952 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <[email protected]>
-Date: Thu, 12 Dec 2024 10:09:13 +0000
-Subject: [PATCH 1457/1482] pwm: Improve PWM_PIO_RP1 dependencies
-
-PWM_PIO_RP1 should select RP1_PIO, as it is useless without it.
-
-Signed-off-by: Phil Elwell <[email protected]>
----
- drivers/pwm/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/pwm/Kconfig
-+++ b/drivers/pwm/Kconfig
-@@ -457,6 +457,7 @@ config PWM_PCA9685
- config PWM_PIO_RP1
- 	tristate "RP1 PIO PWM support"
- 	depends on FIRMWARE_RP1 || COMPILE_TEST
-+	select RP1_PIO
- 	help
- 	  This is a PWM framework driver for Raspberry Pi 5, using the PIO
- 	  hardware of RP1 to provide PWM functionality. Supports up to 4

+ 0 - 20
target/linux/bcm27xx/patches-6.6/950-1458-Revert-pwm-Improve-PWM_PIO_RP1-dependencies.patch

@@ -1,20 +0,0 @@
-From 73fb1e979a210094935f4af4c3d6e700fba30c5f Mon Sep 17 00:00:00 2001
-From: Phil Elwell <[email protected]>
-Date: Thu, 12 Dec 2024 10:28:54 +0000
-Subject: [PATCH 1458/1482] Revert "pwm: Improve PWM_PIO_RP1 dependencies"
-
-This reverts commit f85f3509692f966ec32e4db499f7e64dc6b6b952.
----
- drivers/pwm/Kconfig | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/pwm/Kconfig
-+++ b/drivers/pwm/Kconfig
-@@ -457,7 +457,6 @@ config PWM_PCA9685
- config PWM_PIO_RP1
- 	tristate "RP1 PIO PWM support"
- 	depends on FIRMWARE_RP1 || COMPILE_TEST
--	select RP1_PIO
- 	help
- 	  This is a PWM framework driver for Raspberry Pi 5, using the PIO
- 	  hardware of RP1 to provide PWM functionality. Supports up to 4

+ 79 - 0
target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch

@@ -0,0 +1,79 @@
+From ac0cd73932aa1e371ffaf0b974855ed3cd22937f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Wed, 11 Dec 2024 13:47:30 +0000
+Subject: [PATCH] ASoC: allo-piano-dac-plus: Fix volume limit locking
+
+Calling snd_soc_limit_volume from within a kcontrol put handler seems
+to cause a deadlock as it attempts to claim a write lock that is already
+held. Call snd_soc_limit_volume from the main initialisation code
+instead, to avoid the recursive locking.
+
+See: https://github.com/raspberrypi/linux/issues/6527
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/bcm/allo-piano-dac-plus.c | 32 +++++++++++++----------------
+ 1 file changed, 14 insertions(+), 18 deletions(-)
+
+--- a/sound/soc/bcm/allo-piano-dac-plus.c
++++ b/sound/soc/bcm/allo-piano-dac-plus.c
+@@ -452,14 +452,6 @@ static int pcm512x_set_reg_sub(struct sn
+ 
+ 	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ 
+-	if (digital_gain_0db_limit) {
+-		ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
+-					207);
+-		if (ret < 0)
+-			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+-				ret);
+-	}
+-
+ 	// When in Dual Mono, Sub vol control should not set anything.
+ 	if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode
+ 
+@@ -562,14 +554,6 @@ static int pcm512x_set_reg_master(struct
+ 
+ 	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+ 
+-	if (digital_gain_0db_limit) {
+-		ret = snd_soc_limit_volume(card, "Master Playback Volume",
+-					207);
+-		if (ret < 0)
+-			dev_warn(card->dev, "Failed to set volume limit: %d\n",
+-				ret);
+-	}
+-
+ 	if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode
+ 
+ 		ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+@@ -750,6 +734,18 @@ static int snd_allo_piano_dac_init(struc
+ 	if (digital_gain_0db_limit) {
+ 		int ret;
+ 
++		ret = snd_soc_limit_volume(card, "Master Playback Volume",
++					207);
++		if (ret < 0)
++			dev_warn(card->dev, "Failed to set master volume limit: %d\n",
++				ret);
++
++		ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
++					207);
++		if (ret < 0)
++			dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n",
++				ret);
++
+ 		//Set volume limit on both dacs
+ 		for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) {
+ 			char cname[256];
+@@ -757,8 +753,8 @@ static int snd_allo_piano_dac_init(struc
+ 			sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]);
+ 			ret = snd_soc_limit_volume(card, cname, 207);
+ 			if (ret < 0)
+-				dev_warn(card->dev, "Failed to set volume limit: %d\n",
+-					ret);
++				dev_warn(card->dev, "Failed to set %s volume limit: %d\n",
++					 cname, ret);
+ 		}
+ 	}
+ 

+ 30 - 0
target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch

@@ -0,0 +1,30 @@
+From af4ab4fb77dfc697c8ae068b18f27de1ee5d609f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Wed, 11 Dec 2024 16:30:43 +0000
+Subject: [PATCH] drm: vc4: txp: Do not allow 24bpp formats when transposing
+
+The hardware doesn't support transposing to 24bpp (RGB888/BGR888)
+formats. There's no way to advertise this through DRM, so block
+it from atomic_check instead.
+
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -272,6 +272,13 @@ static int vc4_txp_connector_atomic_chec
+ 		return -EINVAL;
+ 	}
+ 
++	if (conn_state->rotation & DRM_MODE_TRANSPOSE &&
++	    (fb->format->format == DRM_FORMAT_RGB888 ||
++	     fb->format->format == DRM_FORMAT_BGR888)) {
++		DRM_DEBUG_KMS("24bpp formats not supported when transposing\n");
++		return -EINVAL;
++	}
++
+ 	for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) {
+ 		if (fb->format->format == drm_fmts[i])
+ 			break;

+ 29 - 0
target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch

@@ -0,0 +1,29 @@
+From 0b216b3988e5b7035cd5ed8a9910eacbb3420ce0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <[email protected]>
+Date: Thu, 12 Dec 2024 11:59:52 +0000
+Subject: [PATCH] drm: Validate connector rotation has one bit set in the
+ rotation property
+
+Copy the same validation logic as from the plane rotation property.
+
+Fixes: 8fec3ff87049 ("drm: Add a rotation parameter to connectors.")
+Signed-off-by: Dave Stevenson <[email protected]>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -812,6 +812,12 @@ static int drm_atomic_connector_set_prop
+ 	} else if (property == connector->privacy_screen_sw_state_property) {
+ 		state->privacy_screen_sw_state = val;
+ 	} else if (property == connector->rotation_property) {
++		if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) {
++			drm_dbg_atomic(connector->dev,
++				       "[CONNECTOR:%d:%s] bad rotation bitmask: 0x%llx\n",
++				       connector->base.id, connector->name, val);
++			return -EINVAL;
++		}
+ 		state->rotation = val;
+ 	} else if (connector->funcs->atomic_set_property) {
+ 		return connector->funcs->atomic_set_property(connector,

+ 73 - 0
target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch

@@ -0,0 +1,73 @@
+From 61494a7aa2ea887fa1cd1399a8db1317c87f661b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <[email protected]>
+Date: Thu, 12 Dec 2024 13:05:41 +0000
+Subject: [PATCH] ASoC: allo-piano-dac-plus: Suppress -517 errors
+
+Use dev_err_probe to simplify the code and suppress EPROBE_DEFER errors.
+
+Signed-off-by: Phil Elwell <[email protected]>
+---
+ sound/soc/bcm/allo-piano-dac-plus.c | 37 ++++++++---------------------
+ 1 file changed, 10 insertions(+), 27 deletions(-)
+
+--- a/sound/soc/bcm/allo-piano-dac-plus.c
++++ b/sound/soc/bcm/allo-piano-dac-plus.c
+@@ -974,48 +974,31 @@ static int snd_allo_piano_dac_probe(stru
+ 
+ 		allo_piano_2_1_codecs[0].of_node =
+ 			of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+-		if (!allo_piano_2_1_codecs[0].of_node) {
+-			dev_err(&pdev->dev,
+-				"Property 'audio-codec' missing or invalid\n");
+-			return -EINVAL;
+-		}
+-
+ 		allo_piano_2_1_codecs[1].of_node =
+ 			of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
+-		if (!allo_piano_2_1_codecs[1].of_node) {
+-			dev_err(&pdev->dev,
++		if (!allo_piano_2_1_codecs[0].of_node || !allo_piano_2_1_codecs[1].of_node)
++			return dev_err_probe(&pdev->dev, -EINVAL,
+ 				"Property 'audio-codec' missing or invalid\n");
+-			return -EINVAL;
+-		}
+ 
+ 		mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1",
+ 							GPIOD_OUT_LOW);
+-		if (IS_ERR(mute_gpio[0])) {
+-			ret = PTR_ERR(mute_gpio[0]);
+-			dev_err(&pdev->dev,
+-				"failed to get mute1 gpio6: %d\n", ret);
+-			return ret;
+-		}
++		if (IS_ERR(mute_gpio[0]))
++			return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[0]),
++				"failed to get mute1 gpio\n");
+ 
+ 		mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2",
+ 							GPIOD_OUT_LOW);
+-		if (IS_ERR(mute_gpio[1])) {
+-			ret = PTR_ERR(mute_gpio[1]);
+-			dev_err(&pdev->dev,
+-				"failed to get mute2 gpio25: %d\n", ret);
+-			return ret;
+-		}
++		if (IS_ERR(mute_gpio[1]))
++			return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[1]),
++				"failed to get mute2 gpio\n");
+ 
+ 		if (mute_gpio[0] && mute_gpio[1])
+ 			snd_allo_piano_dac.set_bias_level =
+ 				snd_allo_piano_set_bias_level;
+ 
+ 		ret = snd_soc_register_card(&snd_allo_piano_dac);
+-		if (ret < 0) {
+-			dev_err(&pdev->dev,
+-				"snd_soc_register_card() failed: %d\n", ret);
+-			return ret;
+-		}
++		if (ret < 0)
++			return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
+ 
+ 		if ((mute_gpio[0]) && (mute_gpio[1]))
+ 			snd_allo_piano_gpio_mute(&snd_allo_piano_dac);

+ 1 - 2
target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch

@@ -1,8 +1,7 @@
 From 80533a952218696c0ef1b346bab50dc401e6b74c Mon Sep 17 00:00:00 2001
 From: Nick Hollinghurst <[email protected]>
 Date: Thu, 12 Dec 2024 11:58:12 +0000
-Subject: [PATCH 1463/1482] drm: rp1: rp1-dpi: Fix optional dependency on
- RP1_PIO
+Subject: [PATCH] drm: rp1: rp1-dpi: Fix optional dependency on RP1_PIO
 
 Add optional dependency to Kconfig, and conditionally compile
 PIO-dependent code. Add a mode validation function to reject

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików