CONFIG_FONT_8x16=y
CONFIG_FONT_8x8=y
CONFIG_FONT_SUPPORT=y
+CONFIG_FORCE_NR_CPUS=y
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_DMA_CMA=y
CONFIG_DMA_DIRECT_REMAP=y
CONFIG_DMA_ENGINE=y
+# CONFIG_DMA_NUMA_CMA is not set
CONFIG_DMA_OF=y
CONFIG_DMA_SHARED_BUFFER=y
CONFIG_DMA_VIRTUAL_CHANNELS=y
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y
CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_ARCH_NUMA=y
+CONFIG_GENERIC_ARCH_NUMA_EMULATION=y
CONFIG_GENERIC_ARCH_TOPOLOGY=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
# CONFIG_MTD is not set
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
+CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NET_EGRESS=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_XGRESS=y
CONFIG_NLS=y
CONFIG_NLS_ASCII=y
+CONFIG_NODES_SHIFT=4
CONFIG_NOP_USB_XCEIV=y
CONFIG_NO_HZ=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
+CONFIG_NUMA=y
+# CONFIG_NUMA_BALANCING is not set
+CONFIG_NUMA_EMULATION=y
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_NVMEM_RASPBERRYPI_OTP=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
+CONFIG_OF_NUMA=y
CONFIG_OF_OVERLAY=y
CONFIG_OF_RESOLVE=y
CONFIG_PADATA=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_PCI=y
CONFIG_USB_XHCI_PLATFORM=y
+CONFIG_USE_PERCPU_NUMA_NODE_ID=y
CONFIG_VCHIQ_CDEV=y
CONFIG_VIDEO_CMDLINE=y
CONFIG_VIDEO_DEV=y
CONFIG_DMA_CMA=y
CONFIG_DMA_DIRECT_REMAP=y
CONFIG_DMA_ENGINE=y
+# CONFIG_DMA_NUMA_CMA is not set
CONFIG_DMA_OF=y
CONFIG_DMA_OPS=y
CONFIG_DMA_SHARED_BUFFER=y
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y
CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_ARCH_NUMA=y
+CONFIG_GENERIC_ARCH_NUMA_EMULATION=y
CONFIG_GENERIC_ARCH_TOPOLOGY=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
+CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
CONFIG_NEED_SG_DMA_FLAGS=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NET_EGRESS=y
CONFIG_NET_XGRESS=y
CONFIG_NLS=y
CONFIG_NLS_ASCII=y
+CONFIG_NODES_SHIFT=4
CONFIG_NOP_USB_XCEIV=y
CONFIG_NO_HZ=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
+CONFIG_NUMA=y
+# CONFIG_NUMA_BALANCING is not set
+CONFIG_NUMA_EMULATION=y
CONFIG_NVMEM=y
CONFIG_NVMEM_LAYOUTS=y
CONFIG_NVMEM_RASPBERRYPI_OTP=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
+CONFIG_OF_NUMA=y
CONFIG_OF_OVERLAY=y
CONFIG_OF_RESOLVE=y
CONFIG_PADATA=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_PCI=y
CONFIG_USB_XHCI_PLATFORM=y
+CONFIG_USE_PERCPU_NUMA_NODE_ID=y
CONFIG_VCHIQ_CDEV=y
CONFIG_VIDEO_CMDLINE=y
CONFIG_VIDEO_DEV=y
define KernelPackage/sound-soc-rpi-simple-soundcard
TITLE:=Support for Raspberry Pi simple soundcards
KCONFIG:= \
- CONFIG_SND_RPI_SIMPLE_SOUNDCARD
+ CONFIG_SND_RPI_SIMPLE_SOUNDCARD \
+ CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC8X
FILES:= \
$(LINUX_DIR)/sound/soc/bcm/snd-soc-rpi-simple-soundcard.ko
AUTOLOAD:=$(call AutoLoad,68,snd-soc-rpi-simple-soundcard)
$(eval $(call KernelPackage,sound-soc-googlevoicehat))
+define KernelPackage/sound-soc-hifiberry-adc
+ TITLE:=Support for HifiBerry ADC
+ KCONFIG:= \
+ CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC \
+ CONFIG_SND_RPI_HIFIBERRY_ADC \
+ CONFIG_SND_SOC_PCM186X_I2C
+ FILES:= \
+ $(LINUX_DIR)/sound/soc/bcm/snd-soc-hifiberry-adc.ko \
+ $(LINUX_DIR)/sound/soc/codecs/snd-soc-pcm186x.ko \
+ $(LINUX_DIR)/sound/soc/codecs/snd-soc-pcm186x-i2c.ko
+ AUTOLOAD:=$(call AutoLoad,68,snd-soc-pcm186x snd-soc-pcm186x-i2c \
+ snd-soc-hifiberry-adc)
+ DEPENDS:= \
+ kmod-sound-soc-bcm2835-i2s \
+ +kmod-i2c-bcm2835 \
+ +kmod-regmap-i2c
+ $(call AddDepends/sound)
+endef
+
+define KernelPackage/sound-soc-hifiberry-adc/description
+ This package contains support for HifiBerry ADC
+endef
+
+$(eval $(call KernelPackage,sound-soc-hifiberry-adc))
+
+
define KernelPackage/sound-soc-hifiberry-dac
TITLE:=Support for HifiBerry DAC
KCONFIG:= \
+++ /dev/null
-From eaeca896d077e9e42866f7f7caae7b62211a0d0d Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Mon, 1 Mar 2021 09:12:44 +0000
-Subject: [PATCH 0058/1085] Revert "Bluetooth: Always request for user
- confirmation for Just Works (LE SC)"
-
-This reverts commit ffee202a78c2980688bc5d2f7d56480e69a5e0c9.
-
-The commit "Bluetooth: Always request for user confirmation for Just
-Works" prevents BLE devices pairing in (at least) the Raspberry Pi OS
-GUI. After reverting it, pairing works again. Although this companion
-commit ("... (LE SC)") has not been demonstrated to be problematic,
-it follows the same logic and therefore could affect some use cases.
-
-If another solution to the problem is found then this reversion will
-be removed.
-
-See: https://github.com/raspberrypi/linux/issues/4139
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- net/bluetooth/smp.c | 5 +----
- 1 file changed, 1 insertion(+), 4 deletions(-)
-
---- a/net/bluetooth/smp.c
-+++ b/net/bluetooth/smp.c
-@@ -2215,7 +2215,7 @@ mackey_and_ltk:
- if (err)
- return SMP_UNSPECIFIED;
-
-- if (smp->method == REQ_OOB) {
-+ if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
- if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) {
- sc_dhkey_check(smp);
- SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
-@@ -2230,9 +2230,6 @@ mackey_and_ltk:
- confirm_hint = 0;
-
- confirm:
-- if (smp->method == JUST_WORKS)
-- confirm_hint = 1;
--
- err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
- hcon->dst_type, passkey, confirm_hint);
- if (err)
+++ /dev/null
-From 2f223e0e4931486fbc32df3c89bc16ff1ca434bf Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Mon, 29 Nov 2021 12:14:49 +0000
-Subject: [PATCH 0320/1085] spi: spidev: Restore loading from Device Tree
-
-As happens occasionally, an upstream change has once again prevented
-spidev from being loaded via Device Tree. We now need "spidev" to be
-included in the new spi_device_id list, otherwise although the
-spidev driver gets loaded no /dev/spidev*.* entries will appear.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/spi/spidev.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/spi/spidev.c
-+++ b/drivers/spi/spidev.c
-@@ -699,6 +699,7 @@ static const struct file_operations spid
- static struct class *spidev_class;
-
- static const struct spi_device_id spidev_spi_ids[] = {
-+ { .name = "spidev" },
- { .name = "bh2228fv" },
- { .name = "dh2228fv" },
- { .name = "jg10309-01" },
+++ /dev/null
-From c7b98a63328a749d44b7580ee9baafc5d417e48f Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Thu, 31 Aug 2023 11:45:38 +0100
-Subject: [PATCH 0635/1085] drm/vc4: Assign LBM memory during atomic_flush.
-
-Avoid double buffering LBM allocations by making the
-allocation a single alloc per crtc at atomic_flush.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c | 2 +-
- drivers/gpu/drm/vc4/vc4_drv.h | 8 ++--
- drivers/gpu/drm/vc4/vc4_hvs.c | 47 ++++++++++++++++++-
- drivers/gpu/drm/vc4/vc4_plane.c | 38 +++------------
- 4 files changed, 58 insertions(+), 37 deletions(-)
-
---- a/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
-+++ b/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
-@@ -248,7 +248,7 @@ static void drm_vc4_test_vc4_lbm_size(st
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
-
-- KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm.size, params->expected_lbm_size);
-+ KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm_size, params->expected_lbm_size);
-
- for (i = 0; i < 2; i++) {
- KUNIT_EXPECT_EQ(test,
---- a/drivers/gpu/drm/vc4/vc4_drv.h
-+++ b/drivers/gpu/drm/vc4/vc4_drv.h
-@@ -417,6 +417,8 @@ struct vc4_plane_state {
- u32 dlist_size; /* Number of dwords allocated for the display list */
- u32 dlist_count; /* Number of used dwords in the display list. */
-
-+ u32 lbm_size; /* LBM requirements for this plane */
-+
- /* Offset in the dlist to various words, for pageflip or
- * cursor updates.
- */
-@@ -442,9 +444,6 @@ struct vc4_plane_state {
- bool is_unity;
- bool is_yuv;
-
-- /* Our allocation in LBM for temporary storage during scaling. */
-- struct drm_mm_node lbm;
--
- /* Our allocation in UPM for prefetching. */
- struct drm_mm_node upm[DRM_FORMAT_MAX_PLANES];
-
-@@ -635,6 +634,9 @@ struct vc4_crtc {
- * access to that value.
- */
- unsigned int current_hvs_channel;
-+
-+ /* @lbm: Our allocation in LBM for temporary storage during scaling. */
-+ struct drm_mm_node lbm;
- };
-
- #define to_vc4_crtc(_crtc) \
---- a/drivers/gpu/drm/vc4/vc4_hvs.c
-+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
-@@ -1103,6 +1103,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
- struct drm_plane *plane;
- const struct drm_plane_state *plane_state;
- u32 dlist_count = 0;
-+ u32 lbm_count = 0;
-
- /* The pixelvalve can only feed one encoder (and encoders are
- * 1:1 with connectors.)
-@@ -1111,6 +1112,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
- return -EINVAL;
-
- drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) {
-+ const struct vc4_plane_state *vc4_plane_state =
-+ to_vc4_plane_state(plane_state);
- u32 plane_dlist_count = vc4_plane_dlist_size(plane_state);
-
- drm_dbg_driver(dev, "[CRTC:%d:%s] Found [PLANE:%d:%s] with DLIST size: %u\n",
-@@ -1119,6 +1122,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
- plane_dlist_count);
-
- dlist_count += plane_dlist_count;
-+ lbm_count += vc4_plane_state->lbm_size;
- }
-
- dlist_count++; /* Account for SCALER_CTL0_END. */
-@@ -1132,6 +1136,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
-
- vc4_state->mm = alloc;
-
-+ /* FIXME: Check total lbm allocation here */
-+
- return vc4_hvs_gamma_check(crtc, state);
- }
-
-@@ -1246,7 +1252,10 @@ void vc4_hvs_atomic_flush(struct drm_crt
- bool debug_dump_regs = false;
- bool enable_bg_fill = false;
- u32 __iomem *dlist_start, *dlist_next;
-+ unsigned long irqflags;
- unsigned int zpos = 0;
-+ u32 lbm_offset = 0;
-+ u32 lbm_size = 0;
- bool found = false;
- int idx;
-
-@@ -1265,6 +1274,35 @@ void vc4_hvs_atomic_flush(struct drm_crt
- vc4_hvs_dump_state(hvs);
- }
-
-+ drm_atomic_crtc_for_each_plane(plane, crtc) {
-+ vc4_plane_state = to_vc4_plane_state(plane->state);
-+ lbm_size += vc4_plane_state->lbm_size;
-+ }
-+
-+ if (drm_mm_node_allocated(&vc4_crtc->lbm)) {
-+ spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
-+ drm_mm_remove_node(&vc4_crtc->lbm);
-+ spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
-+ }
-+
-+ if (lbm_size) {
-+ int ret;
-+
-+ spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
-+ ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
-+ &vc4_crtc->lbm,
-+ lbm_size, 1,
-+ 0, 0);
-+ spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
-+
-+ if (ret) {
-+ pr_err("Failed to allocate LBM ret %d\n", ret);
-+ return;
-+ }
-+ }
-+
-+ lbm_offset = vc4_crtc->lbm.start;
-+
- dlist_start = vc4->hvs->dlist + vc4_state->mm->mm_node.start;
- dlist_next = dlist_start;
-
-@@ -1276,6 +1314,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
- if (plane->state->normalized_zpos != zpos)
- continue;
-
-+ vc4_plane_state = to_vc4_plane_state(plane->state);
-+
- /* Is this the first active plane? */
- if (dlist_next == dlist_start) {
- /* We need to enable background fill when a plane
-@@ -1286,10 +1326,15 @@ void vc4_hvs_atomic_flush(struct drm_crt
- * already needs it or all planes on top blend from
- * the first or a lower plane.
- */
-- vc4_plane_state = to_vc4_plane_state(plane->state);
- enable_bg_fill = vc4_plane_state->needs_bg_fill;
- }
-
-+ if (vc4_plane_state->lbm_size) {
-+ vc4_plane_state->dlist[vc4_plane_state->lbm_offset] =
-+ lbm_offset;
-+ lbm_offset += vc4_plane_state->lbm_size;
-+ }
-+
- dlist_next += vc4_plane_write_dlist(plane, dlist_next);
-
- found = true;
---- a/drivers/gpu/drm/vc4/vc4_plane.c
-+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -288,7 +288,6 @@ struct drm_plane_state *vc4_plane_duplic
- if (!vc4_state)
- return NULL;
-
-- memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
- memset(&vc4_state->upm, 0, sizeof(vc4_state->upm));
-
- for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++)
-@@ -320,14 +319,6 @@ void vc4_plane_destroy_state(struct drm_
- struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
- unsigned int i;
-
-- if (drm_mm_node_allocated(&vc4_state->lbm)) {
-- unsigned long irqflags;
--
-- spin_lock_irqsave(&hvs->mm_lock, irqflags);
-- drm_mm_remove_node(&vc4_state->lbm);
-- spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
-- }
--
- for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) {
- unsigned long irqflags;
-
-@@ -903,12 +894,13 @@ static int vc4_plane_allocate_lbm(struct
- struct vc4_dev *vc4 = to_vc4_dev(drm);
- struct drm_plane *plane = state->plane;
- struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
-- unsigned long irqflags;
- u32 lbm_size;
-
- lbm_size = vc4_lbm_size(state);
-- if (!lbm_size)
-+ if (!lbm_size) {
-+ vc4_state->lbm_size = 0;
- return 0;
-+ }
-
- /*
- * NOTE: BCM2712 doesn't need to be aligned, since the size
-@@ -925,28 +917,10 @@ static int vc4_plane_allocate_lbm(struct
- if (WARN_ON(!vc4_state->lbm_offset))
- return -EINVAL;
-
-- /* Allocate the LBM memory that the HVS will use for temporary
-- * storage due to our scaling/format conversion.
-+ /* FIXME: Add loop here that ensures that the total LBM assigned in this
-+ * state is less than the total lbm size
- */
-- if (!drm_mm_node_allocated(&vc4_state->lbm)) {
-- int ret;
--
-- spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
-- ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
-- &vc4_state->lbm,
-- lbm_size, 1,
-- 0, 0);
-- spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
--
-- if (ret) {
-- drm_err(drm, "Failed to allocate LBM entry: %d\n", ret);
-- return ret;
-- }
-- } else {
-- WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
-- }
--
-- vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start;
-+ vc4_state->lbm_size = lbm_size;
-
- return 0;
- }
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
-@@ -1349,27 +1349,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
+@@ -1304,27 +1304,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
WARN_ON(!vc4_state->mm);
WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm->mm_node.size);
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1447,9 +1447,9 @@ static int vc4_plane_mode_set(struct drm
+@@ -1473,9 +1473,9 @@ static int vc4_plane_mode_set(struct drm
vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
for (i = 0; i < num_planes; i++) {
}
/* Pointer Context Word 0/1/2: Written by the HVS */
-@@ -1842,9 +1842,8 @@ static int vc6_plane_mode_set(struct drm
+@@ -1868,9 +1868,8 @@ static int vc6_plane_mode_set(struct drm
* TODO: This only covers Raster Scan Order planes
*/
for (i = 0; i < num_planes; i++) {
/* The filter kernel is composed of dwords each containing 3 9-bit
* signed integers packed next to each other.
*/
-@@ -1596,6 +1626,8 @@ int vc4_hvs_debugfs_init(struct drm_mino
+@@ -1551,6 +1581,8 @@ int vc4_hvs_debugfs_init(struct drm_mino
drm_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun, NULL);
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1108,6 +1108,12 @@ static int vc4_plane_mode_set(struct drm
+@@ -1134,6 +1134,12 @@ static int vc4_plane_mode_set(struct drm
width = vc4_state->src_w[0] >> 16;
height = vc4_state->src_h[0] >> 16;
/* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
* and 4:4:4, scl1 should be set to scl0 so both channels of
* the scaler do the same thing. For YUV, the Y plane needs
-@@ -1623,6 +1629,12 @@ static int vc6_plane_mode_set(struct drm
+@@ -1649,6 +1655,12 @@ static int vc6_plane_mode_set(struct drm
width = vc4_state->src_w[0] >> 16;
height = vc4_state->src_h[0] >> 16;
/* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
* and 4:4:4, scl1 should be set to scl0 so both channels of
* the scaler do the same thing. For YUV, the Y plane needs
-@@ -1994,6 +2006,9 @@ int vc4_plane_atomic_check(struct drm_pl
+@@ -2020,6 +2032,9 @@ int vc4_plane_atomic_check(struct drm_pl
if (ret)
return ret;
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
-@@ -667,6 +667,7 @@ struct vc4_hvs_dlist_allocation {
+@@ -665,6 +665,7 @@ struct vc4_hvs_dlist_allocation {
struct drm_mm_node mm_node;
unsigned int channel;
u8 target_frame_count;
spin_lock_irqsave(&hvs->mm_lock, flags);
vc4_hvs_free_dlist_entry_locked(hvs, alloc);
spin_unlock_irqrestore(&hvs->mm_lock, flags);
-@@ -1201,6 +1204,7 @@ static void vc4_hvs_install_dlist(struct
+@@ -1195,6 +1198,7 @@ static void vc4_hvs_install_dlist(struct
return;
WARN_ON(!vc4_state->mm);
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
-@@ -549,6 +549,7 @@ struct vc4_crtc_data {
+@@ -550,6 +550,7 @@ struct vc4_crtc_data {
struct vc4_txp_data {
struct vc4_crtc_data base;
enum vc4_encoder_type encoder_type;
unsigned int irq;
-@@ -714,6 +715,12 @@ struct vc4_crtc_state {
+@@ -712,6 +713,12 @@ struct vc4_crtc_state {
writel(val, hvs->regs + (offset)); \
} while (0)
out:
drm_dev_exit(idx);
-@@ -1227,8 +1286,8 @@ static void vc4_hvs_install_dlist(struct
+@@ -1221,8 +1280,8 @@ static void vc4_hvs_install_dlist(struct
if (vc4->gen >= VC4_GEN_6)
HVS_WRITE(SCALER6_DISPX_LPTRS(vc4_state->assigned_channel),
else
HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
vc4_state->mm->mm_node.start);
-@@ -1427,11 +1486,11 @@ void vc4_hvs_atomic_flush(struct drm_crt
+@@ -1382,11 +1441,11 @@ void vc4_hvs_atomic_flush(struct drm_crt
if (enable_bg_fill)
HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
} else {
/* we can actually run with a lower core clock when background
* fill is enabled on VC4_GEN_5 so leave it enabled always.
-@@ -1701,7 +1760,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+@@ -1656,7 +1715,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
* access a register. Use a plausible size then.
*/
if (!kunit_get_current_test())
else
dlist_size = 4096;
-@@ -1935,14 +1994,17 @@ static int vc6_hvs_hw_init(struct vc4_hv
+@@ -1890,14 +1949,17 @@ static int vc6_hvs_hw_init(struct vc4_hv
const struct vc6_csc_coeff_entry *coeffs;
unsigned int i;
for (i = 0; i < 6; i++) {
coeffs = &csc_coeffs[i / 3][i % 3];
-@@ -2041,21 +2103,21 @@ static int vc4_hvs_cob_init(struct vc4_h
+@@ -1996,21 +2058,21 @@ static int vc4_hvs_cob_init(struct vc4_h
reg = 0;
top = 3840;
VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
break;
-@@ -2086,7 +2148,10 @@ static int vc4_hvs_bind(struct device *d
+@@ -2041,7 +2103,10 @@ static int vc4_hvs_bind(struct device *d
hvs->regset.base = hvs->regs;
hvs->regset.regs = vc6_hvs_regs;
hvs->regset.nregs = ARRAY_SIZE(vc6_hvs_regs);
} else {
-@@ -2253,6 +2318,7 @@ static void vc4_hvs_dev_remove(struct pl
+@@ -2208,6 +2273,7 @@ static void vc4_hvs_dev_remove(struct pl
static const struct of_device_id vc4_hvs_dt_match[] = {
{ .compatible = "brcm,bcm2711-hvs" },
{ .compatible = "brcm,bcm2712-hvs" },
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
-@@ -1933,6 +1933,17 @@ static int vc4_hvs_hw_init(struct vc4_hv
+@@ -1888,6 +1888,17 @@ static int vc4_hvs_hw_init(struct vc4_hv
#define CFC1_N_MA_CSC_COEFF_C23(x) (0xa03c + ((x) * 0x3000))
#define CFC1_N_MA_CSC_COEFF_C24(x) (0xa040 + ((x) * 0x3000))
/* 4 S2.22 multiplication factors, and 1 S9.15 addititive element for each of 3
* output components
*/
-@@ -2003,31 +2014,43 @@ static int vc6_hvs_hw_init(struct vc4_hv
+@@ -1958,31 +1969,43 @@ static int vc6_hvs_hw_init(struct vc4_hv
HVS_WRITE(SCALER6(PRI_MAP0), 0xffffffff);
HVS_WRITE(SCALER6(PRI_MAP1), 0xffffffff);
return 0;
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1054,6 +1054,12 @@ static u32 vc4_hvs5_get_alpha_blend_mode
+@@ -1080,6 +1080,12 @@ static u32 vc4_hvs5_get_alpha_blend_mode
WARN_ON_ONCE(vc4->gen != VC4_GEN_5 && vc4->gen != VC4_GEN_6);
if (!state->fb->format->has_alpha)
return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE);
-@@ -1569,14 +1575,13 @@ static int vc4_plane_mode_set(struct drm
+@@ -1595,14 +1601,13 @@ static int vc4_plane_mode_set(struct drm
static u32 vc6_plane_get_csc_mode(struct vc4_plane_state *vc4_state)
{
struct drm_plane_state *state = &vc4_state->base;
/* CSC pre-loaded with:
* 0 = BT601 limited range
* 1 = BT709 limited range
-@@ -1590,8 +1595,15 @@ static u32 vc6_plane_get_csc_mode(struct
+@@ -1616,8 +1621,15 @@ static u32 vc6_plane_get_csc_mode(struct
if (color_range > DRM_COLOR_YCBCR_FULL_RANGE)
color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1877,7 +1877,7 @@ static int vc6_plane_mode_set(struct drm
+@@ -1903,7 +1903,7 @@ static int vc6_plane_mode_set(struct drm
* The UPM buffer will be allocated in
* vc6_plane_allocate_upm().
*/
SCALER6_PTR0_UPPER_ADDR));
/* Pointer Word 1 */
-@@ -2079,7 +2079,8 @@ void vc4_plane_async_set_fb(struct drm_p
+@@ -2105,7 +2105,8 @@ void vc4_plane_async_set_fb(struct drm_p
{
struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
int idx;
if (!drm_dev_enter(plane->dev, &idx))
-@@ -2089,19 +2090,38 @@ void vc4_plane_async_set_fb(struct drm_p
+@@ -2115,19 +2116,38 @@ void vc4_plane_async_set_fb(struct drm_p
* because this is only called on the primary plane.
*/
WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -2205,11 +2205,15 @@ static int vc4_plane_atomic_async_check(
+@@ -2231,11 +2231,15 @@ static int vc4_plane_atomic_async_check(
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1114,7 +1114,7 @@ static int vc4_plane_mode_set(struct drm
+@@ -1140,7 +1140,7 @@ static int vc4_plane_mode_set(struct drm
width = vc4_state->src_w[0] >> 16;
height = vc4_state->src_h[0] >> 16;
/* 0 source size probably means the plane is offscreen */
vc4_state->dlist_initialized = 1;
return 0;
-@@ -1641,8 +1641,10 @@ static int vc6_plane_mode_set(struct drm
+@@ -1667,8 +1667,10 @@ static int vc6_plane_mode_set(struct drm
width = vc4_state->src_w[0] >> 16;
height = vc4_state->src_h[0] >> 16;
vc4_state->dlist_initialized = 1;
return 0;
}
-@@ -2018,7 +2020,8 @@ int vc4_plane_atomic_check(struct drm_pl
+@@ -2044,7 +2046,8 @@ int vc4_plane_atomic_check(struct drm_pl
if (ret)
return ret;
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -1079,6 +1079,21 @@ static u32 vc4_hvs5_get_alpha_blend_mode
+@@ -1105,6 +1105,21 @@ static u32 vc4_hvs5_get_alpha_blend_mode
}
}
/* Writes out a full display list for an active plane to the plane's
* private dlist state.
*/
-@@ -1824,7 +1839,7 @@ static int vc6_plane_mode_set(struct drm
+@@ -1850,7 +1865,7 @@ static int vc6_plane_mode_set(struct drm
vc4_dlist_write(vc4_state,
SCALER6_CTL0_VALID |
VC4_SET_FIELD(tiling, SCALER6_CTL0_ADDR_MODE) |
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
-@@ -733,7 +733,7 @@ static unsigned int vc4_lbm_channel_size
+@@ -742,7 +742,7 @@ static unsigned int vc4_lbm_channel_size
if (!components)
return 0;
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
-@@ -1406,7 +1406,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+@@ -1400,7 +1400,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
struct drm_plane *plane;
struct vc4_plane_state *vc4_plane_state;
bool debug_dump_regs = false;
- bool enable_bg_fill = false;
+ bool enable_bg_fill = true;
u32 __iomem *dlist_start, *dlist_next;
- unsigned long irqflags;
unsigned int zpos = 0;
+ bool found = false;
+++ /dev/null
-From 43730828eca754b4b527d79fc5a1d3ff50c50481 Mon Sep 17 00:00:00 2001
-From: Jonathan Bell <jonathan@raspberrypi.com>
-Date: Mon, 8 Apr 2024 16:09:52 +0100
-Subject: [PATCH 1019/1085] drivers: mmc: disable write-caching on Samsung 2023
- model year SD cards
-
-Samsung EVO Plus, Pro Plus and Evo Ultimate cards of this era appear to
-have a broken cache-flush implementation when operating in CQ mode.
-
-Unfortunately the cards seem to use a separate CID name string for every
-variant and capacity, so nobble the cache feature for this MANFID, OEMID
-and year. Turning this off seems to have negligible impact on
-random-write throughput in non-CQ mode.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
----
- drivers/mmc/core/card.h | 1 +
- drivers/mmc/core/quirks.h | 8 ++++++++
- 2 files changed, 9 insertions(+)
-
---- a/drivers/mmc/core/card.h
-+++ b/drivers/mmc/core/card.h
-@@ -84,6 +84,7 @@ struct mmc_fixup {
- #define CID_MANFID_TOSHIBA 0x11
- #define CID_MANFID_MICRON 0x13
- #define CID_MANFID_SAMSUNG 0x15
-+#define CID_MANFID_SAMSUNG_SD 0x1b
- #define CID_MANFID_APACER 0x27
- #define CID_MANFID_KINGSTON 0x70
- #define CID_MANFID_HYNIX 0x90
---- a/drivers/mmc/core/quirks.h
-+++ b/drivers/mmc/core/quirks.h
-@@ -25,6 +25,14 @@ static const struct mmc_fixup __maybe_un
- 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
- MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
-
-+ /*
-+ * Samsung Pro Plus/EVO Plus/Pro Ultimate SD cards (2023) claim to cache
-+ * flush OK, but become unresponsive afterwards.
-+ */
-+ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_SAMSUNG_SD, 0x534d, 2023, CID_MONTH_ANY,
-+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
-+ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
-+
- END_FIXUP
- };
-
}
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
-@@ -503,6 +503,7 @@ struct vc4_encoder {
+@@ -504,6 +504,7 @@ struct vc4_encoder {
void (*post_crtc_disable)(struct drm_encoder *encoder, struct drm_atomic_state *state);
void (*post_crtc_powerdown)(struct drm_encoder *encoder, struct drm_atomic_state *state);
+++ /dev/null
-From 8e40644b272a4ddc9d3b58b4373dffcef02d1b63 Mon Sep 17 00:00:00 2001
-From: Jonathan Bell <jonathan@raspberrypi.com>
-Date: Tue, 4 Jun 2024 13:21:47 +0100
-Subject: [PATCH 1115/1135] mmc: sd: halt CQHCI before issuing a cache flush
- command
-
-SD cards perform cache flushes by a CMD49 extension register write -
-which needs to be started from the SDHCI command/argument registers and
-not a CQHCI slot.
-
-Host access to SD/CQ registers should be exclusive to one or the other,
-so issue a halt before doing the command.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
----
- drivers/mmc/core/sd.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
-
---- a/drivers/mmc/core/sd.c
-+++ b/drivers/mmc/core/sd.c
-@@ -1265,6 +1265,14 @@ static int sd_flush_cache(struct mmc_hos
- reg_buf = card->ext_reg_buf;
-
- /*
-+ * Flushing requires sending CMD49 (adtc), which can't be done as a DCMD
-+ * and conflicts with CQHCI - temporarily turn CQE off to use the SDHCI
-+ * command/argument registers.
-+ */
-+ if (host->cqe_on)
-+ host->cqe_ops->cqe_off(host);
-+
-+ /*
- * Set Flush Cache at bit 0 in the performance enhancement register at
- * 261 bytes offset.
- */
}
card->ext_perf.fno = fno;
-@@ -1383,6 +1384,7 @@ retry:
+@@ -1375,6 +1376,7 @@ retry:
card->ocr = ocr;
card->type = MMC_TYPE_SD;
},
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
-@@ -569,6 +569,8 @@ struct vc4_pv_data {
+@@ -570,6 +570,8 @@ struct vc4_pv_data {
/* Number of pixels output per clock period */
u8 pixels_per_clock;
+++ /dev/null
-From 216df57950849f905c398904e7d6cbdf278b5717 Mon Sep 17 00:00:00 2001
-From: Jonathan Bell <jonathan@raspberrypi.com>
-Date: Mon, 5 Aug 2024 11:28:36 +0100
-Subject: [PATCH 1208/1215] DTS: bcm2712: enable SD slot CQE by default on Pi 5
-
-The corresponding driver implementation has seen sufficient testing,
-so enable by default. Retain the dtparam so it can be turned off for test.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/README | 6 +++---
- arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 1 +
- 2 files changed, 4 insertions(+), 3 deletions(-)
-
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -378,9 +378,9 @@ Params:
- non-lite SKU of CM4).
- (default "on")
-
-- sd_cqe Use to enable Command Queueing on the SD
-- interface for faster Class A2 card performance
-- (Pi 5 only, default "off")
-+ sd_cqe Set to "off" to disable Command Queueing if you
-+ have an incompatible Class A2 SD card
-+ (Pi 5 only, default "on")
-
- sd_overclock Clock (in MHz) to use when the MMC framework
- requests 50MHz
---- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
-@@ -363,6 +363,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g
- sd-uhs-sdr50;
- sd-uhs-ddr50;
- sd-uhs-sdr104;
-+ supports-cqe;
- cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>;
- //no-1-8-v;
- status = "okay";
--- /dev/null
+From ad2babc6596ba4f500454a4eaa607f3b49fcbcbe Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 7 Aug 2024 17:41:31 +0100
+Subject: [PATCH 1218/1350] Bluetooth: hci_sync: Fix crash on NULL parent
+
+Although later functions can handle a NULL fwnode, fwnode can't handle
+being passed a NULL pointer.
+
+See: https://github.com/raspberrypi/linux/issues/6305
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ net/bluetooth/hci_sync.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/net/bluetooth/hci_sync.c
++++ b/net/bluetooth/hci_sync.c
+@@ -4861,7 +4861,8 @@ static const struct {
+ */
+ static int hci_dev_setup_sync(struct hci_dev *hdev)
+ {
+- struct fwnode_handle *fwnode = dev_fwnode(hdev->dev.parent);
++ struct fwnode_handle *fwnode =
++ hdev->dev.parent ? dev_fwnode(hdev->dev.parent) : NULL;
+ int ret = 0;
+ bool invalid_bdaddr;
+ size_t i;
--- /dev/null
+From 209e8a3e6646f25abb352fd5a8a4c2e855b1e952 Mon Sep 17 00:00:00 2001
+From: Adrian Figueroa <elagil@takanome.de>
+Date: Wed, 14 Aug 2024 20:00:12 +0200
+Subject: [PATCH 1219/1350] overlays: add overlay for generic I2S clock-master
+ DAC
+
+Adds an overlay for supporting a generic I2S DAC that
+acts as the clock master on the bus.
+The data format is 32 bit stereo.
+
+Signed-off-by: Adrian Figueroa <elagil@takanome.de>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 +++
+ .../dts/overlays/i2s-master-dac-overlay.dts | 50 +++++++++++++++++++
+ 3 files changed, 57 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/i2s-master-dac-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -125,6 +125,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ i2c6.dtbo \
+ i2s-dac.dtbo \
+ i2s-gpio28-31.dtbo \
++ i2s-master-dac.dtbo \
+ ilitek251x.dtbo \
+ imx219.dtbo \
+ imx258.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2633,6 +2633,12 @@ Load: dtoverlay=i2s-gpio28-31
+ Params: <None>
+
+
++Name: i2s-master-dac
++Info: Configures a generic I2S DAC soundcard that acts as a clock master.
++Load: dtoverlay=i2s-master-dac
++Params: <None>
++
++
+ Name: ilitek251x
+ Info: Enables I2C connected Ilitek 251x multiple touch controller using
+ GPIO 4 (pin 7 on GPIO header) for interrupt.
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2s-master-dac-overlay.dts
+@@ -0,0 +1,50 @@
++// Definitions for a generic I2S DAC that acts as clock master on the bus.
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2s_clk_consumer>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target-path = "/";
++ __overlay__ {
++ codec_bare: codec_bare {
++ compatible = "linux,spdif-dit";
++ #sound-dai-cells = <0>;
++ status = "okay";
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&sound>;
++ __overlay__ {
++ compatible = "simple-audio-card";
++ i2s-controller = <&i2s_clk_consumer>;
++ status = "okay";
++
++ simple-audio-card,name = "i2s-master-dac";
++ simple-audio-card,format = "i2s";
++
++ simple-audio-card,bitclock-master = <&snd_codec>;
++ simple-audio-card,frame-master = <&snd_codec>;
++
++ simple-audio-card,cpu {
++ sound-dai = <&i2s_clk_consumer>;
++ dai-tdm-slot-num = <2>;
++ dai-tdm-slot-width = <32>;
++ };
++
++ snd_codec: simple-audio-card,codec {
++ sound-dai = <&codec_bare>;
++ };
++ };
++ };
++};
--- /dev/null
+From 15ca476264094b25d0a210109a061192a468117b Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+Date: Thu, 15 Aug 2024 16:08:14 +0200
+Subject: [PATCH 1220/1350] ASoC: DACplusADCPro - put ADC control definitions
+ in header file
+
+For easier maintenance all ADC ALSA-Kcontrols and the respective
+definitions are placed into a new header file that is included
+by the existing DAC+ADC Pro driver and a new, soon to be
+released ADC only board driver using the same controls.
+
+Signed-off-by: j-schambacher <joerg@hifiberry.com>
+---
+ sound/soc/bcm/hifiberry_adc_controls.h | 128 ++++++++++++++++++++++++
+ sound/soc/bcm/hifiberry_dacplusadcpro.c | 110 +-------------------
+ 2 files changed, 129 insertions(+), 109 deletions(-)
+ create mode 100644 sound/soc/bcm/hifiberry_adc_controls.h
+
+--- /dev/null
++++ b/sound/soc/bcm/hifiberry_adc_controls.h
+@@ -0,0 +1,128 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * ALSA mixer/Kcontrol definitions common to HiFiBerry ADCs
++ *
++ * used by DAC+ADC Pro (hifiberry_dacplusadcpro.c),
++ * ADC (hifiberry_adc.c)
++ *
++ * Author: Joerg Schambacher <joerg@hifiberry.com>
++ * Copyright 2024
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ */
++
++static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
++ 0x00, 0x01, 0x02, 0x03, 0x10
++};
++
++static const char * const pcm186x_adcl_input_channel_sel_text[] = {
++ "No Select",
++ "VINL1[SE]", /* Default for ADCL */
++ "VINL2[SE]",
++ "VINL2[SE] + VINL1[SE]",
++ "{VIN1P, VIN1M}[DIFF]"
++};
++
++static const char * const pcm186x_adcr_input_channel_sel_text[] = {
++ "No Select",
++ "VINR1[SE]", /* Default for ADCR */
++ "VINR2[SE]",
++ "VINR2[SE] + VINR1[SE]",
++ "{VIN2P, VIN2M}[DIFF]"
++};
++
++static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
++ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
++ PCM186X_ADC_INPUT_SEL_MASK,
++ ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
++ pcm186x_adcl_input_channel_sel_text,
++ pcm186x_adc_input_channel_sel_value),
++ SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
++ PCM186X_ADC_INPUT_SEL_MASK,
++ ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
++ pcm186x_adcr_input_channel_sel_text,
++ pcm186x_adc_input_channel_sel_value),
++};
++
++static const unsigned int pcm186x_mic_bias_sel_value[] = {
++ 0x00, 0x01, 0x11
++};
++
++static const char * const pcm186x_mic_bias_sel_text[] = {
++ "Mic Bias off",
++ "Mic Bias on",
++ "Mic Bias with Bypass Resistor"
++};
++
++static const struct soc_enum pcm186x_mic_bias_sel[] = {
++ SOC_VALUE_ENUM_SINGLE(PCM186X_MIC_BIAS_CTRL, 0,
++ GENMASK(4, 0),
++ ARRAY_SIZE(pcm186x_mic_bias_sel_text),
++ pcm186x_mic_bias_sel_text,
++ pcm186x_mic_bias_sel_value),
++};
++
++static const unsigned int pcm186x_gain_sel_value[] = {
++ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
++ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
++ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
++ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
++ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
++ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
++ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
++ 0x50
++};
++
++static const char * const pcm186x_gain_sel_text[] = {
++ "-12.0dB", "-11.5dB", "-11.0dB", "-10.5dB", "-10.0dB", "-9.5dB",
++ "-9.0dB", "-8.5dB", "-8.0dB", "-7.5dB", "-7.0dB", "-6.5dB",
++ "-6.0dB", "-5.5dB", "-5.0dB", "-4.5dB", "-4.0dB", "-3.5dB",
++ "-3.0dB", "-2.5dB", "-2.0dB", "-1.5dB", "-1.0dB", "-0.5dB",
++ "0.0dB", "0.5dB", "1.0dB", "1.5dB", "2.0dB", "2.5dB",
++ "3.0dB", "3.5dB", "4.0dB", "4.5dB", "5.0dB", "5.5dB",
++ "6.0dB", "6.5dB", "7.0dB", "7.5dB", "8.0dB", "8.5dB",
++ "9.0dB", "9.5dB", "10.0dB", "10.5dB", "11.0dB", "11.5dB",
++ "12.0dB", "12.5dB", "13.0dB", "13.5dB", "14.0dB", "14.5dB",
++ "15.0dB", "15.5dB", "16.0dB", "16.5dB", "17.0dB", "17.5dB",
++ "18.0dB", "18.5dB", "19.0dB", "19.5dB", "20.0dB", "20.5dB",
++ "21.0dB", "21.5dB", "22.0dB", "22.5dB", "23.0dB", "23.5dB",
++ "24.0dB", "24.5dB", "25.0dB", "25.5dB", "26.0dB", "26.5dB",
++ "27.0dB", "27.5dB", "28.0dB", "28.5dB", "29.0dB", "29.5dB",
++ "30.0dB", "30.5dB", "31.0dB", "31.5dB", "32.0dB", "32.5dB",
++ "33.0dB", "33.5dB", "34.0dB", "34.5dB", "35.0dB", "35.5dB",
++ "36.0dB", "36.5dB", "37.0dB", "37.5dB", "38.0dB", "38.5dB",
++ "39.0dB", "39.5dB", "40.0dB"};
++
++static const struct soc_enum pcm186x_gain_sel[] = {
++ SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_L, 0,
++ 0xff,
++ ARRAY_SIZE(pcm186x_gain_sel_text),
++ pcm186x_gain_sel_text,
++ pcm186x_gain_sel_value),
++ SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_R, 0,
++ 0xff,
++ ARRAY_SIZE(pcm186x_gain_sel_text),
++ pcm186x_gain_sel_text,
++ pcm186x_gain_sel_value),
++};
++
++static const struct snd_kcontrol_new pcm1863_snd_controls_card[] = {
++ SOC_ENUM("ADC Left Input", pcm186x_adc_input_channel_sel[0]),
++ SOC_ENUM("ADC Right Input", pcm186x_adc_input_channel_sel[1]),
++ SOC_ENUM("ADC Mic Bias", pcm186x_mic_bias_sel),
++ SOC_ENUM("PGA Gain Left", pcm186x_gain_sel[0]),
++ SOC_ENUM("PGA Gain Right", pcm186x_gain_sel[1]),
++};
+--- a/sound/soc/bcm/hifiberry_dacplusadcpro.c
++++ b/sound/soc/bcm/hifiberry_dacplusadcpro.c
+@@ -37,6 +37,7 @@
+
+ #include "../codecs/pcm512x.h"
+ #include "../codecs/pcm186x.h"
++#include "hifiberry_adc_controls.h"
+
+ #define HIFIBERRY_DACPRO_NOCLOCK 0
+ #define HIFIBERRY_DACPRO_CLK44EN 1
+@@ -57,115 +58,6 @@ static bool snd_rpi_hifiberry_is_dacpro;
+ static bool digital_gain_0db_limit = true;
+ static bool leds_off;
+
+-static const unsigned int pcm186x_adc_input_channel_sel_value[] = {
+- 0x00, 0x01, 0x02, 0x03, 0x10
+-};
+-
+-static const char * const pcm186x_adcl_input_channel_sel_text[] = {
+- "No Select",
+- "VINL1[SE]", /* Default for ADCL */
+- "VINL2[SE]",
+- "VINL2[SE] + VINL1[SE]",
+- "{VIN1P, VIN1M}[DIFF]"
+-};
+-
+-static const char * const pcm186x_adcr_input_channel_sel_text[] = {
+- "No Select",
+- "VINR1[SE]", /* Default for ADCR */
+- "VINR2[SE]",
+- "VINR2[SE] + VINR1[SE]",
+- "{VIN2P, VIN2M}[DIFF]"
+-};
+-
+-static const struct soc_enum pcm186x_adc_input_channel_sel[] = {
+- SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0,
+- PCM186X_ADC_INPUT_SEL_MASK,
+- ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text),
+- pcm186x_adcl_input_channel_sel_text,
+- pcm186x_adc_input_channel_sel_value),
+- SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0,
+- PCM186X_ADC_INPUT_SEL_MASK,
+- ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text),
+- pcm186x_adcr_input_channel_sel_text,
+- pcm186x_adc_input_channel_sel_value),
+-};
+-
+-static const unsigned int pcm186x_mic_bias_sel_value[] = {
+- 0x00, 0x01, 0x11
+-};
+-
+-static const char * const pcm186x_mic_bias_sel_text[] = {
+- "Mic Bias off",
+- "Mic Bias on",
+- "Mic Bias with Bypass Resistor"
+-};
+-
+-static const struct soc_enum pcm186x_mic_bias_sel[] = {
+- SOC_VALUE_ENUM_SINGLE(PCM186X_MIC_BIAS_CTRL, 0,
+- GENMASK(4, 0),
+- ARRAY_SIZE(pcm186x_mic_bias_sel_text),
+- pcm186x_mic_bias_sel_text,
+- pcm186x_mic_bias_sel_value),
+-};
+-
+-static const unsigned int pcm186x_gain_sel_value[] = {
+- 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+- 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+- 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+- 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+- 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+- 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+- 0x50
+-};
+-
+-static const char * const pcm186x_gain_sel_text[] = {
+- "-12.0dB", "-11.5dB", "-11.0dB", "-10.5dB", "-10.0dB", "-9.5dB",
+- "-9.0dB", "-8.5dB", "-8.0dB", "-7.5dB", "-7.0dB", "-6.5dB",
+- "-6.0dB", "-5.5dB", "-5.0dB", "-4.5dB", "-4.0dB", "-3.5dB",
+- "-3.0dB", "-2.5dB", "-2.0dB", "-1.5dB", "-1.0dB", "-0.5dB",
+- "0.0dB", "0.5dB", "1.0dB", "1.5dB", "2.0dB", "2.5dB",
+- "3.0dB", "3.5dB", "4.0dB", "4.5dB", "5.0dB", "5.5dB",
+- "6.0dB", "6.5dB", "7.0dB", "7.5dB", "8.0dB", "8.5dB",
+- "9.0dB", "9.5dB", "10.0dB", "10.5dB", "11.0dB", "11.5dB",
+- "12.0dB", "12.5dB", "13.0dB", "13.5dB", "14.0dB", "14.5dB",
+- "15.0dB", "15.5dB", "16.0dB", "16.5dB", "17.0dB", "17.5dB",
+- "18.0dB", "18.5dB", "19.0dB", "19.5dB", "20.0dB", "20.5dB",
+- "21.0dB", "21.5dB", "22.0dB", "22.5dB", "23.0dB", "23.5dB",
+- "24.0dB", "24.5dB", "25.0dB", "25.5dB", "26.0dB", "26.5dB",
+- "27.0dB", "27.5dB", "28.0dB", "28.5dB", "29.0dB", "29.5dB",
+- "30.0dB", "30.5dB", "31.0dB", "31.5dB", "32.0dB", "32.5dB",
+- "33.0dB", "33.5dB", "34.0dB", "34.5dB", "35.0dB", "35.5dB",
+- "36.0dB", "36.5dB", "37.0dB", "37.5dB", "38.0dB", "38.5dB",
+- "39.0dB", "39.5dB", "40.0dB"};
+-
+-static const struct soc_enum pcm186x_gain_sel[] = {
+- SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_L, 0,
+- 0xff,
+- ARRAY_SIZE(pcm186x_gain_sel_text),
+- pcm186x_gain_sel_text,
+- pcm186x_gain_sel_value),
+- SOC_VALUE_ENUM_SINGLE(PCM186X_PGA_VAL_CH1_R, 0,
+- 0xff,
+- ARRAY_SIZE(pcm186x_gain_sel_text),
+- pcm186x_gain_sel_text,
+- pcm186x_gain_sel_value),
+-};
+-
+-static const struct snd_kcontrol_new pcm1863_snd_controls_card[] = {
+- SOC_ENUM("ADC Left Input", pcm186x_adc_input_channel_sel[0]),
+- SOC_ENUM("ADC Right Input", pcm186x_adc_input_channel_sel[1]),
+- SOC_ENUM("ADC Mic Bias", pcm186x_mic_bias_sel),
+- SOC_ENUM("PGA Gain Left", pcm186x_gain_sel[0]),
+- SOC_ENUM("PGA Gain Right", pcm186x_gain_sel[1]),
+-};
+-
+ static int pcm1863_add_controls(struct snd_soc_component *component)
+ {
+ snd_soc_add_component_controls(component,
--- /dev/null
+From 9c5d91f6b938c23065ddc21c8654c5e222c35687 Mon Sep 17 00:00:00 2001
+From: Jan Kehren <jan.kehren@emteria.com>
+Date: Fri, 16 Aug 2024 13:47:50 +0000
+Subject: [PATCH 1222/1350] drm: rp1: rp1-dsi: Add DRM_FORMAT_ARGB8888 and
+ DRM_FORMAT_ABGR8888
+
+Android requires this.
+As the underlying hardware doesn't support alpha blending,
+we ignore the alpha value.
+
+Signed-off-by: Jan Kehren <jan.kehren@emteria.com>
+---
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c | 2 ++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c | 12 ++++++++++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+@@ -229,6 +229,8 @@ static const struct drm_mode_config_func
+ static const u32 rp1dsi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_ARGB8888,
++ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565
+--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
+@@ -247,6 +247,18 @@ static const struct rp1dsi_ipixfmt my_fo
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
++ .format = DRM_FORMAT_ARGB8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_ABGR8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
+ .format = DRM_FORMAT_RGB888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(23, 15, 7),
--- /dev/null
+From 9f0523de1b7ef122fae527326372a4ab5aa42fa6 Mon Sep 17 00:00:00 2001
+From: Jan Kehren <jan.kehren@emteria.com>
+Date: Tue, 20 Aug 2024 08:08:50 +0000
+Subject: [PATCH 1223/1350] drm: rp1: rp1-dpi: Add DRM_FORMAT_ARGB8888 and
+ DRM_FORMAT_ABGR8888
+
+Android requires this.
+As the underlying hardware doesn't support alpha blending,
+we ignore the alpha value.
+
+Signed-off-by: Jan Kehren <jan.kehren@emteria.com>
+---
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 2 ++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 12 ++++++++++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
+@@ -260,6 +260,8 @@ static struct drm_driver rp1dpi_driver =
+ static const u32 rp1dpi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_ARGB8888,
++ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565
+--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
+@@ -258,6 +258,18 @@ static const struct rp1dpi_ipixfmt my_fo
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
++ .format = DRM_FORMAT_ARGB8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_ABGR8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
+ .format = DRM_FORMAT_RGB888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(23, 15, 7),
--- /dev/null
+From 29f7f01091f9aaa6b0c45f5c2e3db1792d381e9d Mon Sep 17 00:00:00 2001
+From: Jan Kehren <jan.kehren@emteria.com>
+Date: Tue, 20 Aug 2024 08:16:06 +0000
+Subject: [PATCH 1224/1350] drm: rp1: rp1-vec: Add DRM_FORMAT_ARGB8888 and
+ DRM_FORMAT_ABGR8888
+
+Android requires this.
+As the underlying hardware doesn't support alpha blending,
+we ignore the alpha value.
+
+Signed-off-by: Jan Kehren <jan.kehren@emteria.com>
+---
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 2 ++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 12 ++++++++++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -420,6 +420,8 @@ static const struct drm_mode_config_func
+ static const u32 rp1vec_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_ARGB8888,
++ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -63,6 +63,18 @@ static const struct rp1vec_ipixfmt my_fo
+ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
+ },
+ {
++ .format = DRM_FORMAT_ARGB8888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++ },
++ {
++ .format = DRM_FORMAT_ABGR8888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++ },
++ {
+ .format = DRM_FORMAT_RGB888,
+ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = SHIFT_RGB(23, 15, 7),
--- /dev/null
+From d984fd8907736d37656c558e213cfe087e43a7ce Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 12 Aug 2024 13:31:58 +0100
+Subject: [PATCH 1225/1350] drm/vc4: Add a delay after disabling hdmi phy
+ output
+
+There appears to be a requirement for some devices
+(I'm testing with a 8K VRROOM 40Gbps HDMI switch)
+for a measable delay between removing the hdmi phy output from
+the old mode, to enabling the hdmi phy output for the new mode.
+
+Without the delay, a mode switch has a small change of getting a permanent
+'no signal', which requires a subsequent mode switch or a unplug/replug
+to redetect.
+
+Switching between 4kp24/25/30 modes fails about 5% of time in my testing.
+
+Add a delay to make it impossible to switch faster than this.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -668,6 +668,7 @@ static void vc4_crtc_atomic_disable(stru
+ * someone was waiting it.
+ */
+ vc4_crtc_send_vblank(crtc);
++ msleep(20);
+ }
+
+ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
--- /dev/null
+From aa54ce17dc3a19eaf26f9c17c05a18aabcac90b0 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 13 Aug 2024 16:13:16 +0100
+Subject: [PATCH 1226/1350] drm/vc4: Implement vc6_hdmi_phy_disable
+
+The body of this function was missing so we don't reset the phy
+when disabling it.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -1197,4 +1197,9 @@ void vc6_hdmi_phy_init(struct vc4_hdmi *
+
+ void vc6_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
+ {
++ unsigned long flags;
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++ vc6_hdmi_reset_phy(vc4_hdmi);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
--- /dev/null
+From 3d21dabd055ca064880e775892a10c5e69fdf5e9 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 13 Aug 2024 17:18:51 +0100
+Subject: [PATCH 1227/1350] drm/vc4: Also power down the PLL core when
+ resetting PHY
+
+The current reset code doesn't actually stop the hdmi output.
+That makes it difficult for displays to handle a mode set.
+
+Powering down the PLL does actually remove the hdmi signal
+and makes mode sets more reliable
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -137,6 +137,7 @@
+ #define VC6_HDMI_TX_PHY_PLL_REFCLK_REFCLK_SEL_CMOS BIT(13)
+ #define VC6_HDMI_TX_PHY_PLL_REFCLK_REFFRQ_MASK VC4_MASK(9, 0)
+
++#define VC6_HDMI_TX_PHY_PLL_POST_KDIV_BYPASS_EN BIT(4)
+ #define VC6_HDMI_TX_PHY_PLL_POST_KDIV_CLK0_SEL_MASK VC4_MASK(3, 2)
+ #define VC6_HDMI_TX_PHY_PLL_POST_KDIV_KDIV_MASK VC4_MASK(1, 0)
+
+@@ -947,6 +948,7 @@ static void vc6_hdmi_reset_phy(struct vc
+
+ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
+ HDMI_WRITE(HDMI_TX_PHY_POWERUP_CTL, 0);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_POST_KDIV, VC6_HDMI_TX_PHY_PLL_POST_KDIV_BYPASS_EN);
+ }
+
+ void vc6_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
--- /dev/null
+From 784ef64631be19ef45a597e618c04a0f8b041307 Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+Date: Tue, 20 Aug 2024 10:08:27 +0200
+Subject: [PATCH 1228/1350] ASoC: add driver for new HiFiBerry ADC only
+ board(s)
+
+Adds the driver for the soon to be released first ADC only board.
+It includes the same ADC controls as used by the DAC+ADC Pro driver.
+
+Signed-off-by: j-schambacher <joerg@hifiberry.com>
+---
+ sound/soc/bcm/Kconfig | 7 ++
+ sound/soc/bcm/Makefile | 2 +
+ sound/soc/bcm/hifiberry_adc.c | 174 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 183 insertions(+)
+ create mode 100644 sound/soc/bcm/hifiberry_adc.c
+
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -39,6 +39,13 @@ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SO
+ help
+ Say Y or M if you want to add support for voiceHAT soundcard.
+
++config SND_BCM2708_SOC_HIFIBERRY_ADC
++ tristate "Support for HifiBerry ADC"
++ select SND_SOC_PCM186X_I2C
++ select SND_RPI_HIFIBERRY_ADC
++ help
++ Say Y or M if you want to add support for HifiBerry ADC.
++
+ config SND_BCM2708_SOC_HIFIBERRY_DAC
+ tristate "Support for HifiBerry DAC and DAC8X"
+ select SND_SOC_PCM5102A
+--- a/sound/soc/bcm/Makefile
++++ b/sound/soc/bcm/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_SND_BCM63XX_I2S_WHISTLER) +
+ snd-soc-googlevoicehat-codec-objs := googlevoicehat-codec.o
+
+ # BCM2708 Machine Support
++snd-soc-hifiberry-adc-objs := hifiberry_adc.o
+ snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o
+ snd-soc-hifiberry-dacplushd-objs := hifiberry_dacplushd.o
+ snd-soc-hifiberry-dacplusadc-objs := hifiberry_dacplusadc.o
+@@ -51,6 +52,7 @@ snd-soc-chipdip-dac-objs := chipdip-dac.
+ snd-soc-dacberry400-objs := dacberry400.o
+
+ obj-$(CONFIG_SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD) += snd-soc-googlevoicehat-codec.o
++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_ADC) += snd-soc-hifiberry-adc.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD) += snd-soc-hifiberry-dacplushd.o
+ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC) += snd-soc-hifiberry-dacplusadc.o
+--- /dev/null
++++ b/sound/soc/bcm/hifiberry_adc.c
+@@ -0,0 +1,174 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * ASoC Driver for HiFiBerry ADC
++ *
++ * Author: Joerg Schambacher <joerg@hifiberry.com>
++ * Copyright 2024
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/kernel.h>
++#include <linux/clk.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/jack.h>
++#include <sound/tlv.h>
++
++#include "../codecs/pcm186x.h"
++#include "hifiberry_adc_controls.h"
++
++static bool leds_off;
++
++static int pcm1863_add_controls(struct snd_soc_component *component)
++{
++ snd_soc_add_component_controls(component,
++ pcm1863_snd_controls_card,
++ ARRAY_SIZE(pcm1863_snd_controls_card));
++ return 0;
++}
++
++static int snd_rpi_hifiberry_adc_init(struct snd_soc_pcm_runtime *rtd)
++{
++ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
++ struct snd_soc_component *adc = codec_dai->component;
++ int ret;
++
++ ret = pcm1863_add_controls(adc);
++ if (ret < 0)
++ dev_warn(rtd->dev, "Failed to add pcm1863 controls: %d\n",
++ ret);
++
++ codec_dai->driver->capture.rates =
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
++ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000;
++
++ /* set GPIO2 to output, GPIO3 input */
++ snd_soc_component_write(adc, PCM186X_GPIO3_2_CTRL, 0x00);
++ snd_soc_component_write(adc, PCM186X_GPIO3_2_DIR_CTRL, 0x04);
++ if (leds_off)
++ snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x00);
++ else
++ snd_soc_component_update_bits(adc, PCM186X_GPIO_IN_OUT, 0x40, 0x40);
++
++ return 0;
++}
++
++static int snd_rpi_hifiberry_adc_hw_params(
++ struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
++{
++ int ret = 0;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ int channels = params_channels(params);
++ int width = snd_pcm_format_width(params_format(params));
++
++ /* Using powers of 2 allows for an integer clock divisor */
++ width = width <= 16 ? 16 : 32;
++
++ ret = snd_soc_dai_set_bclk_ratio(asoc_rtd_to_cpu(rtd, 0), channels * width);
++ return ret;
++}
++
++/* machine stream operations */
++static const struct snd_soc_ops snd_rpi_hifiberry_adc_ops = {
++ .hw_params = snd_rpi_hifiberry_adc_hw_params,
++};
++
++SND_SOC_DAILINK_DEFS(hifi,
++ DAILINK_COMP_ARRAY(COMP_CPU("bcm2708-i2s.0")),
++ DAILINK_COMP_ARRAY(COMP_CODEC("pcm186x.1-004a", "pcm1863-aif")),
++ DAILINK_COMP_ARRAY(COMP_PLATFORM("bcm2708-i2s.0")));
++
++static struct snd_soc_dai_link snd_rpi_hifiberry_adc_dai[] = {
++{
++ .name = "HiFiBerry ADC",
++ .stream_name = "HiFiBerry ADC HiFi",
++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
++ SND_SOC_DAIFMT_CBS_CFS,
++ .ops = &snd_rpi_hifiberry_adc_ops,
++ .init = snd_rpi_hifiberry_adc_init,
++ SND_SOC_DAILINK_REG(hifi),
++},
++};
++
++/* audio machine driver */
++static struct snd_soc_card snd_rpi_hifiberry_adc = {
++ .name = "snd_rpi_hifiberry_adc",
++ .driver_name = "HifiberryAdc",
++ .owner = THIS_MODULE,
++ .dai_link = snd_rpi_hifiberry_adc_dai,
++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_adc_dai),
++};
++
++static int snd_rpi_hifiberry_adc_probe(struct platform_device *pdev)
++{
++ int ret = 0, i = 0;
++ struct snd_soc_card *card = &snd_rpi_hifiberry_adc;
++
++ snd_rpi_hifiberry_adc.dev = &pdev->dev;
++ if (pdev->dev.of_node) {
++ struct device_node *i2s_node;
++ struct snd_soc_dai_link *dai;
++
++ dai = &snd_rpi_hifiberry_adc_dai[0];
++ i2s_node = of_parse_phandle(pdev->dev.of_node,
++ "i2s-controller", 0);
++ if (i2s_node) {
++ for (i = 0; i < card->num_links; i++) {
++ dai->cpus->dai_name = NULL;
++ dai->cpus->of_node = i2s_node;
++ dai->platforms->name = NULL;
++ dai->platforms->of_node = i2s_node;
++ }
++ }
++ }
++ leds_off = of_property_read_bool(pdev->dev.of_node,
++ "hifiberry-adc,leds_off");
++ ret = snd_soc_register_card(&snd_rpi_hifiberry_adc);
++ if (ret && ret != -EPROBE_DEFER)
++ dev_err(&pdev->dev,
++ "snd_soc_register_card() failed: %d\n", ret);
++
++ return ret;
++}
++
++static const struct of_device_id snd_rpi_hifiberry_adc_of_match[] = {
++ { .compatible = "hifiberry,hifiberry-adc", },
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, snd_rpi_hifiberry_adc_of_match);
++
++static struct platform_driver snd_rpi_hifiberry_adc_driver = {
++ .driver = {
++ .name = "snd-rpi-hifiberry-adc",
++ .owner = THIS_MODULE,
++ .of_match_table = snd_rpi_hifiberry_adc_of_match,
++ },
++ .probe = snd_rpi_hifiberry_adc_probe,
++};
++
++module_platform_driver(snd_rpi_hifiberry_adc_driver);
++
++MODULE_AUTHOR("Joerg Schambacher <joerg@hifiberry.com>");
++MODULE_DESCRIPTION("ASoC Driver for HiFiBerry ADC");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 2f656dc533b65ae7f23527c3dc176efa92add105 Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+Date: Tue, 20 Aug 2024 10:24:10 +0200
+Subject: [PATCH 1229/1350] overlays: Add overlay for Hifiberry ADC
+
+Adds the DT overlay for the HiFiBerry ADC.
+
+Signed-off-by: j-schambacher <joerg@hifiberry.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 7 +++
+ .../dts/overlays/hifiberry-adc-overlay.dts | 45 +++++++++++++++++++
+ sound/soc/bcm/Kconfig | 1 +
+ 4 files changed, 54 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/hifiberry-adc-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -85,6 +85,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ hd44780-i2c-lcd.dtbo \
+ hd44780-lcd.dtbo \
+ hdmi-backlight-hwhack-gpio.dtbo \
++ hifiberry-adc.dtbo \
+ hifiberry-amp.dtbo \
+ hifiberry-amp100.dtbo \
+ hifiberry-amp3.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1771,6 +1771,13 @@ Params: gpio_pin GPIO pin
+ expects a high to switch it on.
+
+
++Name: hifiberry-adc
++Info: Configures the HifiBerry ADC audio card
++Load: dtoverlay=hifiberry-adc,<param>=<val>
++Params: leds_off If set to 'true' the onboard indicator LED
++ is switched off at all times.
++
++
+ Name: hifiberry-amp
+ Info: Configures the HifiBerry Amp and Amp+ audio cards
+ Load: dtoverlay=hifiberry-amp
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/hifiberry-adc-overlay.dts
+@@ -0,0 +1,45 @@
++// SPDX-License-Identifier: GPL-2.0
++// Definitions for HiFiBerry ADC, no onboard clocks
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2s_clk_producer>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target = <&i2c1>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ hb_adc: pcm186x@4a {
++ #sound-dai-cells = <0>;
++ compatible = "ti,pcm1863";
++ reg = <0x4a>;
++ status = "okay";
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&sound>;
++ hifiberry_adc: __overlay__ {
++ compatible = "hifiberry,hifiberry-adc";
++ audio-codec = <&hb_adc>;
++ i2s-controller = <&i2s_clk_producer>;
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ leds_off = <&hifiberry_adc>,"hifiberry-adc,leds_off?";
++ };
++};
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -45,6 +45,7 @@ config SND_BCM2708_SOC_HIFIBERRY_ADC
+ select SND_RPI_HIFIBERRY_ADC
+ help
+ Say Y or M if you want to add support for HifiBerry ADC.
++ Use this module for HiFiBerry's ADC-only sound cards
+
+ config SND_BCM2708_SOC_HIFIBERRY_DAC
+ tristate "Support for HifiBerry DAC and DAC8X"
--- /dev/null
+From ba0f2212e0e100ee16bdde76b7efca6bb8ee9446 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 29 Nov 2021 12:14:49 +0000
+Subject: [PATCH 1232/1350] spi: spidev: Restore loading from Device Tree
+
+As happens occasionally, an upstream change has once again prevented
+spidev from being loaded via Device Tree. We now need "spidev" to be
+included in the new spi_device_id list, otherwise although the
+spidev driver gets loaded no /dev/spidev*.* entries will appear.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spidev.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -711,6 +711,7 @@ static const struct spi_device_id spidev
+ { .name = "spi-authenta" },
+ { .name = "em3581" },
+ { .name = "si3210" },
++ { .name = "spidev" },
+ {},
+ };
+ MODULE_DEVICE_TABLE(spi, spidev_spi_ids);
--- /dev/null
+From e596d70725ca70113d39d9366d7b4d3e492f6449 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 31 Jul 2024 19:05:29 +0100
+Subject: [PATCH 1233/1350] drivers: drm: rp1-dsi: Implement more DSI options
+ and flags
+
+Now implementing:
+- Per-command selection of LP or HS for commands (previously LP)
+- EoTp transmission option (previously EoTp was always disabled)
+- Non-continuous clock option (previously always continuous)
+- Per-command enabling of ACK request (in command mode only)
+
+Make a plausible (and possibly correct) attempt to measure the
+longest LP command that will fit into vertical blanking lines.
+
+DON'T set both "Burst Mode" and "Sync Events" flags together.
+This is redundant in the standard IP; in this RP1 variant it
+would enable Sync Pulses but may break with some video timings.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c | 5 +-
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h | 3 +-
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c | 106 +++++++++++++++++-----
+ 3 files changed, 91 insertions(+), 23 deletions(-)
+
+--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+@@ -396,7 +396,10 @@ ssize_t rp1dsi_host_transfer(struct mipi
+ return ret;
+ }
+
+- rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header), packet.payload_length, packet.payload);
++ rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header),
++ packet.payload_length, packet.payload,
++ !!(msg->flags & MIPI_DSI_MSG_USE_LPM),
++ !!(msg->flags & MIPI_DSI_MSG_REQ_ACK));
+
+ /* Optional read back */
+ if (msg->rx_len && msg->rx_buf)
+--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
+@@ -86,7 +86,8 @@ void rp1dsi_mipicfg_setup(struct rp1_dsi
+ /* Functions to control the SNPS D-PHY and DSI block setup */
+
+ void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode);
+-void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf);
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf,
++ bool use_lpm, bool req_ack);
+ int rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf);
+ void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int cmd_mode);
+ void rp1dsi_dsi_stop(struct rp1_dsi *dsi);
+--- a/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
+@@ -103,6 +103,24 @@
+
+ /* And some bitfield definitions */
+
++#define DSI_PCKHDL_EOTP_TX_EN BIT(0)
++#define DSI_PCKHDL_BTA_EN BIT(2)
++
++#define DSI_VID_MODE_LP_CMD_EN BIT(15)
++#define DSI_VID_MODE_FRAME_BTA_ACK_EN BIT(14)
++#define DSI_VID_MODE_LP_HFP_EN BIT(13)
++#define DSI_VID_MODE_LP_HBP_EN BIT(12)
++#define DSI_VID_MODE_LP_VACT_EN BIT(11)
++#define DSI_VID_MODE_LP_VFP_EN BIT(10)
++#define DSI_VID_MODE_LP_VBP_EN BIT(9)
++#define DSI_VID_MODE_LP_VSA_EN BIT(8)
++#define DSI_VID_MODE_SYNC_PULSES 0
++#define DSI_VID_MODE_SYNC_EVENTS 1
++#define DSI_VID_MODE_BURST 2
++
++#define DSI_CMD_MODE_ALL_LP 0x10f7f00
++#define DSI_CMD_MODE_ACK_RQST_EN BIT(1)
++
+ #define DPHY_PWR_UP_SHUTDOWNZ_LSB 0
+ #define DPHY_PWR_UP_SHUTDOWNZ_BITS BIT(DPHY_PWR_UP_SHUTDOWNZ_LSB)
+
+@@ -1252,8 +1270,8 @@ static u32 dphy_configure_pll(struct rp1
+ vco_freq, actual_vco_freq, m, refclk, n,
+ hsfreq_table[dsi->hsfreq_index].hsfreqrange);
+ } else {
+- drm_warn(dsi->drm,
+- "rp1dsi: Error configuring DPHY PLL %uHz\n", vco_freq);
++ drm_err(dsi->drm,
++ "rp1dsi: Error configuring DPHY PLL %uHz\n", vco_freq);
+ }
+
+ return actual_vco_freq;
+@@ -1321,7 +1339,7 @@ static void rp1dsi_dpiclk_start(struct r
+ clk_set_rate(dsi->clocks[RP1DSI_CLOCK_DPI], (4 * lanes * byte_clock) / (bpp >> 1));
+ clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_DPI]);
+ drm_info(dsi->drm,
+- "rp1dsi: Nominal Byte clock %u DPI clock %lu (parent rate %lu)",
++ "rp1dsi: Nominal Byte clock %u DPI clock %lu (parent rate %lu)\n",
+ byte_clock,
+ clk_get_rate(dsi->clocks[RP1DSI_CLOCK_DPI]),
+ clk_get_rate(clk_get_parent(dsi->clocks[RP1DSI_CLOCK_DPI])));
+@@ -1365,7 +1383,8 @@ static u32 get_colorcode(enum mipi_dsi_p
+
+ void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode)
+ {
+- u32 timeout, mask, vid_mode_cfg;
++ int cmdtim;
++ u32 timeout, mask, clkdiv;
+ unsigned int bpp = mipi_dsi_pixel_format_to_bpp(dsi->display_format);
+ u32 byte_clock = clamp((bpp * 125 * min(mode->clock, RP1DSI_DPI_MAX_KHZ)) / dsi->lanes,
+ RP1DSI_BYTE_CLK_MIN, RP1DSI_BYTE_CLK_MAX);
+@@ -1374,19 +1393,31 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
+ DSI_WRITE(DSI_DPI_CFG_POL, 0);
+ DSI_WRITE(DSI_GEN_VCID, dsi->vc);
+ DSI_WRITE(DSI_DPI_COLOR_CODING, get_colorcode(dsi->display_format));
+- /* a conservative guess (LP escape is slow!) */
+- DSI_WRITE(DSI_DPI_LP_CMD_TIM, 0x00100000);
+
+- /* Drop to LP where possible; use LP Escape for all commands */
+- vid_mode_cfg = 0xbf00;
+- if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
+- vid_mode_cfg |= 0x01;
+- else if (8 * dsi->lanes > bpp)
+- vid_mode_cfg &= ~0x400; /* PULSE && inexact DPICLK => fix HBP time */
++ /*
++ * Flags to configure use of LP, EoTp, Burst Mode, Sync Events/Pulses.
++ * Note that Burst Mode implies Sync Events; the two flags need not be
++ * set concurrently, and in this RP1 variant *should not* both be set:
++ * doing so would (counter-intuitively) enable Sync Pulses and may fail
++ * if there is not sufficient time to return to LP11 state during HBP.
++ */
++ mask = DSI_VID_MODE_LP_HFP_EN | DSI_VID_MODE_LP_HBP_EN |
++ DSI_VID_MODE_LP_VACT_EN | DSI_VID_MODE_LP_VFP_EN |
++ DSI_VID_MODE_LP_VBP_EN | DSI_VID_MODE_LP_VSA_EN;
++ if (dsi->display_flags & MIPI_DSI_MODE_LPM)
++ mask |= DSI_VID_MODE_LP_CMD_EN;
+ if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_BURST)
+- vid_mode_cfg |= 0x02;
+- DSI_WRITE(DSI_VID_MODE_CFG, vid_mode_cfg);
+- DSI_WRITE(DSI_CMD_MODE_CFG, 0x10F7F00);
++ mask |= DSI_VID_MODE_BURST;
++ else if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
++ mask |= DSI_VID_MODE_SYNC_EVENTS;
++ else if (8 * dsi->lanes > bpp)
++ mask &= ~DSI_VID_MODE_LP_HBP_EN; /* PULSE && inexact DPICLK => fix HBP time */
++ DSI_WRITE(DSI_VID_MODE_CFG, mask);
++ DSI_WRITE(DSI_CMD_MODE_CFG,
++ (dsi->display_flags & MIPI_DSI_MODE_LPM) ? DSI_CMD_MODE_ALL_LP : 0);
++ DSI_WRITE(DSI_PCKHDL_CFG,
++ DSI_PCKHDL_BTA_EN |
++ ((dsi->display_flags & MIPI_DSI_MODE_NO_EOT_PACKET) ? 0 : DSI_PCKHDL_EOTP_TX_EN));
+
+ /* Select Command Mode */
+ DSI_WRITE(DSI_MODE_CFG, 1);
+@@ -1397,9 +1428,9 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
+ timeout = 0;
+ DSI_WRITE(DSI_TO_CNT_CFG, (timeout << 16) | RP1DSI_LPRX_TO_VAL);
+ DSI_WRITE(DSI_BTA_TO_CNT, RP1DSI_BTA_TO_VAL);
++ clkdiv = max(2u, 1u + byte_clock / RP1DSI_ESC_CLK_MAX); /* byte clocks per escape clock */
+ DSI_WRITE(DSI_CLKMGR_CFG,
+- (RP1DSI_TO_CLK_DIV << 8) |
+- max(2u, 1u + byte_clock / RP1DSI_ESC_CLK_MAX));
++ (RP1DSI_TO_CLK_DIV << 8) | clkdiv);
+
+ /* Configure video timings */
+ DSI_WRITE(DSI_VID_PKT_SIZE, mode->hdisplay);
+@@ -1425,6 +1456,18 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
+ (hsfreq_table[dsi->hsfreq_index].data_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
+ (hsfreq_table[dsi->hsfreq_index].data_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
+
++ /* Estimate how many LP bytes can be sent during vertical blanking (Databook 3.6.2.1) */
++ cmdtim = mode->htotal;
++ if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
++ cmdtim -= mode->hsync_end - mode->hsync_start;
++ cmdtim = (bpp * cmdtim - 64) / (8 * dsi->lanes); /* byte clocks after HSS and EoTp */
++ cmdtim -= hsfreq_table[dsi->hsfreq_index].data_hs2lp;
++ cmdtim -= hsfreq_table[dsi->hsfreq_index].data_lp2hs;
++ cmdtim = (cmdtim / clkdiv) - 24; /* escape clocks for commands */
++ cmdtim = max(0, cmdtim >> 4); /* bytes (at 2 clocks per bit) */
++ drm_info(dsi->drm, "rp1dsi: Command time (outvact): %d\n", cmdtim);
++ DSI_WRITE(DSI_DPI_LP_CMD_TIM, cmdtim << 16);
++
+ /* Wait for PLL lock */
+ for (timeout = (1 << 14); timeout != 0; --timeout) {
+ usleep_range(10, 50);
+@@ -1434,9 +1477,9 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
+ if (timeout == 0)
+ drm_err(dsi->drm, "RP1DSI: Time out waiting for PLL\n");
+
+- DSI_WRITE(DSI_LPCLK_CTRL, 0x1); /* configure the requesthsclk */
++ DSI_WRITE(DSI_LPCLK_CTRL,
++ (dsi->display_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0x3 : 0x1);
+ DSI_WRITE(DSI_PHY_TST_CTRL0, 0x2);
+- DSI_WRITE(DSI_PCKHDL_CFG, 1 << 2); /* allow bus turnaround */
+ DSI_WRITE(DSI_PWR_UP, 0x1); /* power up */
+
+ /* Now it should be safe to start the external DPI clock divider */
+@@ -1460,7 +1503,8 @@ void rp1dsi_dsi_setup(struct rp1_dsi *ds
+ mask, DSI_READ(DSI_PHY_STATUS));
+ }
+
+-void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf)
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf,
++ bool use_lpm, bool req_ack)
+ {
+ u32 val;
+
+@@ -1471,6 +1515,24 @@ void rp1dsi_dsi_send(struct rp1_dsi *dsi
+ usleep_range(100, 150);
+ }
+
++ /*
++ * Update global configuration flags for LP/HS and ACK options.
++ * XXX It's not clear if having empty FIFOs (checked above and below) guarantees that
++ * the last command has completed and been ACKed, or how closely these control registers
++ * align with command/payload FIFO writes (as each is an independent clock-crossing)?
++ */
++ val = DSI_READ(DSI_VID_MODE_CFG);
++ if (use_lpm)
++ val |= DSI_VID_MODE_LP_CMD_EN;
++ else
++ val &= ~DSI_VID_MODE_LP_CMD_EN;
++ DSI_WRITE(DSI_VID_MODE_CFG, val);
++ val = (use_lpm) ? DSI_CMD_MODE_ALL_LP : 0;
++ if (req_ack)
++ val |= DSI_CMD_MODE_ACK_RQST_EN;
++ DSI_WRITE(DSI_CMD_MODE_CFG, val);
++ (void)DSI_READ(DSI_CMD_MODE_CFG);
++
+ /* Write payload (in 32-bit words) and header */
+ for (; len > 0; len -= 4) {
+ val = *buf++;
+@@ -1504,8 +1566,10 @@ int rp1dsi_dsi_recv(struct rp1_dsi *dsi,
+ break;
+ usleep_range(100, 150);
+ }
+- if (i == 0)
++ if (!i) {
++ drm_warn(dsi->drm, "Receive failed\n");
+ return -EIO;
++ }
+
+ for (i = 0; i < len; i += 4) {
+ /* Read fifo must not be empty before all bytes are read */
--- /dev/null
+From 8beb6891489c3c99618a7390578109aadfdf8901 Mon Sep 17 00:00:00 2001
+From: Axel <48924884+Paladinking@users.noreply.github.com>
+Date: Wed, 28 Aug 2024 09:46:13 +0200
+Subject: [PATCH 1234/1350] rtc: pcf8523: Fix oscillator stop bit handling
+ reading from Control_1
+
+The check if the oscillator stop bit is set was reading from Control_1
+register instead of the Seconds register.
+This caused the Seconds register to be incorrectly changed if bit 7 of
+Control_1 happens to be set.
+
+Signed-off-by: Axel Hammarberg <axel.hammarberg@gmail.com>
+---
+ drivers/rtc/rtc-pcf8523.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/rtc/rtc-pcf8523.c
++++ b/drivers/rtc/rtc-pcf8523.c
+@@ -108,10 +108,10 @@ static int pcf8523_rtc_read_time(struct
+ if (err < 0)
+ return err;
+
+- if ((regs[0] & PCF8523_CONTROL1_STOP) || (regs[3] & PCF8523_SECONDS_OS))
++ if (regs[PCF8523_REG_CONTROL1] & PCF8523_CONTROL1_STOP)
+ return -EINVAL;
+
+- if (regs[0] & PCF8523_SECONDS_OS) {
++ if (regs[PCF8523_REG_SECONDS] & PCF8523_SECONDS_OS) {
+ /*
+ * If the oscillator was stopped, try to clear the flag. Upon
+ * power-up the flag is always set, but if we cannot clear it
+@@ -120,10 +120,10 @@ static int pcf8523_rtc_read_time(struct
+ * that the clock cannot be assumed to be correct.
+ */
+
+- regs[0] &= ~PCF8523_SECONDS_OS;
++ regs[PCF8523_REG_SECONDS] &= ~PCF8523_SECONDS_OS;
+
+ err = regmap_write(pcf8523->regmap, PCF8523_REG_SECONDS,
+- regs[0]);
++ regs[PCF8523_REG_SECONDS]);
+ if (err < 0)
+ return err;
+
+@@ -135,7 +135,7 @@ static int pcf8523_rtc_read_time(struct
+ if (value & PCF8523_SECONDS_OS)
+ return -EAGAIN;
+
+- regs[0] = value;
++ regs[PCF8523_REG_SECONDS] = value;
+ }
+
+ tm->tm_sec = bcd2bin(regs[3] & 0x7f);
--- /dev/null
+From a44f17d8193b69aedb1beebf5ad885a88b1c6615 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 2 Aug 2024 11:01:24 +0100
+Subject: [PATCH 1235/1350] drivers: media: pci: Update Hailo accelerator
+ device driver to v4.18.0
+
+Sourced from https://github.com/hailo-ai/hailort-drivers/
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/pci/hailo/Makefile | 2 +
+ drivers/media/pci/hailo/common/fw_operation.c | 2 +-
+ drivers/media/pci/hailo/common/fw_operation.h | 2 +-
+ .../media/pci/hailo/common/fw_validation.c | 10 +-
+ .../media/pci/hailo/common/fw_validation.h | 5 +-
+ .../pci/hailo/common/hailo_ioctl_common.h | 240 +++++++----
+ .../media/pci/hailo/common/hailo_resource.c | 2 +-
+ .../media/pci/hailo/common/hailo_resource.h | 2 +-
+ drivers/media/pci/hailo/common/pcie_common.c | 367 +++++++++++++----
+ drivers/media/pci/hailo/common/pcie_common.h | 42 +-
+ drivers/media/pci/hailo/common/utils.h | 24 +-
+ drivers/media/pci/hailo/common/vdma_common.c | 371 +++++++++++++-----
+ drivers/media/pci/hailo/common/vdma_common.h | 34 +-
+ drivers/media/pci/hailo/src/fops.c | 104 +++--
+ drivers/media/pci/hailo/src/fops.h | 1 +
+ drivers/media/pci/hailo/src/pci_soc_ioctl.c | 155 ++++++++
+ drivers/media/pci/hailo/src/pci_soc_ioctl.h | 19 +
+ drivers/media/pci/hailo/src/pcie.c | 93 ++++-
+ drivers/media/pci/hailo/src/pcie.h | 2 +
+ drivers/media/pci/hailo/src/sysfs.c | 9 +
+ drivers/media/pci/hailo/src/utils.c | 1 -
+ .../pci/hailo/utils/integrated_nnc_utils.c | 101 +++++
+ .../pci/hailo/utils/integrated_nnc_utils.h | 30 ++
+ drivers/media/pci/hailo/vdma/ioctl.c | 53 ++-
+ drivers/media/pci/hailo/vdma/ioctl.h | 6 +-
+ drivers/media/pci/hailo/vdma/memory.c | 148 ++++++-
+ drivers/media/pci/hailo/vdma/memory.h | 4 +-
+ drivers/media/pci/hailo/vdma/vdma.c | 80 ++--
+ drivers/media/pci/hailo/vdma/vdma.h | 30 +-
+ 29 files changed, 1536 insertions(+), 403 deletions(-)
+ create mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c
+ create mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.h
+ create mode 100755 drivers/media/pci/hailo/utils/integrated_nnc_utils.c
+ create mode 100755 drivers/media/pci/hailo/utils/integrated_nnc_utils.h
+
+--- a/drivers/media/pci/hailo/Makefile
++++ b/drivers/media/pci/hailo/Makefile
+@@ -10,6 +10,7 @@ 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 += $(COMMON_SRC_DIRECTORY)/fw_validation.o
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o
+@@ -18,6 +19,7 @@ hailo_pci-objs += $(COMMON_SRC_DIRECTORY
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/hailo_resource.o
+
+ hailo_pci-objs += $(UTILS_SRC_DIRECTORY)/logs.o
++hailo_pci-objs += $(UTILS_SRC_DIRECTORY)/integrated_nnc_utils.o
+
+ hailo_pci-objs += $(VDMA_SRC_DIRECTORY)/vdma.o
+ hailo_pci-objs += $(VDMA_SRC_DIRECTORY)/memory.o
+--- a/drivers/media/pci/hailo/common/fw_operation.c
++++ b/drivers/media/pci/hailo/common/fw_operation.c
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+--- a/drivers/media/pci/hailo/common/fw_operation.h
++++ b/drivers/media/pci/hailo/common/fw_operation.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+--- a/drivers/media/pci/hailo/common/fw_validation.c
++++ b/drivers/media/pci/hailo/common/fw_validation.c
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -28,16 +28,18 @@ int FW_VALIDATION__validate_fw_header(ui
+ firmware_header_t *firmware_header = NULL;
+ u32 consumed_firmware_offset = *outer_consumed_firmware_offset;
+ u32 expected_firmware_magic = 0;
+-
++
+ firmware_header = (firmware_header_t *) (firmware_base_address + consumed_firmware_offset);
+ CONSUME_FIRMWARE(sizeof(firmware_header_t), -EINVAL);
+
+ switch (board_type) {
+ case HAILO_BOARD_TYPE_HAILO8:
+- expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO8;
++ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO8;
+ break;
++ case HAILO_BOARD_TYPE_HAILO10H_LEGACY:
+ case HAILO_BOARD_TYPE_HAILO15:
+- expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15;
++ case HAILO_BOARD_TYPE_HAILO10H:
++ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15;
+ break;
+ case HAILO_BOARD_TYPE_PLUTO:
+ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_PLUTO;
+--- a/drivers/media/pci/hailo/common/fw_validation.h
++++ b/drivers/media/pci/hailo/common/fw_validation.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -11,8 +11,7 @@
+
+ #define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0)
+ #define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB)
+-// TODO - HRT-11344 : change fw magic to pluto specific
+-#define FIRMWARE_HEADER_MAGIC_PLUTO (0xE905DAAB)
++#define FIRMWARE_HEADER_MAGIC_PLUTO (0xF94739AB)
+
+ #ifndef HAILO_EMULATOR
+ #define FIRMWARE_WAIT_TIMEOUT_MS (5000)
+--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h
++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h
+@@ -6,6 +6,14 @@
+ #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_REVISION 0
++
++#define _STRINGIFY_EXPANDED( x ) #x
++#define _STRINGIFY_NUMBER( x ) _STRINGIFY_EXPANDED(x)
++#define HAILO_DRV_VER _STRINGIFY_NUMBER(HAILO_DRV_VER_MAJOR) "." _STRINGIFY_NUMBER(HAILO_DRV_VER_MINOR) "." _STRINGIFY_NUMBER(HAILO_DRV_VER_REVISION)
++
+
+ // This value is not easily changeable.
+ // For example: the channel interrupts ioctls assume we have up to 32 channels
+@@ -23,14 +31,17 @@
+ #define INVALID_DRIVER_HANDLE_VALUE ((uintptr_t)-1)
+
+ // Used by windows and unix driver to raise the right CPU control handle to the FW. The same as in pcie_service FW
+-#define FW_ACCESS_CORE_CPU_CONTROL_SHIFT (1)
+-#define FW_ACCESS_CORE_CPU_CONTROL_MASK (1 << FW_ACCESS_CORE_CPU_CONTROL_SHIFT)
+-#define FW_ACCESS_CONTROL_INTERRUPT_SHIFT (0)
+-#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_CORE_CPU_CONTROL_SHIFT (1)
++#define FW_ACCESS_CORE_CPU_CONTROL_MASK (1 << FW_ACCESS_CORE_CPU_CONTROL_SHIFT)
++#define FW_ACCESS_CONTROL_INTERRUPT_SHIFT (0)
++#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 INVALID_VDMA_CHANNEL (0xff)
+
+-#define INVALID_VDMA_CHANNEL (0xff)
+
+ #if !defined(__cplusplus) && defined(NTDDI_VERSION)
+ #include <wdm.h>
+@@ -53,14 +64,23 @@ typedef uint8_t bool;
+ #define INT_MAX 0x7FFFFFFF
+ #endif // !defined(INT_MAX)
+
++#if !defined(ECONNRESET)
++#define ECONNRESET 104 /* Connection reset by peer */
++#endif // !defined(ECONNRESET)
+
+ // {d88d31f1-fede-4e71-ac2a-6ce0018c1501}
+-DEFINE_GUID (GUID_DEVINTERFACE_HailoKM,
++DEFINE_GUID (GUID_DEVINTERFACE_HailoKM_NNC,
+ 0xd88d31f1,0xfede,0x4e71,0xac,0x2a,0x6c,0xe0,0x01,0x8c,0x15,0x01);
+
+-#define HAILO_GENERAL_IOCTL_MAGIC 0
+-#define HAILO_VDMA_IOCTL_MAGIC 1
+-#define HAILO_NON_LINUX_IOCTL_MAGIC 2
++// {7f16047d-64b8-207a-0092-e970893970a2}
++DEFINE_GUID (GUID_DEVINTERFACE_HailoKM_SOC,
++ 0x7f16047d,0x64b8,0x207a,0x00,0x92,0xe9,0x70,0x89,0x39,0x70,0xa2);
++
++#define HAILO_GENERAL_IOCTL_MAGIC 0
++#define HAILO_VDMA_IOCTL_MAGIC 1
++#define HAILO_SOC_IOCTL_MAGIC 2
++#define HAILO_PCI_EP_IOCTL_MAGIC 3
++#define HAILO_NNC_IOCTL_MAGIC 4
+
+ #define HAILO_IOCTL_COMPATIBLE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+@@ -114,9 +134,11 @@ static ULONG FORCEINLINE _IOC_(ULONG nr,
+ #define _IOWR_ _IOWR
+ #define _IO_ _IO
+
+-#define HAILO_GENERAL_IOCTL_MAGIC 'g'
+-#define HAILO_VDMA_IOCTL_MAGIC 'v'
+-#define HAILO_NON_LINUX_IOCTL_MAGIC 'w'
++#define HAILO_GENERAL_IOCTL_MAGIC 'g'
++#define HAILO_VDMA_IOCTL_MAGIC 'v'
++#define HAILO_SOC_IOCTL_MAGIC 's'
++#define HAILO_NNC_IOCTL_MAGIC 'n'
++#define HAILO_PCI_EP_IOCTL_MAGIC 'p'
+
+ #elif defined(__QNX__) // #ifdef _MSC_VER
+ #include <devctl.h>
+@@ -132,7 +154,6 @@ static ULONG FORCEINLINE _IOC_(ULONG nr,
+ #define _IO_ __DION
+ #define HAILO_GENERAL_IOCTL_MAGIC _DCMD_ALL
+ #define HAILO_VDMA_IOCTL_MAGIC _DCMD_MISC
+-#define HAILO_NON_LINUX_IOCTL_MAGIC _DCMD_PROC
+
+ #else // #ifdef _MSC_VER
+ #error "unsupported platform!"
+@@ -161,6 +182,16 @@ enum hailo_dma_data_direction {
+ HAILO_DMA_MAX_ENUM = INT_MAX,
+ };
+
++// Enum that states what type of buffer we are working with in the driver
++// TODO: HRT-13580 - Add specific type for user allocated and for driver allocated
++enum hailo_dma_buffer_type {
++ HAILO_DMA_USER_PTR_BUFFER = 0,
++ HAILO_DMA_DMABUF_BUFFER = 1,
++
++ /** Max enum value to maintain ABI Integrity */
++ HAILO_DMA_BUFFER_MAX_ENUM = INT_MAX,
++};
++
+ // Enum that determines if buffer should be allocated from user space or from driver
+ enum hailo_allocation_mode {
+ HAILO_ALLOCATION_MODE_USERSPACE = 0,
+@@ -170,10 +201,19 @@ enum hailo_allocation_mode {
+ HAILO_ALLOCATION_MODE_MAX_ENUM = INT_MAX,
+ };
+
++enum hailo_vdma_interrupts_domain {
++ HAILO_VDMA_INTERRUPTS_DOMAIN_NONE = 0,
++ HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE = (1 << 0),
++ HAILO_VDMA_INTERRUPTS_DOMAIN_HOST = (1 << 1),
++
++ /** Max enum value to maintain ABI Integrity */
++ HAILO_VDMA_INTERRUPTS_DOMAIN_MAX_ENUM = INT_MAX,
++};
++
+ /* structure used in ioctl HAILO_VDMA_BUFFER_MAP */
+ struct hailo_vdma_buffer_map_params {
+ #if defined(__linux__) || defined(_MSC_VER)
+- void* user_address; // in
++ uintptr_t user_address; // in
+ #elif defined(__QNX__)
+ shm_handle_t shared_memory_handle; // in
+ #else
+@@ -181,6 +221,7 @@ struct hailo_vdma_buffer_map_params {
+ #endif // __linux__
+ size_t size; // in
+ enum hailo_dma_data_direction data_direction; // in
++ enum hailo_dma_buffer_type buffer_type; // in
+ uintptr_t allocated_buffer_handle; // in
+ size_t mapped_handle; // out
+ };
+@@ -204,31 +245,27 @@ struct hailo_desc_list_release_params {
+ uintptr_t desc_handle; // in
+ };
+
+-/* structure used in ioctl HAILO_NON_LINUX_DESC_LIST_MMAP */
+-struct hailo_non_linux_desc_list_mmap_params {
+- uintptr_t desc_handle; // in
+- size_t size; // in
+- void* user_address; // out
+-};
+-
+ /* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */
+-struct hailo_desc_list_bind_vdma_buffer_params {
++struct hailo_desc_list_program_params {
+ size_t buffer_handle; // in
+ size_t buffer_size; // in
+ size_t buffer_offset; // in
+ uintptr_t desc_handle; // in
+ uint8_t channel_index; // in
+ uint32_t starting_desc; // in
++ bool should_bind; // in
++ enum hailo_vdma_interrupts_domain last_interrupts_domain; // in
++ bool is_debug; // in
+ };
+
+-/* structure used in ioctl HAILO_VDMA_INTERRUPTS_ENABLE */
+-struct hailo_vdma_interrupts_enable_params {
++/* structure used in ioctl HAILO_VDMA_ENABLE_CHANNELS */
++struct hailo_vdma_enable_channels_params {
+ uint32_t channels_bitmap_per_engine[MAX_VDMA_ENGINES]; // in
+ bool enable_timestamps_measure; // in
+ };
+
+-/* structure used in ioctl HAILO_VDMA_INTERRUPTS_DISABLE */
+-struct hailo_vdma_interrupts_disable_params {
++/* structure used in ioctl HAILO_VDMA_DISABLE_CHANNELS */
++struct hailo_vdma_disable_channels_params {
+ uint32_t channels_bitmap_per_engine[MAX_VDMA_ENGINES]; // in
+ };
+
+@@ -237,7 +274,7 @@ struct hailo_vdma_interrupts_channel_dat
+ uint8_t engine_index;
+ uint8_t channel_index;
+ bool is_active; // If not activate, num_processed is ignored.
+- uint16_t host_num_processed;
++ uint8_t transfers_completed; // Number of transfers completed.
+ uint8_t host_error; // Channel errors bits on source side
+ uint8_t device_error; // Channel errors bits on dest side
+ bool validation_success; // If the validation of the channel was successful
+@@ -312,6 +349,10 @@ enum hailo_transfer_memory_type {
+ HAILO_TRANSFER_MEMORY_DMA_ENGINE1,
+ HAILO_TRANSFER_MEMORY_DMA_ENGINE2,
+
++ // PCIe EP driver memories
++ HAILO_TRANSFER_MEMORY_PCIE_EP_CONFIG = 0x400,
++ HAILO_TRANSFER_MEMORY_PCIE_EP_BRIDGE,
++
+ /** Max enum value to maintain ABI Integrity */
+ HAILO_TRANSFER_MEMORY_MAX_ENUM = INT_MAX,
+ };
+@@ -352,15 +393,26 @@ enum hailo_board_type {
+ HAILO_BOARD_TYPE_HAILO8 = 0,
+ HAILO_BOARD_TYPE_HAILO15,
+ HAILO_BOARD_TYPE_PLUTO,
++ HAILO_BOARD_TYPE_HAILO10H,
++ HAILO_BOARD_TYPE_HAILO10H_LEGACY,
+ HAILO_BOARD_TYPE_COUNT,
+
+ /** Max enum value to maintain ABI Integrity */
+ HAILO_BOARD_TYPE_MAX_ENUM = INT_MAX
+ };
+
++enum hailo_accelerator_type {
++ HAILO_ACCELERATOR_TYPE_NNC,
++ HAILO_ACCELERATOR_TYPE_SOC,
++
++ /** Max enum value to maintain ABI Integrity */
++ HAILO_ACCELERATOR_TYPE_MAX_ENUM = INT_MAX
++};
++
+ enum hailo_dma_type {
+ HAILO_DMA_TYPE_PCIE,
+ HAILO_DMA_TYPE_DRAM,
++ HAILO_DMA_TYPE_PCI_EP,
+
+ /** Max enum value to maintain ABI Integrity */
+ HAILO_DMA_TYPE_MAX_ENUM = INT_MAX,
+@@ -428,15 +480,6 @@ struct hailo_vdma_transfer_buffer {
+ uint32_t size; // in
+ };
+
+-enum hailo_vdma_interrupts_domain {
+- HAILO_VDMA_INTERRUPTS_DOMAIN_NONE = 0,
+- HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE = (1 << 0),
+- HAILO_VDMA_INTERRUPTS_DOMAIN_HOST = (1 << 1),
+-
+- /** Max enum value to maintain ABI Integrity */
+- HAILO_VDMA_INTERRUPTS_DOMAIN_MAX_ENUM = INT_MAX,
+-};
+-
+ // We allow maximum 2 buffers per transfer since we may have an extra buffer
+ // to make sure each buffer is aligned to page size.
+ #define HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER (2)
+@@ -460,6 +503,35 @@ struct hailo_vdma_launch_transfer_params
+ // more info (e.g desc complete status)
+
+ uint32_t descs_programed; // out, amount of descriptors programed.
++ int launch_transfer_status; // out, status of the launch transfer call. (only used in case of error)
++};
++
++/* structure used in ioctl HAILO_SOC_CONNECT */
++struct hailo_soc_connect_params {
++ uint8_t input_channel_index; // out
++ uint8_t output_channel_index; // out
++ uintptr_t input_desc_handle; // in
++ uintptr_t output_desc_handle; // in
++};
++
++/* structure used in ioctl HAILO_SOC_CLOSE */
++struct hailo_soc_close_params {
++ uint8_t input_channel_index; // in
++ uint8_t output_channel_index; // in
++};
++
++/* structure used in ioctl HAILO_PCI_EP_ACCEPT */
++struct hailo_pci_ep_accept_params {
++ uint8_t input_channel_index; // out
++ uint8_t output_channel_index; // out
++ uintptr_t input_desc_handle; // in
++ uintptr_t output_desc_handle; // in
++};
++
++/* structure used in ioctl HAILO_PCI_EP_CLOSE */
++struct hailo_pci_ep_close_params {
++ uint8_t input_channel_index; // in
++ uint8_t output_channel_index; // in
+ };
+
+ #ifdef _MSC_VER
+@@ -469,8 +541,8 @@ struct tCompatibleHailoIoctlData
+ ULONG_PTR Value;
+ union {
+ struct hailo_memory_transfer_params MemoryTransfer;
+- struct hailo_vdma_interrupts_enable_params VdmaInterruptsEnable;
+- struct hailo_vdma_interrupts_disable_params VdmaInterruptsDisable;
++ struct hailo_vdma_enable_channels_params VdmaEnableChannels;
++ struct hailo_vdma_disable_channels_params VdmaDisableChannels;
+ struct hailo_vdma_interrupts_read_timestamp_params VdmaInterruptsReadTimestamps;
+ struct hailo_vdma_interrupts_wait_params VdmaInterruptsWait;
+ struct hailo_vdma_buffer_sync_params VdmaBufferSync;
+@@ -479,14 +551,17 @@ struct tCompatibleHailoIoctlData
+ struct hailo_vdma_buffer_unmap_params VdmaBufferUnmap;
+ struct hailo_desc_list_create_params DescListCreate;
+ struct hailo_desc_list_release_params DescListReleaseParam;
+- struct hailo_desc_list_bind_vdma_buffer_params DescListBind;
++ struct hailo_desc_list_program_params DescListProgram;
+ struct hailo_d2h_notification D2HNotification;
+ struct hailo_device_properties DeviceProperties;
+ struct hailo_driver_info DriverInfo;
+- struct hailo_non_linux_desc_list_mmap_params DescListMmap;
+ struct hailo_read_log_params ReadLog;
+ struct hailo_mark_as_in_use_params MarkAsInUse;
+ struct hailo_vdma_launch_transfer_params LaunchTransfer;
++ struct hailo_soc_connect_params ConnectParams;
++ struct hailo_soc_close_params SocCloseParams;
++ struct hailo_pci_ep_accept_params AcceptParams;
++ struct hailo_pci_ep_close_params PciEpCloseParams;
+ } Buffer;
+ };
+ #endif // _MSC_VER
+@@ -495,30 +570,20 @@ struct tCompatibleHailoIoctlData
+
+ enum hailo_general_ioctl_code {
+ HAILO_MEMORY_TRANSFER_CODE,
+- HAILO_FW_CONTROL_CODE,
+- HAILO_READ_NOTIFICATION_CODE,
+- HAILO_DISABLE_NOTIFICATION_CODE,
+ HAILO_QUERY_DEVICE_PROPERTIES_CODE,
+ HAILO_QUERY_DRIVER_INFO_CODE,
+- HAILO_READ_LOG_CODE,
+- HAILO_RESET_NN_CORE_CODE,
+
+ // Must be last
+ HAILO_GENERAL_IOCTL_MAX_NR,
+ };
+
+ #define HAILO_MEMORY_TRANSFER _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_MEMORY_TRANSFER_CODE, struct hailo_memory_transfer_params)
+-#define HAILO_FW_CONTROL _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_FW_CONTROL_CODE, struct hailo_fw_control)
+-#define HAILO_READ_NOTIFICATION _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_READ_NOTIFICATION_CODE, struct hailo_d2h_notification)
+-#define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE)
+ #define HAILO_QUERY_DEVICE_PROPERTIES _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DEVICE_PROPERTIES_CODE, struct hailo_device_properties)
+ #define HAILO_QUERY_DRIVER_INFO _IOW_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_QUERY_DRIVER_INFO_CODE, struct hailo_driver_info)
+-#define HAILO_READ_LOG _IOWR_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params)
+-#define HAILO_RESET_NN_CORE _IO_(HAILO_GENERAL_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE)
+
+ enum hailo_vdma_ioctl_code {
+- HAILO_VDMA_INTERRUPTS_ENABLE_CODE,
+- HAILO_VDMA_INTERRUPTS_DISABLE_CODE,
++ HAILO_VDMA_ENABLE_CHANNELS_CODE,
++ HAILO_VDMA_DISABLE_CHANNELS_CODE,
+ HAILO_VDMA_INTERRUPTS_WAIT_CODE,
+ HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS_CODE,
+ HAILO_VDMA_BUFFER_MAP_CODE,
+@@ -526,7 +591,7 @@ enum hailo_vdma_ioctl_code {
+ HAILO_VDMA_BUFFER_SYNC_CODE,
+ HAILO_DESC_LIST_CREATE_CODE,
+ HAILO_DESC_LIST_RELEASE_CODE,
+- HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE,
++ HAILO_DESC_LIST_PROGRAM_CODE,
+ HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE,
+ HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE,
+ HAILO_MARK_AS_IN_USE_CODE,
+@@ -538,38 +603,67 @@ enum hailo_vdma_ioctl_code {
+ HAILO_VDMA_IOCTL_MAX_NR,
+ };
+
+-#define HAILO_VDMA_INTERRUPTS_ENABLE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_ENABLE_CODE, struct hailo_vdma_interrupts_enable_params)
+-#define HAILO_VDMA_INTERRUPTS_DISABLE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_DISABLE_CODE, struct hailo_vdma_interrupts_disable_params)
++#define HAILO_VDMA_ENABLE_CHANNELS _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_ENABLE_CHANNELS_CODE, struct hailo_vdma_enable_channels_params)
++#define HAILO_VDMA_DISABLE_CHANNELS _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_DISABLE_CHANNELS_CODE, struct hailo_vdma_disable_channels_params)
+ #define HAILO_VDMA_INTERRUPTS_WAIT _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_WAIT_CODE, struct hailo_vdma_interrupts_wait_params)
+ #define HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS_CODE, struct hailo_vdma_interrupts_read_timestamp_params)
+
+-#define HAILO_VDMA_BUFFER_MAP _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_MAP_CODE, struct hailo_vdma_buffer_map_params)
+-#define HAILO_VDMA_BUFFER_UNMAP _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_UNMAP_CODE, struct hailo_vdma_buffer_unmap_params)
+-#define HAILO_VDMA_BUFFER_SYNC _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_SYNC_CODE, struct hailo_vdma_buffer_sync_params)
++#define HAILO_VDMA_BUFFER_MAP _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_MAP_CODE, struct hailo_vdma_buffer_map_params)
++#define HAILO_VDMA_BUFFER_UNMAP _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_UNMAP_CODE, struct hailo_vdma_buffer_unmap_params)
++#define HAILO_VDMA_BUFFER_SYNC _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_BUFFER_SYNC_CODE, struct hailo_vdma_buffer_sync_params)
++
++#define HAILO_DESC_LIST_CREATE _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_CREATE_CODE, struct hailo_desc_list_create_params)
++#define HAILO_DESC_LIST_RELEASE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_RELEASE_CODE, struct hailo_desc_list_release_params)
++#define HAILO_DESC_LIST_PROGRAM _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_PROGRAM_CODE, struct hailo_desc_list_program_params)
+
+-#define HAILO_DESC_LIST_CREATE _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_CREATE_CODE, struct hailo_desc_list_create_params)
+-#define HAILO_DESC_LIST_RELEASE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_RELEASE_CODE, struct hailo_desc_list_release_params)
+-#define HAILO_DESC_LIST_BIND_VDMA_BUFFER _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_DESC_LIST_BIND_VDMA_BUFFER_CODE, struct hailo_desc_list_bind_vdma_buffer_params)
++#define HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_low_memory_buffer_params)
++#define HAILO_VDMA_LOW_MEMORY_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE, struct hailo_free_low_memory_buffer_params)
+
+-#define HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC_CODE, struct hailo_allocate_low_memory_buffer_params)
+-#define HAILO_VDMA_LOW_MEMORY_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LOW_MEMORY_BUFFER_FREE_CODE, struct hailo_free_low_memory_buffer_params)
++#define HAILO_MARK_AS_IN_USE _IOW_(HAILO_VDMA_IOCTL_MAGIC, HAILO_MARK_AS_IN_USE_CODE, struct hailo_mark_as_in_use_params)
+
+-#define HAILO_MARK_AS_IN_USE _IOW_(HAILO_VDMA_IOCTL_MAGIC, HAILO_MARK_AS_IN_USE_CODE, struct hailo_mark_as_in_use_params)
++#define HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, struct hailo_allocate_continuous_buffer_params)
++#define HAILO_VDMA_CONTINUOUS_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE, struct hailo_free_continuous_buffer_params)
+
+-#define HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_ALLOC_CODE, struct hailo_allocate_continuous_buffer_params)
+-#define HAILO_VDMA_CONTINUOUS_BUFFER_FREE _IOR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_CONTINUOUS_BUFFER_FREE_CODE, struct hailo_free_continuous_buffer_params)
++#define HAILO_VDMA_LAUNCH_TRANSFER _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LAUNCH_TRANSFER_CODE, struct hailo_vdma_launch_transfer_params)
+
+-#define HAILO_VDMA_LAUNCH_TRANSFER _IOWR_(HAILO_VDMA_IOCTL_MAGIC, HAILO_VDMA_LAUNCH_TRANSFER_CODE, struct hailo_vdma_launch_transfer_params)
++enum hailo_nnc_ioctl_code {
++ HAILO_FW_CONTROL_CODE,
++ HAILO_READ_NOTIFICATION_CODE,
++ HAILO_DISABLE_NOTIFICATION_CODE,
++ HAILO_READ_LOG_CODE,
++ HAILO_RESET_NN_CORE_CODE,
+
++ // Must be last
++ HAILO_NNC_IOCTL_MAX_NR
++};
+
+-enum hailo_non_linux_ioctl_code {
+- HAILO_NON_LINUX_DESC_LIST_MMAP_CODE,
++#define HAILO_FW_CONTROL _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_FW_CONTROL_CODE, struct hailo_fw_control)
++#define HAILO_READ_NOTIFICATION _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_NOTIFICATION_CODE, struct hailo_d2h_notification)
++#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)
++
++enum hailo_soc_ioctl_code {
++ HAILO_SOC_IOCTL_CONNECT_CODE,
++ HAILO_SOC_IOCTL_CLOSE_CODE,
+
+ // Must be last
+- HAILO_NON_LINUX_IOCTL_MAX_NR,
++ HAILO_SOC_IOCTL_MAX_NR,
+ };
+
+-#define HAILO_NON_LINUX_DESC_LIST_MMAP _IOWR_(HAILO_NON_LINUX_IOCTL_MAGIC, HAILO_NON_LINUX_DESC_LIST_MMAP_CODE, struct hailo_non_linux_desc_list_mmap_params)
++#define HAILO_SOC_CONNECT _IOWR_(HAILO_SOC_IOCTL_MAGIC, HAILO_SOC_IOCTL_CONNECT_CODE, struct hailo_soc_connect_params)
++#define HAILO_SOC_CLOSE _IOR_(HAILO_SOC_IOCTL_MAGIC, HAILO_SOC_IOCTL_CLOSE_CODE, struct hailo_soc_close_params)
++
++
++enum hailo_pci_ep_ioctl_code {
++ HAILO_PCI_EP_ACCEPT_CODE,
++ HAILO_PCI_EP_CLOSE_CODE,
++
++ // Must be last
++ HAILO_PCI_EP_IOCTL_MAX_NR,
++};
+
++#define HAILO_PCI_EP_ACCEPT _IOWR_(HAILO_PCI_EP_IOCTL_MAGIC, HAILO_PCI_EP_ACCEPT_CODE, struct hailo_pci_ep_accept_params)
++#define HAILO_PCI_EP_CLOSE _IOR_(HAILO_PCI_EP_IOCTL_MAGIC, HAILO_PCI_EP_CLOSE_CODE, struct hailo_pci_ep_close_params)
+
+ #endif /* _HAILO_IOCTL_COMMON_H_ */
+--- a/drivers/media/pci/hailo/common/hailo_resource.c
++++ b/drivers/media/pci/hailo/common/hailo_resource.c
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+--- a/drivers/media/pci/hailo/common/hailo_resource.h
++++ b/drivers/media/pci/hailo/common/hailo_resource.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+--- a/drivers/media/pci/hailo/common/pcie_common.c
++++ b/drivers/media/pci/hailo/common/pcie_common.c
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -10,6 +10,8 @@
+ #include <linux/bug.h>
+ #include <linux/delay.h>
+ #include <linux/kernel.h>
++#include <linux/printk.h>
++#include <linux/device.h>
+
+
+ #define BSC_IMASK_HOST (0x0188)
+@@ -19,14 +21,13 @@
+
+ #define PO2_ROUND_UP(size, alignment) ((size + alignment-1) & ~(alignment-1))
+
+-#define ATR0_PARAM (0x17)
+-#define ATR0_SRC_ADDR (0x0)
+-#define ATR0_TRSL_ADDR2 (0x0)
+-#define ATR0_TRSL_PARAM (6)
++#define ATR_PARAM (0x17)
++#define ATR_SRC_ADDR (0x0)
++#define ATR_TRSL_PARAM (6)
++#define ATR_TABLE_SIZE (0x1000u)
++#define ATR_TABLE_SIZE_MASK (0x1000u - 1)
+
+ #define ATR0_PCIE_BRIDGE_OFFSET (0x700)
+-#define ATR0_TABLE_SIZE (0x1000u)
+-#define ATR0_TABLE_SIZE_MASK (0x1000u - 1)
+
+ #define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000)
+ #define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x20000)
+@@ -45,8 +46,13 @@
+ #define HAILO_PCIE_HOST_DMA_DATA_ID (0)
+ #define HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK (1 << 4)
+ #define HAILO_PCIE_DMA_HOST_INTERRUPTS_BITMASK (1 << 5)
++#define HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK (0x0000FFFF)
+
+-typedef u32 hailo_ptr_t;
++#define HAILO_PCIE_MAX_ATR_TABLE_INDEX (3)
++
++#define MAX_FILES_PER_STAGE (4)
++
++#define BOOT_STATUS_UNINITIALIZED (0x1)
+
+ struct hailo_fw_addresses {
+ u32 boot_fw_header;
+@@ -58,14 +64,11 @@ struct hailo_fw_addresses {
+ u32 core_fw_header;
+ u32 atr0_trsl_addr1;
+ u32 raise_ready_offset;
++ u32 boot_status;
+ };
+
+-struct hailo_atr_config {
+- u32 atr_param;
+- u32 atr_src;
+- u32 atr_trsl_addr_1;
+- u32 atr_trsl_addr_2;
+- u32 atr_trsl_param;
++struct loading_stage {
++ const struct hailo_file_batch *batch;
+ };
+
+ struct hailo_board_compatibility {
+@@ -73,6 +76,69 @@ struct hailo_board_compatibility {
+ 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];
++};
++
++static const struct hailo_file_batch hailo10h_files_stg1[] = {
++ {
++ .filename = "hailo/hailo10h/customer_certificate.bin",
++ .address = 0xA0000,
++ .max_size = 0x8004,
++ .is_mandatory = true,
++ .has_header = false
++ },
++ {
++ .filename = "hailo/hailo10h/u-boot.dtb.signed",
++ .address = 0xA8004,
++ .max_size = 0x20000,
++ .is_mandatory = true,
++ .has_header = false
++ },
++ {
++ .filename = "hailo/hailo10h/scu_fw.bin",
++ .address = 0x20000,
++ .max_size = 0x40000,
++ .is_mandatory = true,
++ .has_header = true
++ },
++ {
++ .filename = NULL,
++ .address = 0x00,
++ .max_size = 0x00,
++ .is_mandatory = false,
++ .has_header = false
++ }
++};
++
++static const struct hailo_file_batch hailo10h_files_stg2[] = {
++ {
++ .filename = "hailo/hailo10h/u-boot-spl.bin",
++ .address = 0x85000000,
++ .max_size = 0x1000000,
++ .is_mandatory = true,
++ .has_header = false
++ },
++ {
++ .filename = "hailo/hailo10h/u-boot-tfa.itb",
++ .address = 0x86000000,
++ .max_size = 0x1000000,
++ .is_mandatory = true,
++ .has_header = false
++ },
++ {
++ .filename = "hailo/hailo10h/fitImage",
++ .address = 0x87000000,
++ .max_size = 0x1000000,
++ .is_mandatory = true,
++ .has_header = 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
++ },
+ };
+
+ static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = {
+@@ -87,6 +153,7 @@ static const struct hailo_board_compatib
+ .core_fw_header = 0xA0000,
+ .atr0_trsl_addr1 = 0x60000000,
+ .raise_ready_offset = 0x1684,
++ .boot_status = 0xe0000,
+ },
+ .fw_filename = "hailo/hailo8_fw.bin",
+ .board_cfg = {
+@@ -100,7 +167,7 @@ static const struct hailo_board_compatib
+ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE,
+ },
+ },
+- [HAILO_BOARD_TYPE_HAILO15] = {
++ [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = {
+ .fw_addresses = {
+ .boot_fw_header = 0x88000,
+ .boot_fw_trigger = 0x88c98,
+@@ -111,6 +178,7 @@ static const struct hailo_board_compatib
+ .core_fw_header = 0xC0000,
+ .atr0_trsl_addr1 = 0x000BE000,
+ .raise_ready_offset = 0x1754,
++ .boot_status = 0x80000,
+ },
+ .fw_filename = "hailo/hailo15_fw.bin",
+ .board_cfg = {
+@@ -124,6 +192,39 @@ static const struct hailo_board_compatib
+ .max_size = 0,
+ },
+ },
++ [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,
++ .core_code_ram_base = 0,
++ .core_fw_header = 0,
++ .atr0_trsl_addr1 = 0x000BE000,
++ .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,
++ },
++ {
++ .batch = hailo10h_files_stg2,
++ },
++ },
++ },
+ // HRT-11344 : none of these matter except raise_ready_offset seeing as we load fw seperately - not through driver
+ // After implementing bootloader put correct values here
+ [HAILO_BOARD_TYPE_PLUTO] = {
+@@ -138,6 +239,7 @@ static const struct hailo_board_compatib
+ .atr0_trsl_addr1 = 0x000BE000,
+ // NOTE: After they update hw consts - check register fw_access_interrupt_w1s of pcie_config
+ .raise_ready_offset = 0x174c,
++ .boot_status = 0x80000,
+ },
+ .fw_filename = "hailo/pluto_fw.bin",
+ .board_cfg = {
+@@ -225,7 +327,7 @@ int hailo_pcie_read_firmware_control(str
+ // Copy response buffer
+ hailo_resource_read_buffer(&resources->fw_access, PCIE_REQUEST_SIZE_OFFSET + (size_t)response_header_size,
+ command->buffer_len, &command->buffer);
+-
++
+ return 0;
+ }
+
+@@ -253,93 +355,111 @@ int hailo_pcie_read_firmware_notificatio
+ return hailo_read_firmware_notification(¬ification_resource, notification);
+ }
+
+-static void write_atr_table(struct hailo_pcie_resources *resources,
+- struct hailo_atr_config *atr)
++int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index)
+ {
+- hailo_resource_write_buffer(&resources->config, ATR0_PCIE_BRIDGE_OFFSET,
+- sizeof(*atr), (void*)atr);
+-}
++ size_t offset = 0;
++ struct hailo_atr_config atr = {
++ .atr_param = (ATR_PARAM | (atr_index << 12)),
++ .atr_src = ATR_SRC_ADDR,
++ .atr_trsl_addr_1 = (u32)(trsl_addr & 0xFFFFFFFF),
++ .atr_trsl_addr_2 = (u32)(trsl_addr >> 32),
++ .atr_trsl_param = ATR_TRSL_PARAM
++ };
+
+-static void read_atr_table(struct hailo_pcie_resources *resources,
+- struct hailo_atr_config *atr)
+-{
+- hailo_resource_read_buffer(&resources->config, ATR0_PCIE_BRIDGE_OFFSET,
+- sizeof(*atr), (void*)atr);
++ BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index);
++ offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20);
++
++ return hailo_resource_write_buffer(bridge_config, offset, sizeof(atr), (void*)&atr);
+ }
+
+-static void configure_atr_table(struct hailo_pcie_resources *resources,
+- hailo_ptr_t base_address)
++void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index)
+ {
+- struct hailo_atr_config atr = {
+- .atr_param = ATR0_PARAM,
+- .atr_src = ATR0_SRC_ADDR,
+- .atr_trsl_addr_1 = (u32)base_address,
+- .atr_trsl_addr_2 = ATR0_TRSL_ADDR2,
+- .atr_trsl_param = ATR0_TRSL_PARAM
+- };
+- write_atr_table(resources, &atr);
++ size_t offset = 0;
++
++ BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index);
++ offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20);
++
++ hailo_resource_read_buffer(bridge_config, offset, sizeof(*atr), (void*)atr);
+ }
+
+ static void write_memory_chunk(struct hailo_pcie_resources *resources,
+ hailo_ptr_t dest, u32 dest_offset, const void *src, u32 len)
+ {
++ u32 ATR_INDEX = 0;
+ BUG_ON(dest_offset + len > (u32)resources->fw_access.size);
+
+- configure_atr_table(resources, dest);
++ (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX);
+ (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src);
+ }
+
+ static void read_memory_chunk(
+ struct hailo_pcie_resources *resources, hailo_ptr_t src, u32 src_offset, void *dest, u32 len)
+ {
++ u32 ATR_INDEX = 0;
+ BUG_ON(src_offset + len > (u32)resources->fw_access.size);
+
+- configure_atr_table(resources, src);
++ (void)hailo_pcie_configure_atr_table(&resources->config, (u64)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.
+-static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
++void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
+ {
+- hailo_ptr_t base_address = dest & ~ATR0_TABLE_SIZE_MASK;
++ struct hailo_atr_config previous_atr = {0};
++ hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK);
+ u32 chunk_len = 0;
+ u32 offset = 0;
++ u32 ATR_INDEX = 0;
++
++ // Store previous ATR (Read/write modify the ATR).
++ hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX);
+
+ if (base_address != dest) {
+ // Data is not aligned, write the first chunk
+- chunk_len = min(base_address + ATR0_TABLE_SIZE - dest, len);
++ chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len);
+ write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len);
+ offset += chunk_len;
+ }
+
+ while (offset < len) {
+- chunk_len = min(len - offset, ATR0_TABLE_SIZE);
++ chunk_len = min(len - offset, ATR_TABLE_SIZE);
+ write_memory_chunk(resources, dest + offset, 0, (const u8*)src + offset, chunk_len);
+ offset += chunk_len;
+ }
++
++ (void)hailo_pcie_configure_atr_table(&resources->config,
++ (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX);
+ }
+
+ // 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.
+ static void read_memory(struct hailo_pcie_resources *resources, hailo_ptr_t src, void *dest, u32 len)
+ {
+- hailo_ptr_t base_address = src & ~ATR0_TABLE_SIZE_MASK;
++ struct hailo_atr_config previous_atr = {0};
++ hailo_ptr_t base_address = (src & ~ATR_TABLE_SIZE_MASK);
+ u32 chunk_len = 0;
+ u32 offset = 0;
++ u32 ATR_INDEX = 0;
++
++ // Store previous ATR (Read/write modify the ATR).
++ hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX);
+
+ if (base_address != src) {
+ // Data is not aligned, write the first chunk
+- chunk_len = min(base_address + ATR0_TABLE_SIZE - src, len);
++ chunk_len = min(base_address + ATR_TABLE_SIZE - src, len);
+ read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len);
+ offset += chunk_len;
+ }
+
+ while (offset < len) {
+- chunk_len = min(len - offset, ATR0_TABLE_SIZE);
++ chunk_len = min(len - offset, ATR_TABLE_SIZE);
+ read_memory_chunk(resources, src + offset, 0, (u8*)dest + offset, chunk_len);
+ offset += chunk_len;
+ }
++
++ (void)hailo_pcie_configure_atr_table(&resources->config,
++ (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX);
+ }
+
+ static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header,
+@@ -367,7 +487,7 @@ static void hailo_write_core_firmware(st
+ write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t));
+ }
+
+-static void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources)
++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources)
+ {
+ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+ u32 pcie_finished = 1;
+@@ -376,6 +496,17 @@ static void hailo_trigger_firmware_boot(
+ (void*)&pcie_finished, sizeof(pcie_finished));
+ }
+
++u32 hailo_get_boot_status(struct hailo_pcie_resources *resources)
++{
++ 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));
++
++ return boot_status;
++}
++
+ /**
+ * Validates the FW headers.
+ * @param[in] address Address of the firmware.
+@@ -408,11 +539,14 @@ static int FW_VALIDATION__validate_fw_he
+ goto exit;
+ }
+
+- err = FW_VALIDATION__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_CORE_FIRMWARE_CODE_SIZE,
+- &consumed_firmware_offset, &core_firmware_header, board_type);
+- if (0 != err) {
+- err = -EINVAL;
+- goto exit;
++ // Not validating with HAILO10H since core firmware doesn't loaded over pcie
++ if (HAILO_BOARD_TYPE_HAILO10H != board_type) {
++ err = FW_VALIDATION__validate_fw_header(firmware_base_address, firmware_size, MAXIMUM_CORE_FIRMWARE_CODE_SIZE,
++ &consumed_firmware_offset, &core_firmware_header, board_type);
++ if (0 != err) {
++ err = -EINVAL;
++ goto exit;
++ }
+ }
+
+ if (consumed_firmware_offset != firmware_size) {
+@@ -437,6 +571,70 @@ exit:
+ return err;
+ }
+
++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev)
++{
++ const struct firmware *firmware = NULL;
++ firmware_header_t *app_firmware_header = NULL;
++ secure_boot_certificate_t *firmware_cert = NULL;
++ firmware_header_t *core_firmware_header = NULL;
++ int err = 0;
++
++ err = request_firmware_direct(&firmware, files_batch->filename, dev);
++ if (err < 0) {
++ return err;
++ }
++
++ if (firmware->size > files_batch->max_size) {
++ release_firmware(firmware);
++ return -EFBIG;
++ }
++
++ if (files_batch->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) {
++ release_firmware(firmware);
++ return err;
++ }
++
++ hailo_write_app_firmware(resources, app_firmware_header, firmware_cert);
++ } else {
++ write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size);
++ }
++
++ release_firmware(firmware);
++
++ return 0;
++}
++
++int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage)
++{
++ const struct hailo_file_batch *files_batch = compat[resources->board_type].stages[stage].batch;
++ int file_index = 0;
++ int err = 0;
++
++ for (file_index = 0; file_index < MAX_FILES_PER_STAGE; file_index++)
++ {
++ if (NULL == files_batch[file_index].filename) {
++ break;
++ }
++
++ dev_notice(dev, "Writing file %s\n", files_batch[file_index].filename);
++
++ err = write_single_file(resources, &files_batch[file_index], dev);
++ if (err < 0) {
++ pr_warn("Failed to write file %s\n", files_batch[file_index].filename);
++ if (files_batch[file_index].is_mandatory) {
++ return err;
++ }
++ }
++
++ 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;
+@@ -457,10 +655,25 @@ int hailo_pcie_write_firmware(struct hai
+ return 0;
+ }
+
++// TODO: HRT-14147 - remove this function
++bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources)
++{
++ return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED;
++}
++
+ bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources)
+ {
+- u32 offset = ATR0_PCIE_BRIDGE_OFFSET + offsetof(struct hailo_atr_config, atr_trsl_addr_1);
+- u32 atr_value = hailo_resource_read32(&resources->config, offset);
++ u32 offset;
++ u32 atr_value;
++
++ // TODO: HRT-14147
++ if (HAILO_BOARD_TYPE_HAILO10H == resources->board_type) {
++ return !hailo_pcie_is_device_ready_for_boot(resources);
++ }
++
++ offset = ATR0_PCIE_BRIDGE_OFFSET + offsetof(struct hailo_atr_config, atr_trsl_addr_1);
++ atr_value = hailo_resource_read32(&resources->config, offset);
++
+ return atr_value == compat[resources->board_type].fw_addresses.atr0_trsl_addr1;
+ }
+
+@@ -516,7 +729,7 @@ void hailo_pcie_update_channel_interrupt
+ for (i = 0; i < MAX_VDMA_CHANNELS_PER_ENGINE; ++i) {
+ if (hailo_test_bit(i, &channels_bitmap)) {
+ // based on 18.5.2 "vDMA Interrupt Registers" in PLDA documentation
+- u32 offset = (i < VDMA_DEST_CHANNELS_START) ? 0 : 8;
++ u32 offset = (i & 16) ? 8 : 0;
+ hailo_set_bit((((int)i*8) / MAX_VDMA_CHANNELS_PER_ENGINE) + offset, &mask);
+ }
+ }
+@@ -531,7 +744,8 @@ void hailo_pcie_enable_interrupts(struct
+ hailo_resource_write32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF);
+ 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;
++ mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION |
++ BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED);
+ hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask);
+ }
+
+@@ -569,16 +783,10 @@ long hailo_pcie_read_firmware_log(struct
+ static int direct_memory_transfer(struct hailo_pcie_resources *resources,
+ struct hailo_memory_transfer_params *params)
+ {
+- int err = -EINVAL;
+- struct hailo_atr_config previous_atr = {0};
+-
+ if (params->address > U32_MAX) {
+ return -EFAULT;
+ }
+
+- // Store previous ATR (Read/write modify the ATR).
+- read_atr_table(resources, &previous_atr);
+-
+ switch (params->transfer_direction) {
+ case TRANSFER_READ:
+ read_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
+@@ -587,14 +795,10 @@ static int direct_memory_transfer(struct
+ write_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
+ break;
+ default:
+- err = -EINVAL;
+- goto restore_atr;
++ return -EINVAL;
+ }
+
+- err = 0;
+-restore_atr:
+- write_atr_table(resources, &previous_atr);
+- return err;
++ return 0;
+ }
+
+ int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params)
+@@ -623,6 +827,24 @@ bool hailo_pcie_is_device_connected(stru
+ return PCI_VENDOR_ID_HAILO == hailo_resource_read16(&resources->config, PCIE_CONFIG_VENDOR_OFFSET);
+ }
+
++int hailo_set_device_type(struct hailo_pcie_resources *resources)
++{
++ switch(resources->board_type) {
++ case HAILO_BOARD_TYPE_HAILO8:
++ case HAILO_BOARD_TYPE_HAILO10H_LEGACY:
++ case HAILO_BOARD_TYPE_PLUTO:
++ resources->accelerator_type = HAILO_ACCELERATOR_TYPE_NNC;
++ break;
++ case HAILO_BOARD_TYPE_HAILO10H:
++ resources->accelerator_type = HAILO_ACCELERATOR_TYPE_SOC;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
+ // On PCIe, just return the address
+ static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id)
+ {
+@@ -637,5 +859,14 @@ struct hailo_vdma_hw hailo_pcie_vdma_hw
+ .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID,
+ .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK,
+ .host_interrupts_bitmask = HAILO_PCIE_DMA_HOST_INTERRUPTS_BITMASK,
++ .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK,
++};
+
+-};
+\ No newline at end of file
++void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources)
++{
++ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
++ const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK;
++
++ // 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
+--- a/drivers/media/pci/hailo/common/pcie_common.h
++++ b/drivers/media/pci/hailo/common/pcie_common.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -14,11 +14,13 @@
+ #include "vdma_common.h"
+
+ #include <linux/types.h>
++#include <linux/firmware.h>
+
+
+ #define BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK (0x04000000)
+ #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_HOST_VDMA_SRC_IRQ_MASK (0x000000FF)
+ #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00)
+
+@@ -40,17 +42,35 @@
+ #define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4
+ #define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2
+
++typedef u32 hailo_ptr_t;
++
+ struct hailo_pcie_resources {
+ struct hailo_resource config; // BAR0
+ struct hailo_resource vdma_registers; // BAR2
+ struct hailo_resource fw_access; // BAR4
+ enum hailo_board_type board_type;
++ enum hailo_accelerator_type accelerator_type;
++};
++
++struct hailo_atr_config {
++ u32 atr_param;
++ u32 atr_src;
++ u32 atr_trsl_addr_1;
++ u32 atr_trsl_addr_2;
++ u32 atr_trsl_param;
++};
++
++enum loading_stages {
++ FIRST_STAGE = 0,
++ SECOND_STAGE = 1,
++ MAX_LOADING_STAGES = 2
+ };
+
+ enum hailo_pcie_interrupt_masks {
+ FW_CONTROL = BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK,
+ FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION,
+ DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN,
++ SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED,
+ VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK,
+ VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK
+ };
+@@ -66,6 +86,14 @@ struct hailo_config_constants {
+ size_t max_size;
+ };
+
++struct hailo_file_batch {
++ const char *filename;
++ u32 address;
++ size_t max_size;
++ bool is_mandatory;
++ bool has_header;
++};
++
+ // TODO: HRT-6144 - Align Windows/Linux to QNX
+ #ifdef __QNX__
+ enum hailo_bar_index {
+@@ -103,6 +131,7 @@ int hailo_pcie_write_firmware_control(st
+ 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);
+
+@@ -120,6 +149,17 @@ int hailo_pcie_memory_transfer(struct ha
+
+ 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);
++
++int hailo_set_device_type(struct hailo_pcie_resources *resources);
++
++u32 hailo_get_boot_status(struct hailo_pcie_resources *resources);
++
++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);
+
+ #ifdef __cplusplus
+ }
+--- a/drivers/media/pci/hailo/common/utils.h
++++ b/drivers/media/pci/hailo/common/utils.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -11,6 +11,12 @@
+ #define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); }
+ #define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos)))
+
++#define READ_BITS_AT_OFFSET(amount_bits, offset, initial_value) \
++ (((initial_value) >> (offset)) & ((1 << (amount_bits)) - 1))
++#define WRITE_BITS_AT_OFFSET(amount_bits, offset, initial_value, value) \
++ (((initial_value) & ~(((1 << (amount_bits)) - 1) << (offset))) | \
++ (((value) & ((1 << (amount_bits)) - 1)) << (offset)))
++
+ #ifdef __cplusplus
+ extern "C"
+ {
+@@ -28,6 +34,22 @@ static inline void hailo_set_bit(int nr,
+ *p |= mask;
+ }
+
++static inline uint8_t ceil_log2(uint32_t n)
++{
++ uint8_t result = 0;
++
++ if (n <= 1) {
++ return 0;
++ }
++
++ while (n > 1) {
++ result++;
++ n = (n + 1) >> 1;
++ }
++
++ return result;
++}
++
+ #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,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -17,25 +17,37 @@
+
+
+ #define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5)
+-#define CHANNEL_HOST_OFFSET(channel_index) CHANNEL_BASE_OFFSET(channel_index) + \
+- (channel_index < VDMA_DEST_CHANNELS_START ? 0 : 0x10)
+-#define CHANNEL_DEVICE_OFFSET(channel_index) CHANNEL_BASE_OFFSET(channel_index) + \
+- (channel_index < VDMA_DEST_CHANNELS_START ? 0x10 : 0)
+
+ #define CHANNEL_CONTROL_OFFSET (0x0)
++#define CHANNEL_DEPTH_ID_OFFSET (0x1)
+ #define CHANNEL_NUM_AVAIL_OFFSET (0x2)
+ #define CHANNEL_NUM_PROC_OFFSET (0x4)
+ #define CHANNEL_ERROR_OFFSET (0x8)
++#define CHANNEL_DEST_REGS_OFFSET (0x10)
+
+ #define VDMA_CHANNEL_CONTROL_START (0x1)
+ #define VDMA_CHANNEL_CONTROL_ABORT (0b00)
+ #define VDMA_CHANNEL_CONTROL_ABORT_PAUSE (0b10)
+ #define VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK (0x3)
+ #define VDMA_CHANNEL_CONTROL_START_ABORT_BITMASK (0x1)
++#define VDMA_CHANNEL_CONTROL_MASK (0xFC)
++#define VDMA_CHANNEL_CONTROL_START_RESUME (0b01)
++#define VDMA_CHANNEL_CONTROL_START_PAUSE (0b11)
++#define VDMA_CHANNEL_CONTROL_ABORT (0b00)
++#define VDMA_CHANNEL_CONTROL_ABORT_PAUSE (0b10)
++#define VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK (0x3)
++#define VDMA_CHANNEL_DESC_DEPTH_WIDTH (4)
++#define VDMA_CHANNEL_DESC_DEPTH_SHIFT (11)
++#define VDMA_CHANNEL_DATA_ID_SHIFT (8)
++#define VDMA_CHANNEL__MAX_CHECKS_CHANNEL_IS_IDLE (10000)
++#define VDMA_CHANNEL__ADDRESS_L_OFFSET (0x0A)
++#define VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET (0x8)
++#define VDMA_CHANNEL__ADDRESS_H_OFFSET (0x0C)
+
+ #define DESCRIPTOR_PAGE_SIZE_SHIFT (8)
+ #define DESCRIPTOR_DESC_CONTROL (0x2)
+ #define DESCRIPTOR_ADDR_L_MASK (0xFFFFFFC0)
++#define DESCRIPTOR_LIST_MAX_DEPTH (16)
+
+ #define DESCRIPTOR_DESC_STATUS_DONE_BIT (0x0)
+ #define DESCRIPTOR_DESC_STATUS_ERROR_BIT (0x1)
+@@ -46,10 +58,14 @@
+ #define DESC_REQUEST_IRQ_PROCESSED (1 << 2)
+ #define DESC_REQUEST_IRQ_ERR (1 << 3)
+
++#define VDMA_CHANNEL_NUM_PROCESSED_WIDTH (16)
++#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)
+@@ -146,18 +162,7 @@ void hailo_vdma_program_descriptor(struc
+
+ static u8 get_channel_id(u8 channel_index)
+ {
+- if (channel_index < VDMA_DEST_CHANNELS_START) {
+- // H2D channel
+- return channel_index;
+- }
+- else if ((channel_index >= VDMA_DEST_CHANNELS_START) &&
+- (channel_index < MAX_VDMA_CHANNELS_PER_ENGINE)) {
+- // D2H channel
+- return channel_index - VDMA_DEST_CHANNELS_START;
+- }
+- else {
+- return INVALID_VDMA_CHANNEL;
+- }
++ return (channel_index < MAX_VDMA_CHANNELS_PER_ENGINE) ? (channel_index & 0xF) : INVALID_VDMA_CHANNEL;
+ }
+
+ static int program_descriptors_in_chunk(
+@@ -198,12 +203,36 @@ static int program_descriptors_in_chunk(
+ return (int)desc_per_chunk;
+ }
+
+-int hailo_vdma_program_descriptors_list(
++static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw,
++ enum hailo_vdma_interrupts_domain interrupts_domain, bool is_debug)
++{
++ unsigned long bitmask = 0;
++
++ if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE & interrupts_domain)) {
++ bitmask |= vdma_hw->device_interrupts_bitmask;
++ }
++ if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_HOST & interrupts_domain)) {
++ bitmask |= vdma_hw->host_interrupts_bitmask;
++ }
++
++ if (bitmask != 0) {
++ bitmask |= DESC_REQUEST_IRQ_PROCESSED | DESC_REQUEST_IRQ_ERR;
++ if (is_debug) {
++ bitmask |= DESC_STATUS_REQ | DESC_STATUS_REQ_ERR;
++ }
++ }
++
++ return bitmask;
++}
++
++static int bind_and_program_descriptors_list(
+ struct hailo_vdma_hw *vdma_hw,
+ struct hailo_vdma_descriptors_list *desc_list,
+ u32 starting_desc,
+ struct hailo_vdma_mapped_transfer_buffer *buffer,
+- u8 channel_index)
++ u8 channel_index,
++ enum hailo_vdma_interrupts_domain last_desc_interrupts,
++ bool is_debug)
+ {
+ const u8 channel_id = get_channel_id(channel_index);
+ int desc_programmed = 0;
+@@ -260,9 +289,49 @@ int hailo_vdma_program_descriptors_list(
+ return -EFAULT;
+ }
+
++ desc_list->desc_list[(starting_desc - 1) % desc_list->desc_count].PageSize_DescControl |=
++ get_interrupts_bitmask(vdma_hw, last_desc_interrupts, is_debug);
++
+ return desc_programmed;
+ }
+
++static int program_last_desc(
++ struct hailo_vdma_hw *vdma_hw,
++ struct hailo_vdma_descriptors_list *desc_list,
++ u32 starting_desc,
++ struct hailo_vdma_mapped_transfer_buffer *transfer_buffer,
++ enum hailo_vdma_interrupts_domain last_desc_interrupts,
++ bool is_debug)
++{
++ u8 control = (u8)(DESCRIPTOR_DESC_CONTROL | get_interrupts_bitmask(vdma_hw, last_desc_interrupts, is_debug));
++ u32 total_descs = DIV_ROUND_UP(transfer_buffer->size, desc_list->desc_page_size);
++ u32 last_desc = (starting_desc + total_descs - 1) % desc_list->desc_count;
++ u32 last_desc_size = transfer_buffer->size - (total_descs - 1) * desc_list->desc_page_size;
++
++ // Configure only last descriptor with residue size
++ desc_list->desc_list[last_desc].PageSize_DescControl = (u32)
++ ((last_desc_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + control);
++ return (int)total_descs;
++}
++
++int hailo_vdma_program_descriptors_list(
++ struct hailo_vdma_hw *vdma_hw,
++ struct hailo_vdma_descriptors_list *desc_list,
++ u32 starting_desc,
++ struct hailo_vdma_mapped_transfer_buffer *buffer,
++ bool should_bind,
++ u8 channel_index,
++ enum hailo_vdma_interrupts_domain last_desc_interrupts,
++ bool is_debug)
++{
++ return should_bind ?
++ bind_and_program_descriptors_list(vdma_hw, desc_list, starting_desc,
++ buffer, channel_index, last_desc_interrupts, is_debug) :
++ program_last_desc(vdma_hw, desc_list, starting_desc, buffer,
++ last_desc_interrupts, is_debug);
++}
++
++
+ static bool channel_control_reg_is_active(u8 control)
+ {
+ return (control & VDMA_CHANNEL_CONTROL_START_ABORT_BITMASK) == VDMA_CHANNEL_CONTROL_START;
+@@ -270,12 +339,12 @@ static bool channel_control_reg_is_activ
+
+ static int validate_channel_state(struct hailo_vdma_channel *channel)
+ {
+- const u8 control = ioread8(channel->host_regs + CHANNEL_CONTROL_OFFSET);
+- const u16 hw_num_avail = ioread16(channel->host_regs + CHANNEL_NUM_AVAIL_OFFSET);
++ u32 host_regs_value = ioread32(channel->host_regs);
++ const u8 control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value);
++ const u16 hw_num_avail = READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, host_regs_value);
+
+ if (!channel_control_reg_is_active(control)) {
+- pr_err("Channel %d is not active\n", channel->index);
+- return -EBUSY;
++ return -ECONNRESET;
+ }
+
+ if (hw_num_avail != channel->state.num_avail) {
+@@ -287,51 +356,16 @@ static int validate_channel_state(struct
+ return 0;
+ }
+
+-static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw,
+- enum hailo_vdma_interrupts_domain interrupts_domain, bool is_debug)
+-{
+- unsigned long bitmask = 0;
+-
+- if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_DEVICE & interrupts_domain)) {
+- bitmask |= vdma_hw->device_interrupts_bitmask;
+- }
+- if (0 != (HAILO_VDMA_INTERRUPTS_DOMAIN_HOST & interrupts_domain)) {
+- bitmask |= vdma_hw->host_interrupts_bitmask;
+- }
+-
+- if (bitmask != 0) {
+- bitmask |= DESC_REQUEST_IRQ_PROCESSED | DESC_REQUEST_IRQ_ERR;
+- if (is_debug) {
+- bitmask |= DESC_STATUS_REQ | DESC_STATUS_REQ_ERR;
+- }
+- }
+-
+- return bitmask;
+-}
+-
+ static void set_num_avail(u8 __iomem *host_regs, u16 num_avail)
+ {
+- iowrite16(num_avail, host_regs + CHANNEL_NUM_AVAIL_OFFSET);
++ u32 host_regs_val = ioread32(host_regs);
++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, host_regs_val, num_avail),
++ host_regs);
+ }
+
+ static u16 get_num_proc(u8 __iomem *host_regs)
+ {
+- return ioread16(host_regs + CHANNEL_NUM_PROC_OFFSET);
+-}
+-
+-static int program_last_desc(
+- struct hailo_vdma_descriptors_list *desc_list,
+- u32 starting_desc,
+- struct hailo_vdma_mapped_transfer_buffer *transfer_buffer)
+-{
+- u32 total_descs = DIV_ROUND_UP(transfer_buffer->size, desc_list->desc_page_size);
+- u32 last_desc = (starting_desc + total_descs - 1) % desc_list->desc_count;
+- u32 last_desc_size = transfer_buffer->size - (total_descs - 1) * desc_list->desc_page_size;
+-
+- // Configure only last descriptor with residue size
+- desc_list->desc_list[last_desc].PageSize_DescControl = (u32)
+- ((last_desc_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + DESCRIPTOR_DESC_CONTROL);
+- return (int)total_descs;
++ return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(host_regs + CHANNEL_NUM_PROC_OFFSET));
+ }
+
+ int hailo_vdma_launch_transfer(
+@@ -365,6 +399,11 @@ int hailo_vdma_launch_transfer(
+ return -EINVAL;
+ }
+
++ ret = validate_channel_state(channel);
++ if (ret < 0) {
++ return ret;
++ }
++
+ if (channel->state.num_avail != (u16)starting_desc) {
+ pr_err("Channel %d state out of sync. num available is %d, expected %d\n",
+ channel->index, channel->state.num_avail, (u16)starting_desc);
+@@ -376,25 +415,17 @@ int hailo_vdma_launch_transfer(
+ return -EINVAL;
+ }
+
+- if (is_debug) {
+- ret = validate_channel_state(channel);
+- if (ret < 0) {
+- return ret;
+- }
+- }
+-
+ BUILD_BUG_ON_MSG((HAILO_MAX_BUFFERS_PER_SINGLE_TRANSFER + 1) != ARRAY_SIZE(ongoing_transfer.dirty_descs),
+ "Unexpected amount of dirty descriptors");
+ ongoing_transfer.dirty_descs_count = buffers_count + 1;
+ ongoing_transfer.dirty_descs[0] = (u16)starting_desc;
+
+ for (i = 0; i < buffers_count; i++) {
+- ret = should_bind ?
+- hailo_vdma_program_descriptors_list(vdma_hw, desc_list, starting_desc, &buffers[i], channel->index) :
+- program_last_desc(desc_list, starting_desc, &buffers[i]);
+- if (ret < 0) {
+- return ret;
+- }
++ ret = hailo_vdma_program_descriptors_list(vdma_hw, desc_list,
++ starting_desc, &buffers[i], should_bind, channel->index,
++ (i == (buffers_count - 1) ? last_desc_interrupts : HAILO_VDMA_INTERRUPTS_DOMAIN_NONE),
++ is_debug);
++
+ total_descs += ret;
+ last_desc = (starting_desc + ret - 1) % desc_list->desc_count;
+ starting_desc = (starting_desc + ret) % desc_list->desc_count;
+@@ -406,8 +437,6 @@ int hailo_vdma_launch_transfer(
+
+ desc_list->desc_list[first_desc].PageSize_DescControl |=
+ get_interrupts_bitmask(vdma_hw, first_interrupts_domain, is_debug);
+- desc_list->desc_list[last_desc].PageSize_DescControl |=
+- get_interrupts_bitmask(vdma_hw, last_desc_interrupts, is_debug);
+
+ ongoing_transfer.last_desc = (u16)last_desc;
+ ongoing_transfer.is_debug = is_debug;
+@@ -477,8 +506,21 @@ static void channel_state_init(struct ha
+ state->desc_count_mask = U32_MAX;
+ }
+
++static u8 __iomem *get_channel_regs(u8 __iomem *regs_base, u8 channel_index, bool is_host_side, u32 src_channels_bitmask)
++{
++ // Check if getting host side regs or device side
++ u8 __iomem *channel_regs_base = regs_base + CHANNEL_BASE_OFFSET(channel_index);
++ if (is_host_side) {
++ return hailo_test_bit(channel_index, &src_channels_bitmask) ? channel_regs_base :
++ (channel_regs_base + CHANNEL_DEST_REGS_OFFSET);
++ } else {
++ return hailo_test_bit(channel_index, &src_channels_bitmask) ? (channel_regs_base + CHANNEL_DEST_REGS_OFFSET) :
++ channel_regs_base;
++ }
++}
++
+ void hailo_vdma_engine_init(struct hailo_vdma_engine *engine, u8 engine_index,
+- const struct hailo_resource *channel_registers)
++ const struct hailo_resource *channel_registers, u32 src_channels_bitmask)
+ {
+ u8 channel_index = 0;
+ struct hailo_vdma_channel *channel;
+@@ -489,8 +531,8 @@ void hailo_vdma_engine_init(struct hailo
+
+ for_each_vdma_channel(engine, channel, channel_index) {
+ u8 __iomem *regs_base = (u8 __iomem *)channel_registers->address;
+- channel->host_regs = regs_base + CHANNEL_HOST_OFFSET(channel_index);
+- channel->device_regs = regs_base + CHANNEL_DEVICE_OFFSET(channel_index);
++ channel->host_regs = get_channel_regs(regs_base, channel_index, true, src_channels_bitmask);
++ channel->device_regs = get_channel_regs(regs_base, channel_index, false, src_channels_bitmask);
+ channel->index = channel_index;
+ channel->timestamp_measure_enabled = false;
+
+@@ -502,7 +544,15 @@ void hailo_vdma_engine_init(struct hailo
+ }
+ }
+
+-void hailo_vdma_engine_enable_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap,
++/**
++ * Enables the given channels bitmap in the given engine. Allows launching transfer
++ * and reading interrupts from the channels.
++ *
++ * @param engine - dma engine.
++ * @param bitmap - channels bitmap to enable.
++ * @param measure_timestamp - if set, allow interrupts timestamp measure.
++ */
++void hailo_vdma_engine_enable_channels(struct hailo_vdma_engine *engine, u32 bitmap,
+ bool measure_timestamp)
+ {
+ struct hailo_vdma_channel *channel = NULL;
+@@ -518,7 +568,14 @@ void hailo_vdma_engine_enable_channel_in
+ engine->enabled_channels |= bitmap;
+ }
+
+-void hailo_vdma_engine_disable_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap)
++/**
++ * Disables the given channels bitmap in the given engine.
++ *
++ * @param engine - dma engine.
++ * @param bitmap - channels bitmap to enable.
++ * @param measure_timestamp - if set, allow interrupts timestamp measure.
++ */
++void hailo_vdma_engine_disable_channels(struct hailo_vdma_engine *engine, u32 bitmap)
+ {
+ struct hailo_vdma_channel *channel = NULL;
+ u8 channel_index = 0;
+@@ -582,11 +639,11 @@ void hailo_vdma_engine_set_channel_inter
+ }
+
+ static void fill_channel_irq_data(struct hailo_vdma_interrupts_channel_data *irq_data,
+- struct hailo_vdma_engine *engine, struct hailo_vdma_channel *channel, u16 num_proc,
++ struct hailo_vdma_engine *engine, struct hailo_vdma_channel *channel, u8 transfers_completed,
+ bool validation_success)
+ {
+- u8 host_control = ioread8(channel->host_regs + CHANNEL_CONTROL_OFFSET);
+- u8 device_control = ioread8(channel->device_regs + CHANNEL_CONTROL_OFFSET);
++ u8 host_control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(channel->host_regs));
++ u8 device_control = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(channel->device_regs));
+
+ irq_data->engine_index = engine->index;
+ irq_data->channel_index = channel->index;
+@@ -594,9 +651,9 @@ static void fill_channel_irq_data(struct
+ irq_data->is_active = channel_control_reg_is_active(host_control) &&
+ channel_control_reg_is_active(device_control);
+
+- irq_data->host_num_processed = num_proc;
+- irq_data->host_error = ioread8(channel->host_regs + CHANNEL_ERROR_OFFSET);
+- irq_data->device_error = ioread8(channel->device_regs + CHANNEL_ERROR_OFFSET);
++ irq_data->transfers_completed = transfers_completed;
++ irq_data->host_error = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, 0, ioread32(channel->host_regs + CHANNEL_ERROR_OFFSET));
++ irq_data->device_error = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, 0, ioread32(channel->device_regs + CHANNEL_ERROR_OFFSET));
+ irq_data->validation_success = validation_success;
+ }
+
+@@ -635,7 +692,12 @@ int hailo_vdma_engine_fill_irq_data(stru
+ bool validation_success = true;
+
+ for_each_vdma_channel(engine, channel, channel_index) {
++ u8 transfers_completed = 0;
+ u16 hw_num_proc = U16_MAX;
++
++ BUILD_BUG_ON_MSG(HAILO_VDMA_MAX_ONGOING_TRANSFERS >= U8_MAX,
++ "HAILO_VDMA_MAX_ONGOING_TRANSFERS must be less than U8_MAX to use transfers_completed as u8");
++
+ if (!hailo_test_bit(channel->index, &irq_channels_bitmap)) {
+ continue;
+ }
+@@ -673,12 +735,143 @@ int hailo_vdma_engine_fill_irq_data(stru
+ channel->state.num_proc = (u16)((cur_transfer->last_desc + 1) & channel->state.desc_count_mask);
+
+ ongoing_transfer_pop(channel, NULL);
++ transfers_completed++;
+ }
+
+ fill_channel_irq_data(&irq_data->irq_data[irq_data->channels_count],
+- engine, channel, hw_num_proc, validation_success);
++ engine, channel, transfers_completed, validation_success);
+ irq_data->channels_count++;
+ }
+
+ return 0;
++}
++
++// For all these functions - best way to optimize might be to not call the function when need to pause and then abort,
++// Rather read value once and maybe save
++// This function reads and writes the register - should try to make more optimized in future
++static void start_vdma_control_register(u8 __iomem *host_regs)
++{
++ u32 host_regs_value = ioread32(host_regs);
++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value,
++ VDMA_CHANNEL_CONTROL_START_RESUME), host_regs);
++}
++
++static void hailo_vdma_channel_pause(u8 __iomem *host_regs)
++{
++ u32 host_regs_value = ioread32(host_regs);
++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value,
++ VDMA_CHANNEL_CONTROL_START_PAUSE), host_regs);
++}
++
++// This function reads and writes the register - should try to make more optimized in future
++static void hailo_vdma_channel_abort(u8 __iomem *host_regs)
++{
++ u32 host_regs_value = ioread32(host_regs);
++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, host_regs_value,
++ VDMA_CHANNEL_CONTROL_ABORT), host_regs);
++}
++
++int hailo_vdma_start_channel(u8 __iomem *host_regs, uint64_t desc_dma_address, uint8_t desc_depth,
++ uint8_t data_id)
++{
++ u16 dma_address_l = 0;
++ u32 dma_address_h = 0;
++ u32 desc_depth_data_id = 0;
++
++ if (((desc_dma_address & 0xFFFF) != 0) ||
++ (desc_depth > DESCRIPTOR_LIST_MAX_DEPTH)) {
++ return -EINVAL;
++ }
++
++ // According to spec, depth 16 is equivalent to depth 0.
++ if (DESCRIPTOR_LIST_MAX_DEPTH == desc_depth) {
++ desc_depth = 0;
++ }
++
++ // Stop old channel state
++ hailo_vdma_stop_channel(host_regs);
++
++ // Configure address, depth and id
++ dma_address_l = (uint16_t)((desc_dma_address >> 16) & 0xFFFF);
++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, (VDMA_CHANNEL__ADDRESS_L_OFFSET -
++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET) * BITS_IN_BYTE, ioread32(host_regs +
++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET), dma_address_l), host_regs + VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET);
++
++ dma_address_h = (uint32_t)(desc_dma_address >> 32);
++ iowrite32(dma_address_h, host_regs + VDMA_CHANNEL__ADDRESS_H_OFFSET);
++
++ desc_depth_data_id = (uint32_t)(desc_depth << VDMA_CHANNEL_DESC_DEPTH_SHIFT) |
++ (data_id << VDMA_CHANNEL_DATA_ID_SHIFT);
++ iowrite32(desc_depth_data_id, host_regs);
++
++ start_vdma_control_register(host_regs);
++
++ return 0;
++}
++
++static bool hailo_vdma_channel_is_idle(u8 __iomem *host_regs, size_t host_side_max_desc_count)
++{
++ // Num processed and ongoing are next to each other in the memory.
++ // Reading them both in order to save BAR reads.
++ u32 host_side_num_processed_ongoing = ioread32(host_regs + CHANNEL_NUM_PROC_OFFSET);
++ u16 host_side_num_processed = (host_side_num_processed_ongoing & VDMA_CHANNEL_NUM_PROCESSED_MASK);
++ u16 host_side_num_ongoing = (host_side_num_processed_ongoing >> VDMA_CHANNEL_NUM_PROCESSED_WIDTH) &
++ VDMA_CHANNEL_NUM_ONGOING_MASK;
++
++ if ((host_side_num_processed % host_side_max_desc_count) == (host_side_num_ongoing % host_side_max_desc_count)) {
++ return true;
++ }
++
++ return false;
++}
++
++static int hailo_vdma_wait_until_channel_idle(u8 __iomem *host_regs)
++{
++ bool is_idle = false;
++ uint32_t check_counter = 0;
++
++ u8 depth = (uint8_t)(READ_BITS_AT_OFFSET(VDMA_CHANNEL_DESC_DEPTH_WIDTH, VDMA_CHANNEL_DESC_DEPTH_SHIFT,
++ ioread32(host_regs)));
++ size_t host_side_max_desc_count = (size_t)(1 << depth);
++
++ for (check_counter = 0; check_counter < VDMA_CHANNEL__MAX_CHECKS_CHANNEL_IS_IDLE; check_counter++) {
++ is_idle = hailo_vdma_channel_is_idle(host_regs, host_side_max_desc_count);
++ if (is_idle) {
++ return 0;
++ }
++ }
++
++ return -ETIMEDOUT;
++}
++
++void hailo_vdma_stop_channel(u8 __iomem *host_regs)
++{
++ int err = 0;
++ u8 host_side_channel_regs = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(host_regs));
++
++ if ((host_side_channel_regs & VDMA_CHANNEL_CONTROL_START_ABORT_PAUSE_RESUME_BITMASK) == VDMA_CHANNEL_CONTROL_ABORT_PAUSE) {
++ // The channel is aborted (we set the channel to VDMA_CHANNEL_CONTROL_ABORT_PAUSE at the end of this function)
++ return;
++ }
++
++ // Pause the channel
++ // The channel is paused to allow for "all transfers from fetched descriptors..." to be "...completed"
++ // (from PLDA PCIe refernce manual, "9.2.5 Starting a Channel and Transferring Data")
++ hailo_vdma_channel_pause(host_regs);
++
++ // Even if channel is stuck and not idle, force abort and return error in the end
++ err = hailo_vdma_wait_until_channel_idle(host_regs);
++ // Success oriented - if error occured print error but still abort channel
++ if (err < 0) {
++ pr_err("Timeout occured while waiting for channel to become idle\n");
++ }
++
++ // Abort the channel (even of hailo_vdma_wait_until_channel_idle function fails)
++ hailo_vdma_channel_abort(host_regs);
++}
++
++bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel)
++{
++ return is_input_channel ? hailo_test_bit(channel_index, &src_channels_bitmask) :
++ (!hailo_test_bit(channel_index, &src_channels_bitmask));
+ }
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/vdma_common.h
++++ b/drivers/media/pci/hailo/common/vdma_common.h
+@@ -1,4 +1,4 @@
+-// SPDX-License-Identifier: GPL-2.0
++// SPDX-License-Identifier: MIT
+ /**
+ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+ **/
+@@ -30,8 +30,8 @@ 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.
+- u16 desc_page_size;
++ u32 desc_count; // Must be power of 2 if is_circular is set.
++ u16 desc_page_size;
+ bool is_circular;
+ };
+
+@@ -127,6 +127,9 @@ struct hailo_vdma_hw {
+ // Bitmask needed to set on each descriptor to enable interrupts (either host/device).
+ unsigned long host_interrupts_bitmask;
+ unsigned long device_interrupts_bitmask;
++
++ // Bitmask for each vdma hw, which channels are src side by index (on pcie/dram - 0x0000FFFF, pci ep - 0xFFFF0000)
++ u32 src_channels_bitmask;
+ };
+
+ #define _for_each_element_array(array, size, element, index) \
+@@ -147,7 +150,11 @@ void hailo_vdma_program_descriptor(struc
+ * @param starting_desc index of the first descriptor to program. If the list
+ * is circular, this function may wrap around the list.
+ * @param buffer buffer to program to the descriptors list.
++ * @param should_bind If false, assumes the buffer was already bound to the
++ * desc list. Used for optimization.
+ * @param channel_index channel index of the channel attached.
++ * @param last_desc_interrupts - interrupts settings on last descriptor.
++ * @param is_debug program descriptors for debug run.
+ *
+ * @return On success - the amount of descriptors programmed, negative value on error.
+ */
+@@ -156,7 +163,10 @@ int hailo_vdma_program_descriptors_list(
+ struct hailo_vdma_descriptors_list *desc_list,
+ u32 starting_desc,
+ struct hailo_vdma_mapped_transfer_buffer *buffer,
+- u8 channel_index);
++ bool should_bind,
++ u8 channel_index,
++ enum hailo_vdma_interrupts_domain last_desc_interrupts,
++ bool is_debug);
+
+ /**
+ * Launch a transfer on some vdma channel. Includes:
+@@ -191,14 +201,12 @@ int hailo_vdma_launch_transfer(
+ bool is_debug);
+
+ void hailo_vdma_engine_init(struct hailo_vdma_engine *engine, u8 engine_index,
+- const struct hailo_resource *channel_registers);
++ const struct hailo_resource *channel_registers, u32 src_channels_bitmask);
+
+-// enable/disable channels interrupt (does not update interrupts mask because the
+-// implementation is different between PCIe and DRAM DMA. To support it we
+-// can add some ops struct to the engine).
+-void hailo_vdma_engine_enable_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap,
++void hailo_vdma_engine_enable_channels(struct hailo_vdma_engine *engine, u32 bitmap,
+ bool measure_timestamp);
+-void hailo_vdma_engine_disable_channel_interrupts(struct hailo_vdma_engine *engine, u32 bitmap);
++
++void hailo_vdma_engine_disable_channels(struct hailo_vdma_engine *engine, u32 bitmap);
+
+ void hailo_vdma_engine_push_timestamps(struct hailo_vdma_engine *engine, u32 bitmap);
+ int hailo_vdma_engine_read_timestamps(struct hailo_vdma_engine *engine,
+@@ -237,6 +245,12 @@ int hailo_vdma_engine_fill_irq_data(stru
+ struct hailo_vdma_engine *engine, u32 irq_channels_bitmap,
+ transfer_done_cb_t transfer_done, void *transfer_done_opaque);
+
++int hailo_vdma_start_channel(u8 __iomem *host_regs, uint64_t desc_dma_address, uint8_t desc_depth, uint8_t data_id);
++
++void hailo_vdma_stop_channel(u8 __iomem *host_regs);
++
++bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/drivers/media/pci/hailo/src/fops.c
++++ b/drivers/media/pci/hailo/src/fops.c
+@@ -19,7 +19,6 @@
+ #include <linux/sched/signal.h>
+ #endif
+
+-#include "hailo_pcie_version.h"
+ #include "utils.h"
+ #include "fops.h"
+ #include "vdma_common.h"
+@@ -27,6 +26,7 @@
+ #include "vdma/memory.h"
+ #include "vdma/ioctl.h"
+ #include "utils/compact.h"
++#include "pci_soc_ioctl.h"
+
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 )
+@@ -210,69 +210,66 @@ l_exit:
+
+ int hailo_pcie_fops_release(struct inode *inode, struct file *filp)
+ {
+- struct hailo_pcie_board *pBoard = (struct hailo_pcie_board *)filp->private_data;
++ struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data;
+ struct hailo_file_context *context = NULL;
+
+ u32 major = MAJOR(inode->i_rdev);
+ u32 minor = MINOR(inode->i_rdev);
+
+- if (pBoard) {
+- hailo_info(pBoard, "(%d: %d-%d): fops_release\n", current->tgid, major, minor);
++ if (board) {
++ hailo_info(board, "(%d: %d-%d): fops_release\n", current->tgid, major, minor);
+
+- if (down_interruptible(&pBoard->mutex)) {
+- hailo_err(pBoard, "fops_release down_interruptible failed");
+- return -ERESTARTSYS;
+- }
+
+- context = find_file_context(pBoard, filp);
++ down(&board->mutex);
++
++ context = find_file_context(board, filp);
+ if (NULL == context) {
+- hailo_err(pBoard, "Invalid driver state, file context does not exist\n");
+- up(&pBoard->mutex);
++ hailo_err(board, "Invalid driver state, file context does not exist\n");
++ up(&board->mutex);
+ return -EINVAL;
+ }
+
+ if (false == context->is_valid) {
+ // File context is invalid, but open. It's OK to continue finalize and release it.
+- hailo_err(pBoard, "Invalid file context\n");
++ hailo_err(board, "Invalid file context\n");
+ }
+
+- hailo_pcie_clear_notification_wait_list(pBoard, filp);
++ hailo_pcie_clear_notification_wait_list(board, filp);
+
+- if (filp == pBoard->vdma.used_by_filp) {
+- if (hailo_pcie_driver_down(pBoard)) {
+- hailo_err(pBoard, "Failed sending FW shutdown event");
++ if (filp == board->vdma.used_by_filp) {
++ if (hailo_pcie_driver_down(board)) {
++ hailo_err(board, "Failed sending FW shutdown event");
+ }
+ }
+
+- hailo_vdma_file_context_finalize(&context->vdma_context, &pBoard->vdma, filp);
++ hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp);
+ release_file_context(context);
+
+- if (atomic_dec_and_test(&pBoard->ref_count)) {
++ if (atomic_dec_and_test(&board->ref_count)) {
+ // Disable interrupts
+- hailo_disable_interrupts(pBoard);
++ hailo_disable_interrupts(board);
+
+ if (power_mode_enabled()) {
+- if (pBoard->pDev && pci_set_power_state(pBoard->pDev, PCI_D3hot) < 0) {
+- hailo_err(pBoard, "Failed setting power state to D3hot");
++ if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) {
++ hailo_err(board, "Failed setting power state to D3hot");
+ }
+ }
+
+ // deallocate board if already removed
+- if (!pBoard->pDev) {
+- hailo_dbg(pBoard, "fops_close, freed board\n");
+- up(&pBoard->mutex);
+- kfree(pBoard);
+- pBoard = NULL;
++ if (!board->pDev) {
++ hailo_dbg(board, "fops_release, freed board\n");
++ up(&board->mutex);
++ kfree(board);
++ board = NULL;
+ } else {
+-
+- hailo_dbg(pBoard, "fops_close, released resources for board\n");
+- up(&pBoard->mutex);
++ hailo_dbg(board, "fops_release, released resources for board\n");
++ up(&board->mutex);
+ }
+ } else {
+- up(&pBoard->mutex);
++ up(&board->mutex);
+ }
+
+- hailo_dbg(pBoard, "(%d: %d-%d): fops_close: SUCCESS on /dev/hailo%d\n", current->tgid,
++ hailo_dbg(board, "(%d: %d-%d): fops_release: SUCCESS on /dev/hailo%d\n", current->tgid,
+ major, minor, minor);
+ }
+
+@@ -394,6 +391,10 @@ irqreturn_t hailo_irqhandler(int irq, vo
+ }
+ }
+
++ if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) {
++ complete_all(&board->soc_connect_accepted);
++ }
++
+ if (0 != irq_source.vdma_channels_bitmap) {
+ hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX,
+ irq_source.vdma_channels_bitmap);
+@@ -602,26 +603,35 @@ static long hailo_query_driver_info(stru
+ return 0;
+ }
+
+-static long hailo_general_ioctl(struct hailo_file_context *context, struct hailo_pcie_board *board,
+- unsigned int cmd, unsigned long arg, struct file *filp, bool *should_up_board_mutex)
++static long hailo_general_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg)
+ {
+ switch (cmd) {
+ case HAILO_MEMORY_TRANSFER:
+ return hailo_memory_transfer_ioctl(board, arg);
++ case HAILO_QUERY_DEVICE_PROPERTIES:
++ return hailo_query_device_properties(board, arg);
++ case HAILO_QUERY_DRIVER_INFO:
++ return hailo_query_driver_info(board, arg);
++ default:
++ hailo_err(board, "Invalid general ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++ return -ENOTTY;
++ }
++}
++
++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_QUERY_DEVICE_PROPERTIES:
+- return hailo_query_device_properties(board, arg);
+- case HAILO_QUERY_DRIVER_INFO:
+- return hailo_query_driver_info(board, arg);
+ case HAILO_READ_LOG:
+ return hailo_read_log_ioctl(board, arg);
+ default:
+- hailo_err(board, "Invalid general ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
+ return -ENOTTY;
+ }
+ }
+@@ -673,12 +683,28 @@ long hailo_pcie_fops_unlockedioctl(struc
+
+ switch (_IOC_TYPE(cmd)) {
+ case HAILO_GENERAL_IOCTL_MAGIC:
+- err = hailo_general_ioctl(context, board, cmd, arg, filp, &should_up_board_mutex);
++ err = hailo_general_ioctl(board, cmd, arg);
+ break;
+ case HAILO_VDMA_IOCTL_MAGIC:
+ err = hailo_vdma_ioctl(&context->vdma_context, &board->vdma, cmd, arg, filp, &board->mutex,
+ &should_up_board_mutex);
+ break;
++ case HAILO_SOC_IOCTL_MAGIC:
++ if (HAILO_ACCELERATOR_TYPE_SOC != board->pcie_resources.accelerator_type) {
++ 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);
++ }
++ break;
++ case HAILO_NNC_IOCTL_MAGIC:
++ if (HAILO_ACCELERATOR_TYPE_NNC != board->pcie_resources.accelerator_type) {
++ hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd));
++ err = -EINVAL;
++ } else {
++ err = hailo_nnc_ioctl(board, cmd, arg, filp, &should_up_board_mutex);
++ }
++ break;
+ default:
+ hailo_err(board, "Invalid ioctl type %d\n", _IOC_TYPE(cmd));
+ err = -ENOTTY;
+--- a/drivers/media/pci/hailo/src/fops.h
++++ b/drivers/media/pci/hailo/src/fops.h
+@@ -11,6 +11,7 @@ int hailo_pcie_fops_release(struct inode
+ 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)
+ irqreturn_t hailo_irqhandler(int irq, void* dev_id, struct pt_regs *regs);
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/pci_soc_ioctl.c
+@@ -0,0 +1,155 @@
++// 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(¶ms, (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, ¶ms, 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(¶ms, (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
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/pci_soc_ioctl.h
+@@ -0,0 +1,19 @@
++// 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
+--- a/drivers/media/pci/hailo/src/pcie.c
++++ b/drivers/media/pci/hailo/src/pcie.c
+@@ -20,7 +20,6 @@
+
+ #define KERNEL_CODE 1
+
+-#include "hailo_pcie_version.h"
+ #include "hailo_ioctl_common.h"
+ #include "pcie.h"
+ #include "fops.h"
+@@ -45,6 +44,7 @@ enum hailo_allocate_driver_buffer_driver
+ 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;
+
+ #define DEVICE_NODE_NAME "hailo"
+ static int char_major = 0;
+@@ -322,7 +322,7 @@ static int hailo_write_config(struct hai
+
+ static bool wait_for_firmware_completion(struct completion *fw_load_completion)
+ {
+- return (0 != wait_for_completion_timeout(fw_load_completion, FIRMWARE_WAIT_TIMEOUT_MS));
++ 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,
+@@ -330,6 +330,7 @@ static int hailo_load_firmware(struct ha
+ {
+ const struct firmware *firmware = NULL;
+ int err = 0;
++ u32 boot_status = 0;
+
+ if (hailo_pcie_is_firmware_loaded(resources)) {
+ hailo_dev_warn(dev, "Firmware was already loaded\n");
+@@ -368,7 +369,8 @@ static int hailo_load_firmware(struct ha
+ release_firmware(firmware);
+
+ if (!wait_for_firmware_completion(fw_load_completion)) {
+- hailo_dev_err(dev, "Timeout waiting for firmware..\n");
++ boot_status = hailo_get_boot_status(resources);
++ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+ return -ETIMEDOUT;
+ }
+
+@@ -376,6 +378,55 @@ static int hailo_load_firmware(struct ha
+ return 0;
+ }
+
++static int hailo_load_firmware_batch(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)) {
++ hailo_dev_warn(dev, "Firmware batch was already loaded\n");
++ return 0;
++ }
++
++ init_completion(fw_load_completion);
++
++ err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE);
++ if (err < 0) {
++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
++ 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");
++
++ return 0;
++}
++
+ static int hailo_activate_board(struct hailo_pcie_board *board)
+ {
+ int err = 0;
+@@ -388,8 +439,21 @@ static int hailo_activate_board(struct h
+ return err;
+ }
+
+- err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev,
+- &board->fw_loaded_completion);
++ switch (board->pcie_resources.board_type) {
++ case HAILO_BOARD_TYPE_HAILO10H:
++ err = hailo_load_firmware_batch(&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,
++ &board->fw_loaded_completion);
++ break;
++ default:
++ hailo_err(board, "Invalid board type");
++ err = -EINVAL;
++ }
+ if (err < 0) {
+ hailo_err(board, "Firmware load failed\n");
+ hailo_disable_interrupts(board);
+@@ -513,8 +577,23 @@ static int pcie_resources_init(struct pc
+ goto failure_release_vdma_regs;
+ }
+
++
++ // There is no HAILO15 as mercury through pcie unless it's legacy mode (H15 as accelerator) or HAILO-10H
++ if (HAILO_BOARD_TYPE_HAILO15 == board_type){
++ if (true == force_hailo15_legacy_mode) {
++ board_type = HAILO_BOARD_TYPE_HAILO10H_LEGACY;
++ } else {
++ board_type = HAILO_BOARD_TYPE_HAILO10H;
++ }
++ }
++
+ resources->board_type = board_type;
+
++ err = hailo_set_device_type(resources);
++ if (err < 0) {
++ goto failure_release_fw_access;
++ }
++
+ if (!hailo_pcie_is_device_connected(resources)) {
+ pci_err(pdev, "Probing: Failed reading device BARs, device may be disconnected\n");
+ err = -ENODEV;
+@@ -676,6 +755,7 @@ 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);
+@@ -1005,6 +1085,9 @@ MODULE_PARM_DESC(force_allocation_from_d
+ module_param(force_desc_page_size, int, S_IRUGO);
+ MODULE_PARM_DESC(force_desc_page_size, "Determines the maximum DMA descriptor page size (must be a power of 2)");
+
++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_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
+@@ -70,6 +70,8 @@ struct hailo_pcie_board {
+ 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);
+--- a/drivers/media/pci/hailo/src/sysfs.c
++++ b/drivers/media/pci/hailo/src/sysfs.c
+@@ -26,9 +26,18 @@ static ssize_t device_id_show(struct dev
+ }
+ static DEVICE_ATTR_RO(device_id);
+
++static ssize_t accelerator_type_show(struct device *dev, struct device_attribute *_attr,
++ char *buf)
++{
++ struct hailo_pcie_board *board = (struct hailo_pcie_board *)dev_get_drvdata(dev);
++ return sprintf(buf, "%d", board->pcie_resources.accelerator_type);
++}
++static DEVICE_ATTR_RO(accelerator_type);
++
+ static struct attribute *hailo_dev_attrs[] = {
+ &dev_attr_board_location.attr,
+ &dev_attr_device_id.attr,
++ &dev_attr_accelerator_type.attr,
+ NULL
+ };
+
+--- a/drivers/media/pci/hailo/src/utils.c
++++ b/drivers/media/pci/hailo/src/utils.c
+@@ -8,7 +8,6 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+
+-#include "hailo_pcie_version.h"
+ #include "pcie.h"
+ #include "utils.h"
+ #include "utils/logs.h"
+--- /dev/null
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c
+@@ -0,0 +1,101 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#include "integrated_nnc_utils.h"
++#include "utils/logs.h"
++
++#include <linux/uaccess.h>
++#include <asm/io.h>
++#include <linux/of_address.h>
++#include <linux/cdev.h>
++
++int hailo_ioremap_resource(struct platform_device *pdev, struct hailo_resource *resource,
++ const char *name)
++{
++ void __iomem *address;
++ struct resource *platform_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
++ if (NULL == platform_resource) {
++ return -ENOENT;
++ }
++
++ address = devm_ioremap_resource(&pdev->dev, platform_resource);
++ if (IS_ERR(address)) {
++ return PTR_ERR(address);
++ }
++
++ resource->address = (uintptr_t)address;
++ resource->size = resource_size(platform_resource);
++
++ hailo_dev_dbg(&pdev->dev, "resource[%s]: remap %pr of %zx bytes to virtual start address %lx\n",
++ platform_resource->name, platform_resource, resource->size, (uintptr_t)address);
++
++ return 0;
++}
++
++// TODO: HRT-8475 - change to name instead of index
++int hailo_ioremap_shmem(struct platform_device *pdev, int index, struct hailo_resource *resource)
++{
++ int ret;
++ struct resource res;
++ struct device_node *shmem;
++ void __iomem * remap_ptr;
++
++ shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index);
++ 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);
++ return ret;
++ }
++ of_node_put(shmem);
++
++ remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
++ if (!remap_ptr) {
++ hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to ioremap shmem (index: %d)\n", index);
++ return -EADDRNOTAVAIL;
++ }
++
++ resource->address = (uintptr_t)remap_ptr;
++ resource->size = resource_size(&res);
++
++ return 0;
++}
++
++int direct_memory_transfer(struct platform_device *pdev, struct hailo_memory_transfer_params *params)
++{
++ int err = -EINVAL;
++ void __iomem *mem = ioremap(params->address, params->count);
++ if (NULL == mem) {
++ hailo_dev_err(&pdev->dev, "Failed ioremap %llu %zu\n", params->address, params->count);
++ return -ENOMEM;
++ }
++
++ switch (params->transfer_direction) {
++ case TRANSFER_READ:
++ memcpy_fromio(params->buffer, mem, params->count);
++ err = 0;
++ break;
++ case TRANSFER_WRITE:
++ memcpy_toio(mem, params->buffer, params->count);
++ err = 0;
++ break;
++ default:
++ hailo_dev_err(&pdev->dev, "Invalid transfer direction %d\n", (int)params->transfer_direction);
++ err = -EINVAL;
++ }
++
++ iounmap(mem);
++ return err;
++}
++
++int hailo_get_resource_physical_addr(struct platform_device *pdev, const char *name, u64 *address)
++{
++ struct resource *platform_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
++ if (NULL == platform_resource) {
++ return -ENOENT;
++ }
++
++ *address = (u64)(platform_resource->start);
++ return 0;
++}
+\ No newline at end of file
+--- /dev/null
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h
+@@ -0,0 +1,30 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#ifndef _INTEGRATED_NNC_UTILS_H_
++#define _INTEGRATED_NNC_UTILS_H_
++
++#include <linux/platform_device.h>
++#include "hailo_resource.h"
++
++#define HAILO15_CORE_CONTROL_MAILBOX_INDEX (0)
++#define HAILO15_CORE_NOTIFICATION_MAILBOX_INDEX (1)
++#define HAILO15_CORE_DRIVER_DOWN_MAILBOX_INDEX (2)
++
++#define HAILO15_CORE_CONTROL_MAILBOX_TX_SHMEM_INDEX (0)
++#define HAILO15_CORE_CONTROL_MAILBOX_RX_SHMEM_INDEX (1)
++#define HAILO15_CORE_NOTIFICATION_MAILBOX_RX_SHMEM_INDEX (2)
++
++int hailo_ioremap_resource(struct platform_device *pdev, struct hailo_resource *resource,
++ const char *name);
++
++// TODO: HRT-8475 - change to name instead of index
++int hailo_ioremap_shmem(struct platform_device *pdev, int index, struct hailo_resource *resource);
++
++int direct_memory_transfer(struct platform_device *pDev, struct hailo_memory_transfer_params *params);
++
++int hailo_get_resource_physical_addr(struct platform_device *pdev, const char *name, u64 *address);
++
++#endif /* _INTEGRATED_NNC_UTILS_H_ */
+--- a/drivers/media/pci/hailo/vdma/ioctl.c
++++ b/drivers/media/pci/hailo/vdma/ioctl.c
+@@ -12,9 +12,9 @@
+ #include <linux/uaccess.h>
+
+
+-long hailo_vdma_interrupts_enable_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_interrupts_enable_params input;
++ struct hailo_vdma_enable_channels_params input;
+ struct hailo_vdma_engine *engine = NULL;
+ u8 engine_index = 0;
+ u32 channels_bitmap = 0;
+@@ -35,7 +35,7 @@ long hailo_vdma_interrupts_enable_ioctl(
+
+ for_each_vdma_engine(controller, engine, engine_index) {
+ channels_bitmap = input.channels_bitmap_per_engine[engine_index];
+- hailo_vdma_engine_enable_channel_interrupts(engine, channels_bitmap,
++ hailo_vdma_engine_enable_channels(engine, channels_bitmap,
+ input.enable_timestamps_measure);
+ hailo_vdma_update_interrupts_mask(controller, engine_index);
+ hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n",
+@@ -45,12 +45,13 @@ long hailo_vdma_interrupts_enable_ioctl(
+ return 0;
+ }
+
+-long hailo_vdma_interrupts_disable_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_interrupts_disable_params input;
++ struct hailo_vdma_disable_channels_params input;
+ struct hailo_vdma_engine *engine = NULL;
+ u8 engine_index = 0;
+ u32 channels_bitmap = 0;
++ unsigned long irq_saved_flags = 0;
+
+ if (copy_from_user(&input, (void*)arg, sizeof(input))) {
+ hailo_dev_err(controller->dev, "copy_from_user fail\n");
+@@ -61,15 +62,21 @@ long hailo_vdma_interrupts_disable_ioctl
+ for_each_vdma_engine(controller, engine, engine_index) {
+ channels_bitmap = input.channels_bitmap_per_engine[engine_index];
+ if (channels_bitmap != (channels_bitmap & engine->enabled_channels)) {
+- hailo_dev_err(controller->dev, "Trying to disable channels that were not enabled\n");
+- return -EINVAL;
++ hailo_dev_warn(controller->dev, "Trying to disable channels that were not enabled\n");
+ }
+ }
+
+ for_each_vdma_engine(controller, engine, engine_index) {
+ channels_bitmap = input.channels_bitmap_per_engine[engine_index];
+- hailo_vdma_engine_interrupts_disable(controller, engine, engine_index,
+- channels_bitmap);
++ hailo_vdma_engine_disable_channels(engine, channels_bitmap);
++ 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);
++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
++
++ hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n",
++ engine_index, channels_bitmap);
+ }
+
+ // Wake up threads waiting
+@@ -197,7 +204,7 @@ long hailo_vdma_buffer_map_ioctl(struct
+ return -EFAULT;
+ }
+
+- hailo_dev_info(controller->dev, "address %px tgid %d size: %zu\n",
++ hailo_dev_info(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);
+@@ -209,10 +216,9 @@ long hailo_vdma_buffer_map_ioctl(struct
+ low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, buf_info.allocated_buffer_handle);
+
+ mapped_buffer = hailo_vdma_buffer_map(controller->dev,
+- buf_info.user_address, buf_info.size, direction, low_memory_buffer);
++ buf_info.user_address, buf_info.size, direction, buf_info.buffer_type, low_memory_buffer);
+ if (IS_ERR(mapped_buffer)) {
+- hailo_dev_err(controller->dev, "failed map buffer %px\n",
+- buf_info.user_address);
++ hailo_dev_err(controller->dev, "failed map buffer %lx\n", buf_info.user_address);
+ return PTR_ERR(mapped_buffer);
+ }
+
+@@ -225,7 +231,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 %px (handle %zu) is mapped\n",
++ hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n",
+ buf_info.user_address, buf_info.mapped_handle);
+ return 0;
+ }
+@@ -374,10 +380,10 @@ long hailo_desc_list_release_ioctl(struc
+ return 0;
+ }
+
+-long hailo_desc_list_bind_vdma_buffer(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller,
++long hailo_desc_list_program_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller,
+ unsigned long arg)
+ {
+- struct hailo_desc_list_bind_vdma_buffer_params configure_info;
++ struct hailo_desc_list_program_params configure_info;
+ struct hailo_vdma_buffer *mapped_buffer = NULL;
+ struct hailo_descriptors_list_buffer *descriptors_buffer = NULL;
+ struct hailo_vdma_mapped_transfer_buffer transfer_buffer = {0};
+@@ -410,7 +416,10 @@ long hailo_desc_list_bind_vdma_buffer(st
+ &descriptors_buffer->desc_list,
+ configure_info.starting_desc,
+ &transfer_buffer,
+- configure_info.channel_index
++ configure_info.should_bind,
++ configure_info.channel_index,
++ configure_info.last_interrupts_domain,
++ configure_info.is_debug
+ );
+ }
+
+@@ -683,11 +692,19 @@ long hailo_vdma_launch_transfer_ioctl(st
+ params.is_debug
+ );
+ if (ret < 0) {
+- hailo_dev_err(controller->dev, "Failed launch transfer %d\n", ret);
++ params.launch_transfer_status = ret;
++ if (-ECONNRESET != ret) {
++ hailo_dev_err(controller->dev, "Failed launch transfer %d\n", ret);
++ }
++ // Still need to copy fail status back to userspace - success oriented
++ if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) {
++ hailo_dev_err(controller->dev, "copy_to_user fail\n");
++ }
+ return ret;
+ }
+
+ params.descs_programed = ret;
++ params.launch_transfer_status = 0;
+
+ if (copy_to_user((void __user*)arg, ¶ms, sizeof(params))) {
+ hailo_dev_err(controller->dev, "copy_to_user fail\n");
+--- a/drivers/media/pci/hailo/vdma/ioctl.h
++++ b/drivers/media/pci/hailo/vdma/ioctl.h
+@@ -8,8 +8,8 @@
+
+ #include "vdma/vdma.h"
+
+-long hailo_vdma_interrupts_enable_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_vdma_interrupts_disable_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
++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_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg,
+ struct semaphore *mutex, bool *should_up_board_mutex);
+
+@@ -19,7 +19,7 @@ long hailo_vdma_buffer_sync_ioctl(struct
+
+ long hailo_desc_list_create_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
+ long hailo_desc_list_release_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_desc_list_bind_vdma_buffer(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
++long hailo_desc_list_program_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
+
+ long hailo_vdma_low_memory_buffer_alloc_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
+ long hailo_vdma_low_memory_buffer_free_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, unsigned long arg);
+--- a/drivers/media/pci/hailo/vdma/memory.c
++++ b/drivers/media/pci/hailo/vdma/memory.c
+@@ -11,27 +11,107 @@
+ #include <linux/slab.h>
+ #include <linux/scatterlist.h>
+ #include <linux/sched.h>
++#include <linux/module.h>
+
+
+ #define SGL_MAX_SEGMENT_SIZE (0x10000)
+ // See linux/mm.h
+ #define MMIO_AND_NO_PAGES_VMA_MASK (VM_IO | VM_PFNMAP)
+
+-static int map_mmio_address(void __user* user_address, u32 size, struct vm_area_struct *vma,
++static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma,
+ struct sg_table *sgt);
+-static int prepare_sg_table(struct sg_table *sg_table, void __user* user_address, u32 size,
++static int prepare_sg_table(struct sg_table *sg_table, uintptr_t user_address, u32 size,
+ struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer);
+ static void clear_sg_table(struct sg_table *sgt);
+
++#if LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 )
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
++// Import DMA_BUF namespace for needed kernels
++MODULE_IMPORT_NS(DMA_BUF);
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) */
++
++static int hailo_map_dmabuf(struct device *dev, int dmabuf_fd, enum dma_data_direction direction, struct sg_table *sgt,
++ struct hailo_dmabuf_info *dmabuf_info)
++{
++ int ret = -EINVAL;
++ struct dma_buf *dmabuf = NULL;
++ struct dma_buf_attachment *dmabuf_attachment = NULL;
++ struct sg_table *res_sgt = NULL;
++
++ dmabuf = dma_buf_get(dmabuf_fd);
++ if (IS_ERR(dmabuf)) {
++ dev_err(dev, "dma_buf_get failed, err=%ld\n", PTR_ERR(dmabuf));
++ ret = -EINVAL;
++ goto cleanup;
++ }
++
++ dmabuf_attachment = dma_buf_attach(dmabuf, dev);
++ if (IS_ERR(dmabuf_attachment)) {
++ dev_err(dev, "dma_buf_attach failed, err=%ld\n", PTR_ERR(dmabuf_attachment));
++ ret = -EINVAL;
++ goto l_buf_get;
++ }
++
++ res_sgt = dma_buf_map_attachment(dmabuf_attachment, direction);
++ if (IS_ERR(res_sgt)) {
++ dev_err(dev, "dma_buf_map_attachment failed, err=%ld\n", PTR_ERR(res_sgt));
++ goto l_buf_attach;
++ }
++
++ *sgt = *res_sgt;
++
++ dmabuf_info->dmabuf = dmabuf;
++ dmabuf_info->dmabuf_attachment = dmabuf_attachment;
++ dmabuf_info->dmabuf_sg_table = res_sgt;
++ return 0;
++
++l_buf_attach:
++ dma_buf_detach(dmabuf, dmabuf_attachment);
++l_buf_get:
++ dma_buf_put(dmabuf);
++cleanup:
++ return ret;
++}
++
++static void hailo_unmap_dmabuf(struct hailo_vdma_buffer *vdma_buffer)
++{
++ dma_buf_unmap_attachment(vdma_buffer->dmabuf_info.dmabuf_attachment, vdma_buffer->dmabuf_info.dmabuf_sg_table, vdma_buffer->data_direction);
++ dma_buf_detach(vdma_buffer->dmabuf_info.dmabuf, vdma_buffer->dmabuf_info.dmabuf_attachment);
++ dma_buf_put(vdma_buffer->dmabuf_info.dmabuf);
++}
++
++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */
++
++static int hailo_map_dmabuf(struct device *dev, int dmabuf_fd, enum dma_data_direction direction, struct sg_table *sgt,
++ struct hailo_dmabuf_info *dmabuf_info)
++{
++ (void) dmabuf_fd;
++ (void) direction;
++ (void) sgt;
++ (void) mapped_buffer;
++ dev_err(dev, "dmabuf not supported in kernel versions lower than 3.3.0\n");
++ return -EINVAL;
++}
++
++static void hailo_unmap_dmabuf(struct hailo_vdma_buffer *vdma_buffer)
++{
++ dev_err(vdma_buffer->device, "dmabuf not supported in kernel versions lower than 3.3.0\n");
++ return -EINVAL;
++}
++
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */
++
+ struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev,
+- void __user *user_address, size_t size, enum dma_data_direction direction,
+- struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer)
++ uintptr_t user_address, size_t size, enum dma_data_direction direction,
++ enum hailo_dma_buffer_type buffer_type, struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer)
+ {
+ int ret = -EINVAL;
+ struct hailo_vdma_buffer *mapped_buffer = NULL;
+ struct sg_table sgt = {0};
+ struct vm_area_struct *vma = NULL;
+ bool is_mmio = false;
++ struct hailo_dmabuf_info dmabuf_info = {0};
+
+ mapped_buffer = kzalloc(sizeof(*mapped_buffer), GFP_KERNEL);
+ if (NULL == mapped_buffer) {
+@@ -40,17 +120,19 @@ struct hailo_vdma_buffer *hailo_vdma_buf
+ goto cleanup;
+ }
+
+- if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING)) {
+- vma = find_vma(current->mm, (uintptr_t)user_address);
++ if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && (HAILO_DMA_DMABUF_BUFFER != buffer_type)) {
++ vma = find_vma(current->mm, user_address);
+ if (NULL == vma) {
+- dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", (uintptr_t)user_address, size);
++ dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", user_address, size);
+ ret = -EFAULT;
+ goto cleanup;
+ }
+ }
+
++ // TODO: is MMIO DMA MAPPINGS STILL needed after dmabuf
+ if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) &&
+- (MMIO_AND_NO_PAGES_VMA_MASK == (vma->vm_flags & MMIO_AND_NO_PAGES_VMA_MASK))) {
++ (MMIO_AND_NO_PAGES_VMA_MASK == (vma->vm_flags & MMIO_AND_NO_PAGES_VMA_MASK)) &&
++ (HAILO_DMA_DMABUF_BUFFER != buffer_type)) {
+ // user_address represents memory mapped I/O and isn't backed by 'struct page' (only by pure pfn)
+ if (NULL != low_mem_driver_allocated_buffer) {
+ // low_mem_driver_allocated_buffer are backed by regular 'struct page' addresses, just in low memory
+@@ -66,6 +148,14 @@ struct hailo_vdma_buffer *hailo_vdma_buf
+ }
+
+ is_mmio = true;
++
++ } else if (HAILO_DMA_DMABUF_BUFFER == buffer_type) {
++ // Content user_address in case of dmabuf is fd - for now
++ ret = hailo_map_dmabuf(dev, user_address, direction, &sgt, &dmabuf_info);
++ if (ret < 0) {
++ dev_err(dev, "Failed mapping dmabuf\n");
++ goto cleanup;
++ }
+ } else {
+ // user_address is a standard 'struct page' backed memory address
+ ret = prepare_sg_table(&sgt, user_address, size, low_mem_driver_allocated_buffer);
+@@ -88,6 +178,7 @@ struct hailo_vdma_buffer *hailo_vdma_buf
+ mapped_buffer->data_direction = direction;
+ mapped_buffer->sg_table = sgt;
+ mapped_buffer->is_mmio = is_mmio;
++ mapped_buffer->dmabuf_info = dmabuf_info;
+
+ return mapped_buffer;
+
+@@ -103,11 +194,16 @@ static void unmap_buffer(struct kref *kr
+ {
+ struct hailo_vdma_buffer *buf = container_of(kref, struct hailo_vdma_buffer, kref);
+
+- if (!buf->is_mmio) {
+- dma_unmap_sg(buf->device, buf->sg_table.sgl, buf->sg_table.orig_nents, buf->data_direction);
+- }
++ // If dmabuf - unmap and detatch dmabuf
++ if (NULL != buf->dmabuf_info.dmabuf) {
++ hailo_unmap_dmabuf(buf);
++ } else {
++ if (!buf->is_mmio) {
++ dma_unmap_sg(buf->device, buf->sg_table.sgl, buf->sg_table.orig_nents, buf->data_direction);
++ }
+
+- clear_sg_table(&buf->sg_table);
++ clear_sg_table(&buf->sg_table);
++ }
+ kfree(buf);
+ }
+
+@@ -164,8 +260,9 @@ void hailo_vdma_buffer_sync(struct hailo
+ struct hailo_vdma_buffer *mapped_buffer, enum hailo_vdma_buffer_sync_type sync_type,
+ size_t offset, size_t size)
+ {
+- if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && mapped_buffer->is_mmio) {
+- // MMIO buffers don't need to be sync'd
++ if ((IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && mapped_buffer->is_mmio) ||
++ (NULL != mapped_buffer->dmabuf_info.dmabuf)) {
++ // MMIO buffers and dmabufs don't need to be sync'd
+ return;
+ }
+
+@@ -404,7 +501,8 @@ void hailo_vdma_clear_continuous_buffer_
+
+ // Assumes the provided user_address belongs to the vma and that MMIO_AND_NO_PAGES_VMA_MASK bits are set under
+ // vma->vm_flags. This is validated in hailo_vdma_buffer_map, and won't be checked here
+-static int map_mmio_address(void __user* user_address, u32 size, struct vm_area_struct *vma,
++#if defined(HAILO_SUPPORT_MMIO_DMA_MAPPING)
++static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma,
+ struct sg_table *sgt)
+ {
+ int ret = -EINVAL;
+@@ -413,7 +511,7 @@ static int map_mmio_address(void __user*
+ unsigned long next_pfn = 0;
+ phys_addr_t phys_addr = 0;
+ dma_addr_t mmio_dma_address = 0;
+- const uintptr_t virt_addr = (uintptr_t)user_address;
++ const uintptr_t virt_addr = user_address;
+ const u32 vma_size = vma->vm_end - vma->vm_start + 1;
+ const uintptr_t num_pages = PFN_UP(virt_addr + size) - PFN_DOWN(virt_addr);
+
+@@ -462,8 +560,21 @@ static int map_mmio_address(void __user*
+
+ return 0;
+ }
++#else /* defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) */
++static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma,
++ struct sg_table *sgt)
++{
++ (void) user_address;
++ (void) size;
++ (void) vma;
++ (void) sgt;
++ pr_err("MMIO DMA MAPPINGS are not supported in this kernel version\n");
++ return -EINVAL;
++}
++#endif /* defined(HAILO_SUPPORT_MMIO_DMA_MAPPING) */
++
+
+-static int prepare_sg_table(struct sg_table *sg_table, void __user *user_address, u32 size,
++static int prepare_sg_table(struct sg_table *sg_table, uintptr_t user_address, u32 size,
+ struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer)
+ {
+ int ret = -EINVAL;
+@@ -482,8 +593,7 @@ static int prepare_sg_table(struct sg_ta
+ // Check whether mapping user allocated buffer or driver allocated low memory buffer
+ if (NULL == low_mem_driver_allocated_buffer) {
+ mmap_read_lock(current->mm);
+- pinned_pages = get_user_pages_compact((unsigned long)user_address,
+- npages, FOLL_WRITE | FOLL_FORCE, pages);
++ pinned_pages = get_user_pages_compact(user_address, npages, FOLL_WRITE | FOLL_FORCE, pages);
+ mmap_read_unlock(current->mm);
+
+ if (pinned_pages < 0) {
+--- a/drivers/media/pci/hailo/vdma/memory.h
++++ b/drivers/media/pci/hailo/vdma/memory.h
+@@ -11,8 +11,8 @@
+
+ #include "vdma/vdma.h"
+
+-struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev,
+- void __user *user_address, size_t size, enum dma_data_direction direction,
++struct hailo_vdma_buffer *hailo_vdma_buffer_map(struct device *dev, uintptr_t user_address, size_t size,
++ enum dma_data_direction direction, enum hailo_dma_buffer_type buffer_type,
+ struct hailo_vdma_low_memory_buffer *low_mem_driver_allocated_buffer);
+ void hailo_vdma_buffer_get(struct hailo_vdma_buffer *buf);
+ void hailo_vdma_buffer_put(struct hailo_vdma_buffer *buf);
+--- a/drivers/media/pci/hailo/vdma/vdma.c
++++ b/drivers/media/pci/hailo/vdma/vdma.c
+@@ -21,7 +21,7 @@
+
+
+ static struct hailo_vdma_engine* init_vdma_engines(struct device *dev,
+- struct hailo_resource *channel_registers_per_engine, size_t engines_count)
++ struct hailo_resource *channel_registers_per_engine, size_t engines_count, u32 src_channels_bitmask)
+ {
+ struct hailo_vdma_engine *engines = NULL;
+ u8 i = 0;
+@@ -33,7 +33,7 @@ static struct hailo_vdma_engine* init_vd
+ }
+
+ for (i = 0; i < engines_count; i++) {
+- hailo_vdma_engine_init(&engines[i], i, &channel_registers_per_engine[i]);
++ hailo_vdma_engine_init(&engines[i], i, &channel_registers_per_engine[i], src_channels_bitmask);
+ }
+
+ return engines;
+@@ -72,7 +72,8 @@ int hailo_vdma_controller_init(struct ha
+ controller->dev = dev;
+
+ controller->vdma_engines_count = engines_count;
+- controller->vdma_engines = init_vdma_engines(dev, channel_registers_per_engine, engines_count);
++ controller->vdma_engines = init_vdma_engines(dev, channel_registers_per_engine, engines_count,
++ vdma_hw->src_channels_bitmask);
+ if (IS_ERR(controller->vdma_engines)) {
+ dev_err(dev, "Failed initialized vdma engines\n");
+ return PTR_ERR(controller->vdma_engines);
+@@ -113,36 +114,27 @@ void hailo_vdma_update_interrupts_mask(s
+ controller->ops->update_channel_interrupts(controller, engine_index, engine->enabled_channels);
+ }
+
+-void hailo_vdma_engine_interrupts_disable(struct hailo_vdma_controller *controller,
+- struct hailo_vdma_engine *engine, u8 engine_index, u32 channels_bitmap)
+-{
+- unsigned long irq_saved_flags = 0;
+- // In case of FLR, the vdma registers will be NULL
+- const bool is_device_up = (NULL != controller->dev);
+-
+- hailo_vdma_engine_disable_channel_interrupts(engine, channels_bitmap);
+- 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);
+- spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+-
+- hailo_dev_info(controller->dev, "Disabled interrupts for engine %u, channels bitmap 0x%x\n",
+- engine_index, channels_bitmap);
+-}
+-
+ void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context,
+ struct hailo_vdma_controller *controller, struct file *filp)
+ {
+ 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_interrupts_disable(controller, engine, engine_index, channels_bitmap);
++ hailo_vdma_engine_disable_channels(engine, channels_bitmap);
++
++ 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);
++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+ }
+ }
+
+@@ -178,10 +170,10 @@ long hailo_vdma_ioctl(struct hailo_vdma_
+ unsigned int cmd, unsigned long arg, struct file *filp, struct semaphore *mutex, bool *should_up_board_mutex)
+ {
+ switch (cmd) {
+- case HAILO_VDMA_INTERRUPTS_ENABLE:
+- return hailo_vdma_interrupts_enable_ioctl(controller, arg);
+- case HAILO_VDMA_INTERRUPTS_DISABLE:
+- return hailo_vdma_interrupts_disable_ioctl(controller, arg);
++ case HAILO_VDMA_ENABLE_CHANNELS:
++ return hailo_vdma_enable_channels_ioctl(controller, arg);
++ case HAILO_VDMA_DISABLE_CHANNELS:
++ return hailo_vdma_disable_channels_ioctl(controller, arg);
+ case HAILO_VDMA_INTERRUPTS_WAIT:
+ return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex);
+ case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS:
+@@ -196,8 +188,8 @@ long hailo_vdma_ioctl(struct hailo_vdma_
+ return hailo_desc_list_create_ioctl(context, controller, arg);
+ case HAILO_DESC_LIST_RELEASE:
+ return hailo_desc_list_release_ioctl(context, controller, arg);
+- case HAILO_DESC_LIST_BIND_VDMA_BUFFER:
+- return hailo_desc_list_bind_vdma_buffer(context, controller, arg);
++ case HAILO_DESC_LIST_PROGRAM:
++ return hailo_desc_list_program_ioctl(context, controller, arg);
+ case HAILO_VDMA_LOW_MEMORY_BUFFER_ALLOC:
+ return hailo_vdma_low_memory_buffer_alloc_ioctl(context, controller, arg);
+ case HAILO_VDMA_LOW_MEMORY_BUFFER_FREE:
+@@ -216,28 +208,6 @@ long hailo_vdma_ioctl(struct hailo_vdma_
+ }
+ }
+
+-static int desc_list_mmap(struct hailo_vdma_controller *controller,
+- struct hailo_descriptors_list_buffer *vdma_descriptors_buffer, struct vm_area_struct *vma)
+-{
+- int err = 0;
+- unsigned long vsize = vma->vm_end - vma->vm_start;
+-
+- if (vsize > vdma_descriptors_buffer->buffer_size) {
+- hailo_dev_err(controller->dev, "Requested size to map (%lx) is larger than the descriptor list size(%x)\n",
+- vsize, vdma_descriptors_buffer->buffer_size);
+- return -EINVAL;
+- }
+-
+- err = dma_mmap_coherent(controller->dev, vma, vdma_descriptors_buffer->kernel_address,
+- vdma_descriptors_buffer->dma_address, vsize);
+- if (err != 0) {
+- hailo_dev_err(controller->dev, " Failed mmap descriptors %d\n", err);
+- return err;
+- }
+-
+- return 0;
+-}
+-
+ static int low_memory_buffer_mmap(struct hailo_vdma_controller *controller,
+ struct hailo_vdma_low_memory_buffer *vdma_buffer, struct vm_area_struct *vma)
+ {
+@@ -300,15 +270,11 @@ static int continuous_buffer_mmap(struct
+ int hailo_vdma_mmap(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller,
+ struct vm_area_struct *vma, uintptr_t vdma_handle)
+ {
+- struct hailo_descriptors_list_buffer *vdma_descriptors_buffer = NULL;
+ struct hailo_vdma_low_memory_buffer *low_memory_buffer = NULL;
+ struct hailo_vdma_continuous_buffer *continuous_buffer = NULL;
+
+ hailo_dev_info(controller->dev, "Map vdma_handle %llu\n", (u64)vdma_handle);
+- if (NULL != (vdma_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, vdma_handle))) {
+- return desc_list_mmap(controller, vdma_descriptors_buffer, vma);
+- }
+- else if (NULL != (low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, vdma_handle))) {
++ if (NULL != (low_memory_buffer = hailo_vdma_find_low_memory_buffer(context, vdma_handle))) {
+ return low_memory_buffer_mmap(controller, low_memory_buffer, vma);
+ }
+ else if (NULL != (continuous_buffer = hailo_vdma_find_continuous_buffer(context, vdma_handle))) {
+--- a/drivers/media/pci/hailo/vdma/vdma.h
++++ b/drivers/media/pci/hailo/vdma/vdma.h
+@@ -16,6 +16,8 @@
+ #include <linux/dma-mapping.h>
+ #include <linux/types.h>
+ #include <linux/semaphore.h>
++#include <linux/dma-buf.h>
++#include <linux/version.h>
+
+ #define VDMA_CHANNEL_CONTROL_REG_OFFSET(channel_index, direction) (((direction) == DMA_TO_DEVICE) ? \
+ (((channel_index) << 5) + 0x0) : (((channel_index) << 5) + 0x10))
+@@ -28,6 +30,22 @@
+ ((u8*)((vdma_registers)->address) + VDMA_CHANNEL_NUM_PROC_OFFSET(channel_index, direction))
+
+
++// dmabuf is supported from linux kernel version 3.3
++#if LINUX_VERSION_CODE < KERNEL_VERSION( 3, 3, 0 )
++// Make dummy struct with one byte (C standards does not allow empty struct) - in order to not have to ifdef everywhere
++struct hailo_dmabuf_info {
++ uint8_t dummy;
++};
++#else
++// dmabuf_sg_table is needed because in dma_buf_unmap_attachment() the sg_table's address has to match the
++// The one returned from dma_buf_map_attachment() - otherwise we would need to malloc each time
++struct hailo_dmabuf_info {
++ struct dma_buf *dmabuf;
++ struct dma_buf_attachment *dmabuf_attachment;
++ struct sg_table *dmabuf_sg_table;
++};
++#endif // LINUX_VERSION_CODE < KERNEL_VERSION( 3, 3, 0 )
++
+ struct hailo_vdma_buffer {
+ struct list_head mapped_user_buffer_list;
+ size_t handle;
+@@ -35,7 +53,7 @@ struct hailo_vdma_buffer {
+ struct kref kref;
+ struct device *device;
+
+- void __user *user_address;
++ uintptr_t user_address;
+ u32 size;
+ enum dma_data_direction data_direction;
+ struct sg_table sg_table;
+@@ -44,7 +62,10 @@ struct hailo_vdma_buffer {
+ // 'struct page' (only by pure pfn). On this case, accessing to the page,
+ // or calling APIs that access the page (e.g. dma_sync_sg_for_cpu) is not
+ // allowed.
+- bool is_mmio;
++ bool is_mmio;
++
++ // Relevant paramaters that need to be saved in case of dmabuf - otherwise struct pointers will be NULL
++ struct hailo_dmabuf_info dmabuf_info;
+ };
+
+ // Continuous buffer that holds a descriptor list.
+@@ -53,7 +74,7 @@ struct hailo_descriptors_list_buffer {
+ uintptr_t handle;
+ void *kernel_address;
+ dma_addr_t dma_address;
+- u32 buffer_size;
++ u32 buffer_size;
+ struct hailo_vdma_descriptors_list desc_list;
+ };
+
+@@ -120,9 +141,6 @@ int hailo_vdma_controller_init(struct ha
+ void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller,
+ size_t engine_index);
+
+-void hailo_vdma_engine_interrupts_disable(struct hailo_vdma_controller *controller,
+- struct hailo_vdma_engine *engine, u8 engine_index, u32 channels_bitmap);
+-
+ void hailo_vdma_file_context_init(struct hailo_vdma_file_context *context);
+ void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context,
+ struct hailo_vdma_controller *controller, struct file *filp);
--- /dev/null
+From cf64a1dfecc2dc418efdd61701c1a4b185ab4761 Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Fri, 23 Aug 2024 16:48:38 +0100
+Subject: [PATCH 1236/1350] media/rpivid: Make SPS / PPS optional in a request
+
+SPS & PPS are optional in requests. Fix. The framework keeps the last
+value so this is mostly a matter of changing .required to false when
+requesting the controls. Check that SPS has ever been set on frame
+start, PPS is valid if all zeros so is at best tricky to check.
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/staging/media/rpivid/rpivid.c | 4 ++--
+ drivers/staging/media/rpivid/rpivid_h265.c | 6 ++++++
+ drivers/staging/media/rpivid/rpivid_video.c | 5 -----
+ drivers/staging/media/rpivid/rpivid_video.h | 5 +++++
+ 4 files changed, 13 insertions(+), 7 deletions(-)
+
+--- a/drivers/staging/media/rpivid/rpivid.c
++++ b/drivers/staging/media/rpivid/rpivid.c
+@@ -40,14 +40,14 @@ static const struct rpivid_control rpivi
+ .id = V4L2_CID_STATELESS_HEVC_SPS,
+ .ops = &rpivid_hevc_sps_ctrl_ops,
+ },
+- .required = true,
++ .required = false,
+ },
+ {
+ .cfg = {
+ .id = V4L2_CID_STATELESS_HEVC_PPS,
+ .ops = &rpivid_hevc_pps_ctrl_ops,
+ },
+- .required = true,
++ .required = false,
+ },
+ {
+ .cfg = {
+--- a/drivers/staging/media/rpivid/rpivid_h265.c
++++ b/drivers/staging/media/rpivid/rpivid_h265.c
+@@ -1726,6 +1726,12 @@ static void rpivid_h265_setup(struct rpi
+ unsigned int ctb_size_y;
+ bool sps_changed = false;
+
++ if (!is_sps_set(run->h265.sps)) {
++ v4l2_warn(&dev->v4l2_dev, "SPS never set\n");
++ goto fail;
++ }
++ // Can't check for PPS easily as all 0's looks valid to me
++
+ if (memcmp(&s->sps, run->h265.sps, sizeof(s->sps)) != 0) {
+ /* SPS changed */
+ v4l2_info(&dev->v4l2_dev, "SPS changed\n");
+--- a/drivers/staging/media/rpivid/rpivid_video.c
++++ b/drivers/staging/media/rpivid/rpivid_video.c
+@@ -257,11 +257,6 @@ static int rpivid_hevc_validate_sps(cons
+ return 1;
+ }
+
+-static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
+-{
+- return sps && sps->pic_width_in_luma_samples != 0;
+-}
+-
+ static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps,
+ const int index)
+ {
+--- a/drivers/staging/media/rpivid/rpivid_video.h
++++ b/drivers/staging/media/rpivid/rpivid_video.h
+@@ -20,6 +20,11 @@ struct rpivid_format {
+ unsigned int capabilities;
+ };
+
++static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
++{
++ return sps && sps->pic_width_in_luma_samples != 0;
++}
++
+ extern const struct v4l2_ioctl_ops rpivid_ioctl_ops;
+
+ int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
--- /dev/null
+From 31be188fb945560c193020b19773625421847112 Mon Sep 17 00:00:00 2001
+From: Satadru Pramanik <satadru@gmail.com>
+Date: Wed, 4 Sep 2024 08:40:42 -0400
+Subject: [PATCH 1238/1350] piscreen-overlay: Add invert[x,y] and swapxy
+
+Signed-off-by: Satadru Pramanik <satadru@gmail.com>
+---
+ arch/arm/boot/dts/overlays/README | 6 ++++++
+ arch/arm/boot/dts/overlays/piscreen-overlay.dts | 3 +++
+ 2 files changed, 9 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3694,6 +3694,12 @@ Params: speed Display
+ drm Select the DRM/KMS driver instead of the FBTFT
+ one
+
++ invx Touchscreen inverted x axis
++
++ invy Touchscreen inverted y axis
++
++ swapxy Touchscreen swapped x y axis
++
+
+ Name: piscreen2r
+ Info: PiScreen 2 with resistive TP display by OzzMaker.com
+--- a/arch/arm/boot/dts/overlays/piscreen-overlay.dts
++++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts
+@@ -103,5 +103,8 @@
+ xohms = <&piscreen_ts>,"ti,x-plate-ohms;0";
+ drm = <&piscreen>,"compatible=waveshare,rpi-lcd-35",
+ <&piscreen>,"reset-gpios:8=",<GPIO_ACTIVE_HIGH>;
++ invx = <&piscreen_ts>,"touchscreen-inverted-x?";
++ invy = <&piscreen_ts>,"touchscreen-inverted-y?";
++ swapxy = <&piscreen_ts>,"touchscreen-swapped-x-y!";
+ };
+ };
--- /dev/null
+From f955b7838f9ce72e48b29e58d65e0642d097c8b3 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 4 Sep 2024 10:48:22 +0800
+Subject: [PATCH 1239/1350] drivers:gpu:drm:panel: Added waveshare 5.0inch,
+ 6.25inch, and 8.8inch dsi screen devices
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 55 +++++++++++++++++++++
+ 1 file changed, 55 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -150,6 +150,51 @@ static const struct drm_display_mode ws_
+ .vtotal = 720 + 8 + 4 + 16,
+ };
+
++/* 5.0inch 720x1280
++ * https://www.waveshare.com/5inch-dsi-lcd-d.htm
++ */
++static const struct drm_display_mode ws_panel_5_0_mode = {
++ .clock = 83333,
++ .hdisplay = 720,
++ .hsync_start = 720 + 100,
++ .hsync_end = 720 + 100 + 80,
++ .htotal = 720 + 100 + 80 + 100,
++ .vdisplay = 1280,
++ .vsync_start = 1280 + 20,
++ .vsync_end = 1280 + 20 + 20,
++ .vtotal = 1280 + 20 + 20 + 20,
++};
++
++/* 6.25inch 720x1560
++ * https://www.waveshare.com/6.25inch-dsi-lcd.htm
++ */
++static const struct drm_display_mode ws_panel_6_25_mode = {
++ .clock = 83333,
++ .hdisplay = 720,
++ .hsync_start = 720 + 50,
++ .hsync_end = 720 + 50 + 50,
++ .htotal = 720 + 50 + 50 + 50,
++ .vdisplay = 1560,
++ .vsync_start = 1560 + 20,
++ .vsync_end = 1560 + 20 + 20,
++ .vtotal = 1560 + 20 + 20 + 20,
++};
++
++/* 8.8inch 480x1920
++ * https://www.waveshare.com/8.8inch-dsi-lcd.htm
++ */
++static const struct drm_display_mode ws_panel_8_8_mode = {
++ .clock = 83333,
++ .hdisplay = 480,
++ .hsync_start = 480 + 50,
++ .hsync_end = 480 + 50 + 50,
++ .htotal = 480 + 50 + 50 + 50,
++ .vdisplay = 1920,
++ .vsync_start = 1920 + 20,
++ .vsync_end = 1920 + 20 + 20,
++ .vtotal = 1920 + 20 + 20 + 20,
++};
++
+ static struct ws_panel *panel_to_ts(struct drm_panel *panel)
+ {
+ return container_of(panel, struct ws_panel, base);
+@@ -413,6 +458,16 @@ static const struct of_device_id ws_pane
+ .compatible = "waveshare,4inch-panel",
+ .data = &ws_panel_4_mode,
+ }, {
++ .compatible = "waveshare,5.0inch-panel",
++ .data = &ws_panel_5_0_mode,
++ }, {
++ .compatible = "waveshare,6.25inch-panel",
++ .data = &ws_panel_6_25_mode,
++ }, {
++ .compatible = "waveshare,8.8inch-panel",
++ .data = &ws_panel_8_8_mode,
++ }, {
++ }, {
+ /* sentinel */
+ }
+ };
--- /dev/null
+From 769cd349d65fb29aaa8f443f9c7a701205b0409c Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 4 Sep 2024 10:56:13 +0800
+Subject: [PATCH 1240/1350] arch:arm:boot:dts:overlays: Added waveshare
+ 5.0inch, 6.25inch, and 8.8inch dsi screen dts
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- 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
+@@ -113,6 +113,13 @@
+ 4_0_inchC = <&panel>, "compatible=waveshare,4inch-panel",
+ <&touch>, "touchscreen-size-x:0=720",
+ <&touch>, "touchscreen-size-y:0=720";
++ 5_0_inch = <&panel>, "compatible=waveshare,5.0inch-panel",
++ <&touch>, "touchscreen-inverted-x?",
++ <&touch>, "touchscreen-inverted-y?";
++ 6_25_inch = <&panel>, "compatible=waveshare,6.25inch-panel",
++ <&touch>, "touchscreen-inverted-x?",
++ <&touch>, "touchscreen-inverted-y?";
++ 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel";
+ i2c1 = <&i2c_frag>, "target:0=",<&i2c1>,
+ <0>, "-3-4+5";
+ disable_touch = <&touch>, "status=disabled";
--- /dev/null
+From 3d39588b52abb06ed2b4b0d2db026133a920758b Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Thu, 5 Sep 2024 17:19:56 +0800
+Subject: [PATCH 1241/1350] arch:arm:boot:dts:overlays: Added waveshare
+ 5.0inch, 6.25inch, and 8.8inch dsi screen description
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ arch/arm/boot/dts/overlays/README | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5233,6 +5233,9 @@ Params: 2_8_inch 2.8" 480
+ 3_4_inch 3.4" 800x800 round
+ 4_0_inch 4.0" 480x800
+ 4_0_inchC 4.0" 720x720
++ 5_0_inch 5.0" 720x1280
++ 6_25_inch 6.25" 720x1560
++ 8_8_inch 8.8" 480x1920
+ 7_0_inchC 7.0" C 1024x600
+ 7_9_inch 7.9" 400x1280
+ 8_0_inch 8.0" 1280x800
--- /dev/null
+From 4d219b15a7a01a14c1be1eeee9a939732130d30a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 13 Aug 2024 13:45:54 +0100
+Subject: [PATCH 1242/1350] spi: rp2040-gpio-bridge: Add debugfs progress
+ indicator
+
+Useful for tracking upload progress via userspace.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/spi/spi-rp2040-gpio-bridge.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/drivers/spi/spi-rp2040-gpio-bridge.c
++++ b/drivers/spi/spi-rp2040-gpio-bridge.c
+@@ -6,6 +6,7 @@
+ */
+
+ #include <crypto/hash.h>
++#include <linux/debugfs.h>
+ #include <linux/crypto.h>
+ #include <linux/delay.h>
+ #include <linux/firmware.h>
+@@ -91,6 +92,9 @@ enum rp2040_gbdg_fixed_size_commands {
+ struct rp2040_gbdg {
+ struct spi_controller *controller;
+
++ struct dentry *debugfs;
++ size_t transfer_progress;
++
+ struct i2c_client *client;
+ struct crypto_shash *shash;
+ struct shash_desc *shash_desc;
+@@ -702,6 +706,7 @@ static int rp2040_gbdg_transfer_cached(s
+ return 0;
+ }
+
++ priv_data->transfer_progress = 0;
+ while (length) {
+ unsigned int xfer = min(length, RP2040_GBDG_BLOCK_SIZE);
+
+@@ -710,7 +715,9 @@ static int rp2040_gbdg_transfer_cached(s
+ return ret;
+ length -= xfer;
+ data += xfer;
++ priv_data->transfer_progress += xfer;
+ }
++ priv_data->transfer_progress = 0;
+
+ return 0;
+ }
+@@ -1016,6 +1023,16 @@ static int rp2040_gbdg_power_on(struct r
+ return 0;
+ }
+
++static int transfer_progress_show(struct seq_file *s, void *data)
++{
++ struct rp2040_gbdg *rp2040_gbdg = s->private;
++
++ seq_printf(s, "%zu\n", rp2040_gbdg->transfer_progress);
++ return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(transfer_progress);
++
+ static int rp2040_gbdg_probe(struct i2c_client *client)
+ {
+ struct rp2040_gbdg_device_info info;
+@@ -1023,6 +1040,7 @@ static int rp2040_gbdg_probe(struct i2c_
+ struct device *dev = &client->dev;
+ struct rp2040_gbdg *rp2040_gbdg;
+ struct device_node *np;
++ char debugfs_name[128];
+ int ret;
+
+ np = dev->of_node;
+@@ -1136,6 +1154,12 @@ static int rp2040_gbdg_probe(struct i2c_
+
+ rp2040_gbdg_parse_dt(rp2040_gbdg);
+
++ snprintf(debugfs_name, sizeof(debugfs_name), "rp2040-spi:%s",
++ dev_name(dev));
++ rp2040_gbdg->debugfs = debugfs_create_dir(debugfs_name, NULL);
++ debugfs_create_file("transfer_progress", 0444, rp2040_gbdg->debugfs,
++ rp2040_gbdg, &transfer_progress_fops);
++
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
--- /dev/null
+From 30921ddfa24a5325d0f4980c72f98d20bace87a2 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Fri, 24 May 2024 11:43:07 +0100
+Subject: [PATCH 1243/1350] media: dt-bindings: i2c: Add Sony IMX500
+
+Add YAML device tree binding for the Sony IMX500 CMOS image sensor /
+CNN inference engine. Also, add a MAINTAINERS entry.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ .../bindings/media/i2c/sony,imx500.yaml | 132 ++++++++++++++++++
+ MAINTAINERS | 7 +
+ 2 files changed, 139 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
+@@ -0,0 +1,132 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/sony,imx500.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony CMOS Digital Image Sensor and CNN
++
++maintainers:
++ - Raspberry Pi <kernel-list@raspberrypi.com>
++
++description: |-
++ The Sony IMX500 is a stacked 1/2.3-inch CMOS digital image sensor and inbuilt
++ AI processor with an active array CNN (Convolutional Neural Network) inference
++ engine. The native sensor size is 4056H x 3040V, and the module also contains
++ an in-built ISP for the CNN. The module is programmable through an I2C
++ interface with firmware and neural network uploads being made over SPI. The
++ default I2C address is 0x1A, with an address of 0x10 being selectable via
++ SLASEL. The module also has a second I2C interface available with a fixed
++ address of 0x36. Image data is sent through MIPI CSI-2, which is configured
++ as either 2 or 4 data lanes.
++
++properties:
++ compatible:
++ const: sony,imx500
++
++ reg:
++ description: I2C device address
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ clock-names:
++ description: |-
++ Input clock (12 to 27 MHz)
++ items:
++ - const: inck
++
++ interrupts:
++ maxItems: 1
++
++ vana-supply:
++ description: Supply voltage (analog) - 2.7 V
++
++ vdig-supply:
++ description: Supply voltage (digital) - 0.84 V
++
++ vif-supply:
++ description: Supply voltage (interface) - 1.8 V
++
++ reset-gpios:
++ description: |-
++ Sensor reset (XCLR) GPIO
++
++ Chip clear in lieu of built-in power on reset. To be set 'High' after
++ power supplies are brought up and INCK supplied.
++
++ port:
++ $ref: /schemas/graph.yaml#/$defs/port-base
++ additionalProperties: false
++ description: |
++ Video output port
++
++ properties:
++ endpoint:
++ $ref: /schemas/media/video-interfaces.yaml#
++ type: object
++ unevaluatedProperties: false
++ properties:
++ data-lanes:
++ items:
++ - const: 2
++ - const: 4
++ clock-noncontinuous: true
++ link-frequencies: true
++ required:
++ - link-frequencies
++ - data-lanes
++
++ spi:
++ $ref: /schemas/types.yaml#/definitions/phandle
++ description: |-
++ SPI peripheral
++
++ Optional SPI peripheral for uploading firmware and network weights to AI
++ processor.
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - clock-names
++ - vana-supply
++ - vdig-supply
++ - vif-supply
++ - port
++
++examples:
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ i2c {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ imx500: sensor@1a {
++ compatible = "sony,imx500";
++ reg = <0x1a>;
++
++ clocks = <&imx500_clk>;
++ clock-names = "inck";
++
++ vana-supply = <&imx500_vana>; /* 2.7 +/- 0.1 V */
++ vdig-supply = <&imx500_vdig>; /* 0.84 +/- 0.04 V */
++ vif-supply = <&imx500_vif>; /* 1.8 +/- 0.1 V */
++
++ reset-gpios = <&gpio_sensor 0 GPIO_ACTIVE_LOW>;
++
++ port {
++ imx500_0: endpoint {
++ remote-endpoint = <&csi1_ep>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies = /bits/ 64 <499500000>;
++ };
++ };
++ };
++ };
++
++...
++
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -20127,6 +20127,13 @@ F: Documentation/devicetree/bindings/med
+ F: Documentation/devicetree/bindings/media/i2c/imx477.yaml
+ F: drivers/media/i2c/imx477.c
+
++SONY IMX500 SENSOR DRIVER
++M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++T: git git://linuxtv.org/media_tree.git
++F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
++
+ SONY IMX519 SENSOR DRIVER
+ M: Arducam Kernel Maintenance <info@arducam.com>
+ L: linux-media@vger.kernel.org
--- /dev/null
+From f9b1e0ffdd9b5134d9356d7e09e9c1a065fdfa13 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Thu, 27 Jun 2024 10:11:44 +0100
+Subject: [PATCH 1244/1350] media: i2c: Add driver for Sony IMX500 sensor
+
+The Sony IMX500 is a stacked 1/2.3-inch CMOS digital image sensor and
+inbuilt AI processor with an active array CNN (Convolutional Neural
+Network) inference engine. The native sensor size is 4056H x 3040V, and
+the module also contains an in-built ISP for the CNN. The module is
+programmable through an I2C interface with firmware and neural network
+uploads being made over SPI. This driver supports imaging only.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ MAINTAINERS | 1 +
+ drivers/media/i2c/Kconfig | 12 +
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/imx500.c | 1610 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1624 insertions(+)
+ create mode 100644 drivers/media/i2c/imx500.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -20133,6 +20133,7 @@ L: linux-media@vger.kernel.org
+ S: Maintained
+ T: git git://linuxtv.org/media_tree.git
+ F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml
++F: drivers/media/i2c/imx500.c
+
+ SONY IMX519 SENSOR DRIVER
+ M: Arducam Kernel Maintenance <info@arducam.com>
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -236,6 +236,18 @@ config VIDEO_IMX477
+ To compile this driver as a module, choose M here: the
+ module will be called imx477.
+
++config VIDEO_IMX500
++ tristate "Sony IMX500 sensor support"
++ depends on I2C && VIDEO_DEV
++ select VIDEO_V4L2_SUBDEV_API
++ select V4L2_CCI_I2C
++ help
++ This is a Video4Linux2 sensor driver for the Sony
++ IMX500 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called IMX500.
++
+ config VIDEO_IMX519
+ tristate "Arducam IMX519 sensor support"
+ depends on I2C && VIDEO_DEV
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -58,6 +58,7 @@ obj-$(CONFIG_VIDEO_IMX355) += imx355.o
+ obj-$(CONFIG_VIDEO_IMX412) += imx412.o
+ obj-$(CONFIG_VIDEO_IMX415) += imx415.o
+ obj-$(CONFIG_VIDEO_IMX477) += imx477.o
++obj-$(CONFIG_VIDEO_IMX500) += imx500.o
+ obj-$(CONFIG_VIDEO_IMX519) += imx519.o
+ obj-$(CONFIG_VIDEO_IMX708) += imx708.o
+ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+--- /dev/null
++++ b/drivers/media/i2c/imx500.c
+@@ -0,0 +1,1610 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX500 cameras.
++ * Copyright (C) 2024, Raspberry Pi Ltd
++ */
++#include <asm/unaligned.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-cci.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++
++/* Chip ID */
++#define IMX500_REG_CHIP_ID CCI_REG16(0x0016)
++#define IMX500_CHIP_ID 0x0500
++
++#define IMX500_REG_MODE_SELECT CCI_REG8(0x0100)
++#define IMX500_MODE_STANDBY 0x00
++#define IMX500_MODE_STREAMING 0x01
++
++#define IMX500_REG_IMAGE_ONLY_MODE CCI_REG8(0xa700)
++#define IMX500_IMAGE_ONLY_FALSE 0x00
++#define IMX500_IMAGE_ONLY_TRUE 0x01
++
++#define IMX500_REG_ORIENTATION CCI_REG8(0x101)
++
++#define IMX500_XCLK_FREQ 24000000
++
++#define IMX500_DEFAULT_LINK_FREQ 444000000
++
++#define IMX500_PIXEL_RATE 744000000
++
++/* V_TIMING internal */
++#define IMX500_REG_FRAME_LENGTH CCI_REG16(0x0340)
++#define IMX500_FRAME_LENGTH_MAX 0xffdc
++#define IMX500_VBLANK_MIN 4
++
++/* H_TIMING internal */
++#define IMX500_REG_LINE_LENGTH CCI_REG16(0x0342)
++#define IMX500_LINE_LENGTH_MAX 0xfff0
++
++/* Long exposure multiplier */
++#define IMX500_LONG_EXP_SHIFT_MAX 7
++#define IMX500_LONG_EXP_SHIFT_REG CCI_REG8(0x3210)
++
++/* Exposure control */
++#define IMX500_REG_EXPOSURE CCI_REG16(0x0202)
++#define IMX500_EXPOSURE_OFFSET 22
++#define IMX500_EXPOSURE_MIN 8
++#define IMX500_EXPOSURE_STEP 1
++#define IMX500_EXPOSURE_DEFAULT 0x640
++#define IMX500_EXPOSURE_MAX (IMX500_FRAME_LENGTH_MAX - IMX500_EXPOSURE_OFFSET)
++
++/* Analog gain control */
++#define IMX500_REG_ANALOG_GAIN CCI_REG16(0x0204)
++#define IMX500_ANA_GAIN_MIN 0
++#define IMX500_ANA_GAIN_MAX 978
++#define IMX500_ANA_GAIN_STEP 1
++#define IMX500_ANA_GAIN_DEFAULT 0x0
++
++/* Colour balance controls */
++#define IMX500_REG_COLOUR_BALANCE_R CCI_REG16(0xd804)
++#define IMX500_REG_COLOUR_BALANCE_GR CCI_REG16(0xd806)
++#define IMX500_REG_COLOUR_BALANCE_GB CCI_REG16(0xd808)
++#define IMX500_REG_COLOUR_BALANCE_B CCI_REG16(0xd80a)
++#define IMX500_COLOUR_BALANCE_MIN 0x0001
++#define IMX500_COLOUR_BALANCE_MAX 0x0fff
++#define IMX500_COLOUR_BALANCE_STEP 0x0001
++#define IMX500_COLOUR_BALANCE_DEFAULT 0x0100
++
++/* Embedded sizes */
++#define IMX500_MAX_EMBEDDED_SIZE \
++ (2 * ((((IMX500_PIXEL_ARRAY_WIDTH * 10) >> 3) + 15) & ~15))
++
++/* IMX500 native and active pixel array size. */
++#define IMX500_NATIVE_WIDTH 4072U
++#define IMX500_NATIVE_HEIGHT 3176U
++#define IMX500_PIXEL_ARRAY_LEFT 8U
++#define IMX500_PIXEL_ARRAY_TOP 16U
++#define IMX500_PIXEL_ARRAY_WIDTH 4056U
++#define IMX500_PIXEL_ARRAY_HEIGHT 3040U
++
++#define NUM_PADS 1
++
++/* regulator supplies */
++static const char *const imx500_supply_name[] = {
++ /* Supplies can be enabled in any order */
++ "vana", /* Analog (2.7V) supply */
++ "vdig", /* Digital Core (0.84V) supply */
++ "vif", /* Interface (1.8V) supply */
++};
++
++#define IMX500_NUM_SUPPLIES ARRAY_SIZE(imx500_supply_name)
++
++struct imx500_reg_list {
++ unsigned int num_of_regs;
++ const struct cci_reg_sequence *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx500_mode {
++ /* Frame width */
++ unsigned int width;
++
++ /* Frame height */
++ unsigned int height;
++
++ /* H-timing in pixels */
++ unsigned int line_length_pix;
++
++ /* Analog crop rectangle. */
++ struct v4l2_rect crop;
++
++ /* Default framerate. */
++ unsigned int framerate_default;
++
++ /* Default register values */
++ struct imx500_reg_list reg_list;
++};
++
++static const struct cci_reg_sequence mode_common_regs[] = {
++ { CCI_REG8(0x0305), 0x02 },
++ { CCI_REG8(0x0306), 0x00 },
++ { CCI_REG8(0x030d), 0x02 },
++ { CCI_REG8(0x030e), 0x00 },
++ { CCI_REG8(0x0106), 0x01 }, /* FAST_STANDBY_CTL */
++ { CCI_REG8(0x0136), 0x1b }, /* EXCLK_FREQ */
++ { CCI_REG8(0x0137), 0x00 },
++ { CCI_REG8(0x0138), 0x01 }, /* TEMP_SENS_CTL */
++ { CCI_REG8(0x0112), 0x0a },
++ { CCI_REG8(0x0113), 0x0a },
++ { CCI_REG8(0x0114), 0x01 }, /* CSI_LANE_MODE */
++};
++
++/* 12 mpix 15fps */
++static const struct cci_reg_sequence mode_4056x3040_regs[] = {
++ { CCI_REG8(0x0340), 0x12 },
++ { CCI_REG8(0x0341), 0x42 },
++ { CCI_REG8(0x0342), 0x45 },
++ { CCI_REG8(0x0343), 0xec },
++ { CCI_REG8(0x3210), 0x00 },
++ { CCI_REG8(0x0344), 0x00 },
++ { CCI_REG8(0x0345), 0x00 },
++ { CCI_REG8(0x0346), 0x00 },
++ { CCI_REG8(0x0347), 0x00 },
++ { CCI_REG8(0x0348), 0x0f },
++ { CCI_REG8(0x0349), 0xd7 },
++ { CCI_REG8(0x0350), 0x00 },
++ { CCI_REG8(0x034a), 0x0b },
++ { CCI_REG8(0x034b), 0xdf },
++ { CCI_REG8(0x3f58), 0x01 },
++ { CCI_REG8(0x0381), 0x01 },
++ { CCI_REG8(0x0383), 0x01 },
++ { CCI_REG8(0x0385), 0x01 },
++ { CCI_REG8(0x0387), 0x01 },
++ { CCI_REG8(0x0900), 0x00 },
++ { CCI_REG8(0x0901), 0x11 },
++ { CCI_REG8(0x0902), 0x00 },
++ { CCI_REG8(0x3241), 0x11 },
++ { CCI_REG8(0x3242), 0x01 },
++ { CCI_REG8(0x3250), 0x00 },
++ { CCI_REG8(0x3f0f), 0x00 },
++ { CCI_REG8(0x3f40), 0x00 },
++ { CCI_REG8(0x3f41), 0x00 },
++ { CCI_REG8(0x3f42), 0x00 },
++ { CCI_REG8(0x3f43), 0x00 },
++ { CCI_REG8(0xb34e), 0x00 },
++ { CCI_REG8(0xb351), 0x20 },
++ { CCI_REG8(0xb35c), 0x00 },
++ { CCI_REG8(0xb35e), 0x08 },
++ { CCI_REG8(0x0401), 0x00 },
++ { CCI_REG8(0x0404), 0x00 },
++ { CCI_REG8(0x0405), 0x10 },
++ { CCI_REG8(0x0408), 0x00 },
++ { CCI_REG8(0x0409), 0x00 },
++ { CCI_REG8(0x040a), 0x00 },
++ { CCI_REG8(0x040b), 0x00 },
++ { CCI_REG8(0x040c), 0x0f },
++ { CCI_REG8(0x040d), 0xd8 },
++ { CCI_REG8(0x040e), 0x0b },
++ { CCI_REG8(0x040f), 0xe0 },
++ { CCI_REG8(0x034c), 0x0f },
++ { CCI_REG8(0x034d), 0xd8 },
++ { CCI_REG8(0x034e), 0x0b },
++ { CCI_REG8(0x034f), 0xe0 },
++ { CCI_REG8(0x0301), 0x05 },
++ { CCI_REG8(0x0303), 0x02 },
++ { CCI_REG8(0x0307), 0x9b },
++ { CCI_REG8(0x0309), 0x0a },
++ { CCI_REG8(0x030b), 0x01 },
++ { CCI_REG8(0x030f), 0x4a },
++ { CCI_REG8(0x0310), 0x01 },
++ { CCI_REG8(0x0820), 0x07 },
++ { CCI_REG8(0x0821), 0xce },
++ { CCI_REG8(0x0822), 0x00 },
++ { CCI_REG8(0x0823), 0x00 },
++ { CCI_REG8(0x3e20), 0x01 },
++ { CCI_REG8(0x3e35), 0x01 },
++ { CCI_REG8(0x3e36), 0x01 },
++ { CCI_REG8(0x3e37), 0x00 },
++ { CCI_REG8(0x3e3a), 0x01 },
++ { CCI_REG8(0x3e3b), 0x00 },
++ { CCI_REG8(0x00e3), 0x00 },
++ { CCI_REG8(0x00e4), 0x00 },
++ { CCI_REG8(0x00e6), 0x00 },
++ { CCI_REG8(0x00e7), 0x00 },
++ { CCI_REG8(0x00e8), 0x00 },
++ { CCI_REG8(0x00e9), 0x00 },
++ { CCI_REG8(0x3f50), 0x00 },
++ { CCI_REG8(0x3f56), 0x02 },
++ { CCI_REG8(0x3f57), 0x42 },
++ { CCI_REG8(0x3606), 0x01 },
++ { CCI_REG8(0x3607), 0x01 },
++ { CCI_REG8(0x3f26), 0x00 },
++ { CCI_REG8(0x3f4a), 0x00 },
++ { CCI_REG8(0x3f4b), 0x00 },
++ { CCI_REG8(0x4bc0), 0x16 },
++ { CCI_REG8(0x7ba8), 0x00 },
++ { CCI_REG8(0x7ba9), 0x00 },
++ { CCI_REG8(0x886b), 0x00 },
++ { CCI_REG8(0x579a), 0x00 },
++ { CCI_REG8(0x579b), 0x0a },
++ { CCI_REG8(0x579c), 0x01 },
++ { CCI_REG8(0x579d), 0x2a },
++ { CCI_REG8(0x57ac), 0x00 },
++ { CCI_REG8(0x57ad), 0x00 },
++ { CCI_REG8(0x57ae), 0x00 },
++ { CCI_REG8(0x57af), 0x81 },
++ { CCI_REG8(0x57be), 0x00 },
++ { CCI_REG8(0x57bf), 0x00 },
++ { CCI_REG8(0x57c0), 0x00 },
++ { CCI_REG8(0x57c1), 0x81 },
++ { CCI_REG8(0x57d0), 0x00 },
++ { CCI_REG8(0x57d1), 0x00 },
++ { CCI_REG8(0x57d2), 0x00 },
++ { CCI_REG8(0x57d3), 0x81 },
++ { CCI_REG8(0x5324), 0x00 },
++ { CCI_REG8(0x5325), 0x26 },
++ { CCI_REG8(0x5326), 0x00 },
++ { CCI_REG8(0x5327), 0x6b },
++ { CCI_REG8(0xbca7), 0x00 },
++ { CCI_REG8(0x5fcc), 0x28 },
++ { CCI_REG8(0x5fd7), 0x2d },
++ { CCI_REG8(0x5fe2), 0x2d },
++ { CCI_REG8(0x5fed), 0x2d },
++ { CCI_REG8(0x5ff8), 0x2d },
++ { CCI_REG8(0x6003), 0x2d },
++ { CCI_REG8(0x5d0b), 0x01 },
++ { CCI_REG8(0x6f6d), 0x00 },
++ { CCI_REG8(0x61c9), 0x00 },
++ { CCI_REG8(0x5352), 0x00 },
++ { CCI_REG8(0x5353), 0x49 },
++ { CCI_REG8(0x5356), 0x00 },
++ { CCI_REG8(0x5357), 0x30 },
++ { CCI_REG8(0x5358), 0x00 },
++ { CCI_REG8(0x5359), 0x3b },
++ { CCI_REG8(0x535c), 0x00 },
++ { CCI_REG8(0x535d), 0xb0 },
++ { CCI_REG8(0x6187), 0x18 },
++ { CCI_REG8(0x6189), 0x18 },
++ { CCI_REG8(0x618b), 0x18 },
++ { CCI_REG8(0x618d), 0x1d },
++ { CCI_REG8(0x618f), 0x1d },
++ { CCI_REG8(0x5414), 0x01 },
++ { CCI_REG8(0x5415), 0x0c },
++ { CCI_REG8(0xbca8), 0x0a },
++ { CCI_REG8(0x5fcf), 0x1e },
++ { CCI_REG8(0x5fda), 0x1e },
++ { CCI_REG8(0x5fe5), 0x1e },
++ { CCI_REG8(0x5ff0), 0x1e },
++ { CCI_REG8(0x5ffb), 0x1e },
++ { CCI_REG8(0x6006), 0x1e },
++ { CCI_REG8(0x616e), 0x04 },
++ { CCI_REG8(0x616f), 0x04 },
++ { CCI_REG8(0x6170), 0x04 },
++ { CCI_REG8(0x6171), 0x06 },
++ { CCI_REG8(0x6172), 0x06 },
++ { CCI_REG8(0x6173), 0x0c },
++ { CCI_REG8(0x6174), 0x0c },
++ { CCI_REG8(0x6175), 0x0c },
++ { CCI_REG8(0x6176), 0x00 },
++ { CCI_REG8(0x6177), 0x10 },
++ { CCI_REG8(0x6178), 0x00 },
++ { CCI_REG8(0x6179), 0x1a },
++ { CCI_REG8(0x617a), 0x00 },
++ { CCI_REG8(0x617b), 0x1a },
++ { CCI_REG8(0x617c), 0x00 },
++ { CCI_REG8(0x617d), 0x27 },
++ { CCI_REG8(0x617e), 0x00 },
++ { CCI_REG8(0x617f), 0x27 },
++ { CCI_REG8(0x6180), 0x00 },
++ { CCI_REG8(0x6181), 0x44 },
++ { CCI_REG8(0x6182), 0x00 },
++ { CCI_REG8(0x6183), 0x44 },
++ { CCI_REG8(0x6184), 0x00 },
++ { CCI_REG8(0x6185), 0x44 },
++ { CCI_REG8(0x5dfc), 0x0a },
++ { CCI_REG8(0x5e00), 0x0a },
++ { CCI_REG8(0x5e04), 0x0a },
++ { CCI_REG8(0x5e08), 0x0a },
++ { CCI_REG8(0x5dfd), 0x0a },
++ { CCI_REG8(0x5e01), 0x0a },
++ { CCI_REG8(0x5e05), 0x0a },
++ { CCI_REG8(0x5e09), 0x0a },
++ { CCI_REG8(0x5dfe), 0x0a },
++ { CCI_REG8(0x5e02), 0x0a },
++ { CCI_REG8(0x5e06), 0x0a },
++ { CCI_REG8(0x5e0a), 0x0a },
++ { CCI_REG8(0x5dff), 0x0a },
++ { CCI_REG8(0x5e03), 0x0a },
++ { CCI_REG8(0x5e07), 0x0a },
++ { CCI_REG8(0x5e0b), 0x0a },
++ { CCI_REG8(0x5dec), 0x12 },
++ { CCI_REG8(0x5df0), 0x12 },
++ { CCI_REG8(0x5df4), 0x21 },
++ { CCI_REG8(0x5df8), 0x31 },
++ { CCI_REG8(0x5ded), 0x12 },
++ { CCI_REG8(0x5df1), 0x12 },
++ { CCI_REG8(0x5df5), 0x21 },
++ { CCI_REG8(0x5df9), 0x31 },
++ { CCI_REG8(0x5dee), 0x12 },
++ { CCI_REG8(0x5df2), 0x12 },
++ { CCI_REG8(0x5df6), 0x21 },
++ { CCI_REG8(0x5dfa), 0x31 },
++ { CCI_REG8(0x5def), 0x12 },
++ { CCI_REG8(0x5df3), 0x12 },
++ { CCI_REG8(0x5df7), 0x21 },
++ { CCI_REG8(0x5dfb), 0x31 },
++ { CCI_REG8(0x5ddc), 0x0d },
++ { CCI_REG8(0x5de0), 0x0d },
++ { CCI_REG8(0x5de4), 0x0d },
++ { CCI_REG8(0x5de8), 0x0d },
++ { CCI_REG8(0x5ddd), 0x0d },
++ { CCI_REG8(0x5de1), 0x0d },
++ { CCI_REG8(0x5de5), 0x0d },
++ { CCI_REG8(0x5de9), 0x0d },
++ { CCI_REG8(0x5dde), 0x0d },
++ { CCI_REG8(0x5de2), 0x0d },
++ { CCI_REG8(0x5de6), 0x0d },
++ { CCI_REG8(0x5dea), 0x0d },
++ { CCI_REG8(0x5ddf), 0x0d },
++ { CCI_REG8(0x5de3), 0x0d },
++ { CCI_REG8(0x5de7), 0x0d },
++ { CCI_REG8(0x5deb), 0x0d },
++ { CCI_REG8(0x5dcc), 0x55 },
++ { CCI_REG8(0x5dd0), 0x50 },
++ { CCI_REG8(0x5dd4), 0x4b },
++ { CCI_REG8(0x5dd8), 0x4b },
++ { CCI_REG8(0x5dcd), 0x55 },
++ { CCI_REG8(0x5dd1), 0x50 },
++ { CCI_REG8(0x5dd5), 0x4b },
++ { CCI_REG8(0x5dd9), 0x4b },
++ { CCI_REG8(0x5dce), 0x55 },
++ { CCI_REG8(0x5dd2), 0x50 },
++ { CCI_REG8(0x5dd6), 0x4b },
++ { CCI_REG8(0x5dda), 0x4b },
++ { CCI_REG8(0x5dcf), 0x55 },
++ { CCI_REG8(0x5dd3), 0x50 },
++ { CCI_REG8(0x5dd7), 0x4b },
++ { CCI_REG8(0x5ddb), 0x4b },
++ { CCI_REG8(0x0202), 0x12 },
++ { CCI_REG8(0x0203), 0x2c },
++ { CCI_REG8(0x0204), 0x00 },
++ { CCI_REG8(0x0205), 0x00 },
++ { CCI_REG8(0x020e), 0x01 },
++ { CCI_REG8(0x020f), 0x00 },
++ { CCI_REG8(0x0210), 0x01 },
++ { CCI_REG8(0x0211), 0x00 },
++ { CCI_REG8(0x0212), 0x01 },
++ { CCI_REG8(0x0213), 0x00 },
++ { CCI_REG8(0x0214), 0x01 },
++ { CCI_REG8(0x0215), 0x00 },
++};
++
++/* 2x2 binned. 56fps */
++static const struct cci_reg_sequence mode_2028x1520_regs[] = {
++ { CCI_REG8(0x0112), 0x0a },
++ { CCI_REG8(0x0113), 0x0a },
++ { CCI_REG8(0x0114), 0x01 },
++ { CCI_REG8(0x0342), 0x24 },
++ { CCI_REG8(0x0343), 0xb6 },
++ { CCI_REG8(0x0340), 0x0b },
++ { CCI_REG8(0x0341), 0x9c },
++ { CCI_REG8(0x3210), 0x00 },
++ { CCI_REG8(0x0344), 0x00 },
++ { CCI_REG8(0x0345), 0x00 },
++ { CCI_REG8(0x0346), 0x00 },
++ { CCI_REG8(0x0347), 0x00 },
++ { CCI_REG8(0x0348), 0x0f },
++ { CCI_REG8(0x0349), 0xd7 },
++ { CCI_REG8(0x0350), 0x00 },
++ { CCI_REG8(0x034a), 0x0b },
++ { CCI_REG8(0x034b), 0xdf },
++ { CCI_REG8(0x3f58), 0x01 },
++ { CCI_REG8(0x0381), 0x01 },
++ { CCI_REG8(0x0383), 0x01 },
++ { CCI_REG8(0x0385), 0x01 },
++ { CCI_REG8(0x0387), 0x01 },
++ { CCI_REG8(0x0900), 0x01 },
++ { CCI_REG8(0x0901), 0x22 },
++ { CCI_REG8(0x0902), 0x02 },
++ { CCI_REG8(0x3241), 0x11 },
++ { CCI_REG8(0x3242), 0x01 },
++ { CCI_REG8(0x3250), 0x03 },
++ { CCI_REG8(0x3f0f), 0x00 },
++ { CCI_REG8(0x3f40), 0x00 },
++ { CCI_REG8(0x3f41), 0x00 },
++ { CCI_REG8(0x3f42), 0x00 },
++ { CCI_REG8(0x3f43), 0x00 },
++ { CCI_REG8(0xb34e), 0x00 },
++ { CCI_REG8(0xb351), 0x20 },
++ { CCI_REG8(0xb35c), 0x00 },
++ { CCI_REG8(0xb35e), 0x08 },
++ { CCI_REG8(0x0401), 0x00 },
++ { CCI_REG8(0x0404), 0x00 },
++ { CCI_REG8(0x0405), 0x10 },
++ { CCI_REG8(0x0408), 0x00 },
++ { CCI_REG8(0x0409), 0x00 },
++ { CCI_REG8(0x040a), 0x00 },
++ { CCI_REG8(0x040b), 0x00 },
++ { CCI_REG8(0x040c), 0x07 },
++ { CCI_REG8(0x040d), 0xec },
++ { CCI_REG8(0x040e), 0x05 },
++ { CCI_REG8(0x040f), 0xf0 },
++ { CCI_REG8(0x034c), 0x07 },
++ { CCI_REG8(0x034d), 0xec },
++ { CCI_REG8(0x034e), 0x05 },
++ { CCI_REG8(0x034f), 0xf0 },
++ { CCI_REG8(0x0301), 0x05 },
++ { CCI_REG8(0x0303), 0x02 },
++ { CCI_REG8(0x0307), 0x9b },
++ { CCI_REG8(0x0309), 0x0a },
++ { CCI_REG8(0x030b), 0x01 },
++ { CCI_REG8(0x030f), 0x4a },
++ { CCI_REG8(0x0310), 0x01 },
++ { CCI_REG8(0x0820), 0x07 },
++ { CCI_REG8(0x0821), 0xce },
++ { CCI_REG8(0x0822), 0x00 },
++ { CCI_REG8(0x0823), 0x00 },
++ { CCI_REG8(0x3e20), 0x01 },
++ { CCI_REG8(0x3e35), 0x01 },
++ { CCI_REG8(0x3e36), 0x01 },
++ { CCI_REG8(0x3e37), 0x00 },
++ { CCI_REG8(0x3e3a), 0x01 },
++ { CCI_REG8(0x3e3b), 0x00 },
++ { CCI_REG8(0x00e3), 0x00 },
++ { CCI_REG8(0x00e4), 0x00 },
++ { CCI_REG8(0x00e6), 0x00 },
++ { CCI_REG8(0x00e7), 0x00 },
++ { CCI_REG8(0x00e8), 0x00 },
++ { CCI_REG8(0x00e9), 0x00 },
++ { CCI_REG8(0x3f50), 0x00 },
++ { CCI_REG8(0x3f56), 0x01 },
++ { CCI_REG8(0x3f57), 0x30 },
++ { CCI_REG8(0x3606), 0x01 },
++ { CCI_REG8(0x3607), 0x01 },
++ { CCI_REG8(0x3f26), 0x00 },
++ { CCI_REG8(0x3f4a), 0x00 },
++ { CCI_REG8(0x3f4b), 0x00 },
++ { CCI_REG8(0x4bc0), 0x16 },
++ { CCI_REG8(0x7ba8), 0x00 },
++ { CCI_REG8(0x7ba9), 0x00 },
++ { CCI_REG8(0x886b), 0x00 },
++ { CCI_REG8(0x579a), 0x00 },
++ { CCI_REG8(0x579b), 0x0a },
++ { CCI_REG8(0x579c), 0x01 },
++ { CCI_REG8(0x579d), 0x2a },
++ { CCI_REG8(0x57ac), 0x00 },
++ { CCI_REG8(0x57ad), 0x00 },
++ { CCI_REG8(0x57ae), 0x00 },
++ { CCI_REG8(0x57af), 0x81 },
++ { CCI_REG8(0x57be), 0x00 },
++ { CCI_REG8(0x57bf), 0x00 },
++ { CCI_REG8(0x57c0), 0x00 },
++ { CCI_REG8(0x57c1), 0x81 },
++ { CCI_REG8(0x57d0), 0x00 },
++ { CCI_REG8(0x57d1), 0x00 },
++ { CCI_REG8(0x57d2), 0x00 },
++ { CCI_REG8(0x57d3), 0x81 },
++ { CCI_REG8(0x5324), 0x00 },
++ { CCI_REG8(0x5325), 0x31 },
++ { CCI_REG8(0x5326), 0x00 },
++ { CCI_REG8(0x5327), 0x60 },
++ { CCI_REG8(0xbca7), 0x08 },
++ { CCI_REG8(0x5fcc), 0x1e },
++ { CCI_REG8(0x5fd7), 0x1e },
++ { CCI_REG8(0x5fe2), 0x1e },
++ { CCI_REG8(0x5fed), 0x1e },
++ { CCI_REG8(0x5ff8), 0x1e },
++ { CCI_REG8(0x6003), 0x1e },
++ { CCI_REG8(0x5d0b), 0x02 },
++ { CCI_REG8(0x6f6d), 0x01 },
++ { CCI_REG8(0x61c9), 0x68 },
++ { CCI_REG8(0x5352), 0x00 },
++ { CCI_REG8(0x5353), 0x3f },
++ { CCI_REG8(0x5356), 0x00 },
++ { CCI_REG8(0x5357), 0x1c },
++ { CCI_REG8(0x5358), 0x00 },
++ { CCI_REG8(0x5359), 0x3d },
++ { CCI_REG8(0x535c), 0x00 },
++ { CCI_REG8(0x535d), 0xa6 },
++ { CCI_REG8(0x6187), 0x1d },
++ { CCI_REG8(0x6189), 0x1d },
++ { CCI_REG8(0x618b), 0x1d },
++ { CCI_REG8(0x618d), 0x23 },
++ { CCI_REG8(0x618f), 0x23 },
++ { CCI_REG8(0x5414), 0x01 },
++ { CCI_REG8(0x5415), 0x12 },
++ { CCI_REG8(0xbca8), 0x00 },
++ { CCI_REG8(0x5fcf), 0x28 },
++ { CCI_REG8(0x5fda), 0x2d },
++ { CCI_REG8(0x5fe5), 0x2d },
++ { CCI_REG8(0x5ff0), 0x2d },
++ { CCI_REG8(0x5ffb), 0x2d },
++ { CCI_REG8(0x6006), 0x2d },
++ { CCI_REG8(0x616e), 0x04 },
++ { CCI_REG8(0x616f), 0x04 },
++ { CCI_REG8(0x6170), 0x04 },
++ { CCI_REG8(0x6171), 0x06 },
++ { CCI_REG8(0x6172), 0x06 },
++ { CCI_REG8(0x6173), 0x0c },
++ { CCI_REG8(0x6174), 0x0c },
++ { CCI_REG8(0x6175), 0x0c },
++ { CCI_REG8(0x6176), 0x00 },
++ { CCI_REG8(0x6177), 0x10 },
++ { CCI_REG8(0x6178), 0x00 },
++ { CCI_REG8(0x6179), 0x1a },
++ { CCI_REG8(0x617a), 0x00 },
++ { CCI_REG8(0x617b), 0x1a },
++ { CCI_REG8(0x617c), 0x00 },
++ { CCI_REG8(0x617d), 0x27 },
++ { CCI_REG8(0x617e), 0x00 },
++ { CCI_REG8(0x617f), 0x27 },
++ { CCI_REG8(0x6180), 0x00 },
++ { CCI_REG8(0x6181), 0x44 },
++ { CCI_REG8(0x6182), 0x00 },
++ { CCI_REG8(0x6183), 0x44 },
++ { CCI_REG8(0x6184), 0x00 },
++ { CCI_REG8(0x6185), 0x44 },
++ { CCI_REG8(0x5dfc), 0x0a },
++ { CCI_REG8(0x5e00), 0x0a },
++ { CCI_REG8(0x5e04), 0x0a },
++ { CCI_REG8(0x5e08), 0x0a },
++ { CCI_REG8(0x5dfd), 0x0a },
++ { CCI_REG8(0x5e01), 0x0a },
++ { CCI_REG8(0x5e05), 0x0a },
++ { CCI_REG8(0x5e09), 0x0a },
++ { CCI_REG8(0x5dfe), 0x0a },
++ { CCI_REG8(0x5e02), 0x0a },
++ { CCI_REG8(0x5e06), 0x0a },
++ { CCI_REG8(0x5e0a), 0x0a },
++ { CCI_REG8(0x5dff), 0x0a },
++ { CCI_REG8(0x5e03), 0x0a },
++ { CCI_REG8(0x5e07), 0x0a },
++ { CCI_REG8(0x5e0b), 0x0a },
++ { CCI_REG8(0x5dec), 0x12 },
++ { CCI_REG8(0x5df0), 0x12 },
++ { CCI_REG8(0x5df4), 0x21 },
++ { CCI_REG8(0x5df8), 0x31 },
++ { CCI_REG8(0x5ded), 0x12 },
++ { CCI_REG8(0x5df1), 0x12 },
++ { CCI_REG8(0x5df5), 0x21 },
++ { CCI_REG8(0x5df9), 0x31 },
++ { CCI_REG8(0x5dee), 0x12 },
++ { CCI_REG8(0x5df2), 0x12 },
++ { CCI_REG8(0x5df6), 0x21 },
++ { CCI_REG8(0x5dfa), 0x31 },
++ { CCI_REG8(0x5def), 0x12 },
++ { CCI_REG8(0x5df3), 0x12 },
++ { CCI_REG8(0x5df7), 0x21 },
++ { CCI_REG8(0x5dfb), 0x31 },
++ { CCI_REG8(0x5ddc), 0x0d },
++ { CCI_REG8(0x5de0), 0x0d },
++ { CCI_REG8(0x5de4), 0x0d },
++ { CCI_REG8(0x5de8), 0x0d },
++ { CCI_REG8(0x5ddd), 0x0d },
++ { CCI_REG8(0x5de1), 0x0d },
++ { CCI_REG8(0x5de5), 0x0d },
++ { CCI_REG8(0x5de9), 0x0d },
++ { CCI_REG8(0x5dde), 0x0d },
++ { CCI_REG8(0x5de2), 0x0d },
++ { CCI_REG8(0x5de6), 0x0d },
++ { CCI_REG8(0x5dea), 0x0d },
++ { CCI_REG8(0x5ddf), 0x0d },
++ { CCI_REG8(0x5de3), 0x0d },
++ { CCI_REG8(0x5de7), 0x0d },
++ { CCI_REG8(0x5deb), 0x0d },
++ { CCI_REG8(0x5dcc), 0x55 },
++ { CCI_REG8(0x5dd0), 0x50 },
++ { CCI_REG8(0x5dd4), 0x4b },
++ { CCI_REG8(0x5dd8), 0x4b },
++ { CCI_REG8(0x5dcd), 0x55 },
++ { CCI_REG8(0x5dd1), 0x50 },
++ { CCI_REG8(0x5dd5), 0x4b },
++ { CCI_REG8(0x5dd9), 0x4b },
++ { CCI_REG8(0x5dce), 0x55 },
++ { CCI_REG8(0x5dd2), 0x50 },
++ { CCI_REG8(0x5dd6), 0x4b },
++ { CCI_REG8(0x5dda), 0x4b },
++ { CCI_REG8(0x5dcf), 0x55 },
++ { CCI_REG8(0x5dd3), 0x50 },
++ { CCI_REG8(0x5dd7), 0x4b },
++ { CCI_REG8(0x5ddb), 0x4b },
++ { CCI_REG8(0x0202), 0x0b },
++ { CCI_REG8(0x0203), 0x86 },
++ { CCI_REG8(0x0204), 0x00 },
++ { CCI_REG8(0x0205), 0x00 },
++ { CCI_REG8(0x020e), 0x01 },
++ { CCI_REG8(0x020f), 0x00 },
++ { CCI_REG8(0x0210), 0x01 },
++ { CCI_REG8(0x0211), 0x00 },
++ { CCI_REG8(0x0212), 0x01 },
++ { CCI_REG8(0x0213), 0x00 },
++ { CCI_REG8(0x0214), 0x01 },
++ { CCI_REG8(0x0215), 0x00 },
++};
++
++/* Mode configs */
++static const struct imx500_mode imx500_supported_modes[] = {
++ {
++ /* 12MPix 10fps mode */
++ .width = 4056,
++ .height = 3040,
++ .line_length_pix = 17900,
++ .crop = {
++ .left = IMX500_PIXEL_ARRAY_LEFT,
++ .top = IMX500_PIXEL_ARRAY_TOP,
++ .width = 4056,
++ .height = 3040,
++ },
++ .framerate_default = 10,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
++ .regs = mode_4056x3040_regs,
++ },
++ },
++ {
++ /* 2x2 binned 40fps mode */
++ .width = 2028,
++ .height = 1520,
++ .line_length_pix = 9398,
++ .crop = {
++ .left = IMX500_PIXEL_ARRAY_LEFT,
++ .top = IMX500_PIXEL_ARRAY_TOP,
++ .width = 4056,
++ .height = 3040,
++ },
++ .framerate_default = 40,
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs),
++ .regs = mode_2028x1520_regs,
++ },
++ },
++};
++
++/*
++ * The supported formats.
++ * This table MUST contain 4 entries per format, to cover the various flip
++ * combinations in the order
++ * - no flip
++ * - h flip
++ * - v flip
++ * - h&v flips
++ */
++static const u32 codes[] = {
++ /* 10-bit modes. */
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++};
++
++struct imx500 {
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++ struct regmap *regmap;
++
++ unsigned int fmt_code;
++
++ struct clk *xclk;
++ u32 xclk_freq;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[IMX500_NUM_SUPPLIES];
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ /* V4L2 Controls */
++ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *link_freq;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++
++ /* Current mode */
++ const struct imx500_mode *mode;
++
++ /*
++ * Mutex for serialized access:
++ * Protect sensor module set pad format and start/stop streaming safely.
++ */
++ struct mutex mutex;
++
++ /* Streaming on/off */
++ bool streaming;
++
++ /* Rewrite common registers on stream on? */
++ bool common_regs_written;
++
++ /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
++ unsigned int long_exp_shift;
++};
++
++static inline struct imx500 *to_imx500(struct v4l2_subdev *_sd)
++{
++ return container_of(_sd, struct imx500, sd);
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx500_get_format_code(struct imx500 *imx500)
++{
++ unsigned int i;
++
++ lockdep_assert_held(&imx500->mutex);
++
++ i = (imx500->vflip->val ? 2 : 0) | (imx500->hflip->val ? 1 : 0);
++
++ return codes[i];
++}
++
++static void imx500_set_default_format(struct imx500 *imx500)
++{
++ /* Set default mode to max resolution */
++ imx500->mode = &imx500_supported_modes[0];
++ imx500->fmt_code = MEDIA_BUS_FMT_SRGGB10_1X10;
++}
++
++static void imx500_adjust_exposure_range(struct imx500 *imx500)
++{
++ int exposure_max, exposure_def;
++
++ /* Honour the VBLANK limits when setting exposure. */
++ exposure_max = imx500->mode->height + imx500->vblank->val -
++ IMX500_EXPOSURE_OFFSET;
++ exposure_def = min(exposure_max, imx500->exposure->val);
++ __v4l2_ctrl_modify_range(imx500->exposure, imx500->exposure->minimum,
++ exposure_max, imx500->exposure->step,
++ exposure_def);
++}
++
++static int imx500_set_frame_length(struct imx500 *imx500, unsigned int val)
++{
++ int ret = 0;
++
++ imx500->long_exp_shift = 0;
++
++ while (val > IMX500_FRAME_LENGTH_MAX) {
++ imx500->long_exp_shift++;
++ val >>= 1;
++ }
++
++ ret = cci_write(imx500->regmap, IMX500_REG_FRAME_LENGTH, val, NULL);
++ if (ret)
++ return ret;
++
++ return cci_write(imx500->regmap, IMX500_LONG_EXP_SHIFT_REG,
++ imx500->long_exp_shift, NULL);
++}
++
++static int imx500_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct imx500 *imx500 =
++ container_of(ctrl->handler, struct imx500, ctrl_handler);
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ int ret = 0;
++
++ /*
++ * The VBLANK control may change the limits of usable exposure, so check
++ * and adjust if necessary.
++ */
++ if (ctrl->id == V4L2_CID_VBLANK)
++ imx500_adjust_exposure_range(imx500);
++
++ /*
++ * Applying V4L2 control value only happens
++ * when power is up for streaming
++ */
++ if (pm_runtime_get_if_in_use(&client->dev) == 0)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = cci_write(imx500->regmap, IMX500_REG_ANALOG_GAIN,
++ ctrl->val, NULL);
++ break;
++ case V4L2_CID_EXPOSURE:
++ ret = cci_write(imx500->regmap, IMX500_REG_EXPOSURE,
++ ctrl->val >> imx500->long_exp_shift, NULL);
++ break;
++ case V4L2_CID_HFLIP:
++ case V4L2_CID_VFLIP:
++ ret = cci_write(imx500->regmap, IMX500_REG_ORIENTATION,
++ imx500->hflip->val | imx500->vflip->val << 1,
++ NULL);
++ break;
++ case V4L2_CID_VBLANK:
++ ret = imx500_set_frame_length(imx500,
++ imx500->mode->height + ctrl->val);
++ break;
++ case V4L2_CID_HBLANK:
++ ret = cci_write(imx500->regmap, IMX500_REG_LINE_LENGTH,
++ imx500->mode->width + ctrl->val, NULL);
++ break;
++ case V4L2_CID_NOTIFY_GAINS:
++ ret = cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_B,
++ ctrl->p_new.p_u32[0], NULL);
++ cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_GB,
++ ctrl->p_new.p_u32[1], &ret);
++ cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_GR,
++ ctrl->p_new.p_u32[2], &ret);
++ cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_R,
++ ctrl->p_new.p_u32[3], &ret);
++ break;
++ default:
++ dev_info(&client->dev,
++ "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id,
++ ctrl->val);
++ ret = -EINVAL;
++ break;
++ }
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops imx500_ctrl_ops = {
++ .s_ctrl = imx500_set_ctrl,
++};
++
++static const struct v4l2_ctrl_config imx500_notify_gains_ctrl = {
++ .ops = &imx500_ctrl_ops,
++ .id = V4L2_CID_NOTIFY_GAINS,
++ .type = V4L2_CTRL_TYPE_U32,
++ .min = IMX500_COLOUR_BALANCE_MIN,
++ .max = IMX500_COLOUR_BALANCE_MAX,
++ .step = IMX500_COLOUR_BALANCE_STEP,
++ .def = IMX500_COLOUR_BALANCE_DEFAULT,
++ .dims = { 4 },
++ .elem_size = sizeof(u32),
++};
++
++static int imx500_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct imx500 *imx500 = to_imx500(sd);
++
++ if (code->pad >= NUM_PADS)
++ return -EINVAL;
++
++ if (code->index != 0)
++ return -EINVAL;
++
++ code->code = imx500_get_format_code(imx500);
++
++ return 0;
++}
++
++static int imx500_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct imx500 *imx500 = to_imx500(sd);
++
++ if (fse->pad >= NUM_PADS)
++ return -EINVAL;
++
++ const struct imx500_mode *mode_list = imx500_supported_modes;
++ unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
++
++ if (fse->index >= num_modes)
++ return -EINVAL;
++
++ if (fse->code != imx500_get_format_code(imx500))
++ return -EINVAL;
++
++ fse->min_width = mode_list[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = mode_list[fse->index].height;
++ fse->max_height = fse->min_height;
++
++ return 0;
++}
++
++static void imx500_update_image_pad_format(struct imx500 *imx500,
++ const struct imx500_mode *mode,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width = mode->width;
++ fmt->format.height = mode->height;
++ fmt->format.field = V4L2_FIELD_NONE;
++ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
++ fmt->format.ycbcr_enc =
++ V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->format.colorspace);
++ fmt->format.quantization = V4L2_MAP_QUANTIZATION_DEFAULT(
++ true, fmt->format.colorspace, fmt->format.ycbcr_enc);
++ fmt->format.xfer_func =
++ V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
++}
++
++static int imx500_get_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct imx500 *imx500 = to_imx500(sd);
++
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
++
++ mutex_lock(&imx500->mutex);
++
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(
++ &imx500->sd, sd_state, fmt->pad);
++ /* update the code which could change due to vflip or hflip */
++ try_fmt->code = imx500_get_format_code(imx500);
++ fmt->format = *try_fmt;
++ } else {
++ imx500_update_image_pad_format(imx500, imx500->mode, fmt);
++ fmt->format.code = imx500_get_format_code(imx500);
++ }
++
++ mutex_unlock(&imx500->mutex);
++ return 0;
++}
++
++static unsigned int imx500_get_frame_length(const struct imx500_mode *mode,
++ unsigned int framerate_default)
++{
++ u64 frame_length;
++
++ frame_length = IMX500_PIXEL_RATE;
++ do_div(frame_length, (u64)framerate_default * mode->line_length_pix);
++
++ if (WARN_ON(frame_length > IMX500_FRAME_LENGTH_MAX))
++ frame_length = IMX500_FRAME_LENGTH_MAX;
++
++ return max_t(unsigned int, frame_length, mode->height);
++}
++
++static void imx500_set_framing_limits(struct imx500 *imx500)
++{
++ unsigned int frm_length_default, hblank_min;
++ const struct imx500_mode *mode = imx500->mode;
++
++ frm_length_default =
++ imx500_get_frame_length(mode, mode->framerate_default);
++
++ /* Default to no long exposure multiplier. */
++ imx500->long_exp_shift = 0;
++
++ /* Update limits and set FPS to default */
++ __v4l2_ctrl_modify_range(
++ imx500->vblank, 1,
++ ((1 << IMX500_LONG_EXP_SHIFT_MAX) * IMX500_FRAME_LENGTH_MAX) -
++ mode->height,
++ IMX500_VBLANK_MIN, frm_length_default - mode->height);
++
++ /* Setting this will adjust the exposure limits as well. */
++ __v4l2_ctrl_s_ctrl(imx500->vblank, frm_length_default - mode->height);
++
++ hblank_min = mode->line_length_pix - mode->width;
++ __v4l2_ctrl_modify_range(imx500->hblank, hblank_min, hblank_min, 1,
++ hblank_min);
++ __v4l2_ctrl_s_ctrl(imx500->hblank, hblank_min);
++}
++
++static int imx500_set_pad_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct v4l2_mbus_framefmt *framefmt;
++ const struct imx500_mode *mode;
++ struct imx500 *imx500 = to_imx500(sd);
++
++ if (fmt->pad >= NUM_PADS)
++ return -EINVAL;
++
++ mutex_lock(&imx500->mutex);
++
++ const struct imx500_mode *mode_list = imx500_supported_modes;
++ unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
++
++ /* Bayer order varies with flips */
++ fmt->format.code = imx500_get_format_code(imx500);
++
++ mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
++ fmt->format.width, fmt->format.height);
++ imx500_update_image_pad_format(imx500, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
++ *framefmt = fmt->format;
++ } else if (imx500->mode != mode) {
++ imx500->mode = mode;
++ imx500->fmt_code = fmt->format.code;
++ imx500_set_framing_limits(imx500);
++ }
++
++ mutex_unlock(&imx500->mutex);
++
++ return 0;
++}
++
++static const struct v4l2_rect *
++__imx500_get_pad_crop(struct imx500 *imx500, struct v4l2_subdev_state *sd_state,
++ unsigned int pad, enum v4l2_subdev_format_whence which)
++{
++ switch (which) {
++ case V4L2_SUBDEV_FORMAT_TRY:
++ return v4l2_subdev_get_try_crop(&imx500->sd, sd_state, pad);
++ case V4L2_SUBDEV_FORMAT_ACTIVE:
++ return &imx500->mode->crop;
++ }
++
++ return NULL;
++}
++
++static int imx500_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP: {
++ struct imx500 *imx500 = to_imx500(sd);
++
++ mutex_lock(&imx500->mutex);
++ sel->r = *__imx500_get_pad_crop(imx500, sd_state, sel->pad,
++ sel->which);
++ mutex_unlock(&imx500->mutex);
++
++ return 0;
++ }
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.left = 0;
++ sel->r.top = 0;
++ sel->r.width = IMX500_NATIVE_WIDTH;
++ sel->r.height = IMX500_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ case V4L2_SEL_TGT_CROP_BOUNDS:
++ sel->r.left = IMX500_PIXEL_ARRAY_LEFT;
++ sel->r.top = IMX500_PIXEL_ARRAY_TOP;
++ sel->r.width = IMX500_PIXEL_ARRAY_WIDTH;
++ sel->r.height = IMX500_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++/* Start streaming */
++static int imx500_start_streaming(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ const struct imx500_reg_list *reg_list;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(&client->dev);
++ if (ret < 0)
++ return ret;
++
++ ret = cci_write(imx500->regmap, IMX500_REG_IMAGE_ONLY_MODE,
++ IMX500_IMAGE_ONLY_TRUE,
++ NULL);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set image mode\n",
++ __func__);
++ return ret;
++ }
++
++ if (!imx500->common_regs_written) {
++ ret = cci_multi_reg_write(imx500->regmap, mode_common_regs,
++ ARRAY_SIZE(mode_common_regs), NULL);
++
++ if (ret) {
++ dev_err(&client->dev,
++ "%s failed to set common settings\n", __func__);
++ return ret;
++ }
++
++ imx500->common_regs_written = true;
++ }
++
++ /* Apply default values of current mode */
++ reg_list = &imx500->mode->reg_list;
++ ret = cci_multi_reg_write(imx500->regmap, reg_list->regs,
++ reg_list->num_of_regs, NULL);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to set mode\n", __func__);
++ return ret;
++ }
++
++ /* Apply customized values from user */
++ ret = __v4l2_ctrl_handler_setup(imx500->sd.ctrl_handler);
++
++ /* Disable any sensor startup frame drops. This must be written here! */
++ cci_write(imx500->regmap, CCI_REG8(0xD405), 0, &ret);
++
++ /* set stream on register */
++ cci_write(imx500->regmap, IMX500_REG_MODE_SELECT, IMX500_MODE_STREAMING,
++ &ret);
++
++ return ret;
++}
++
++/* Stop streaming */
++static void imx500_stop_streaming(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ int ret;
++
++ /* set stream off register */
++ ret = cci_write(imx500->regmap, IMX500_REG_MODE_SELECT,
++ IMX500_MODE_STANDBY, NULL);
++ if (ret)
++ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++}
++
++static int imx500_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct imx500 *imx500 = to_imx500(sd);
++ int ret = 0;
++
++ mutex_lock(&imx500->mutex);
++ if (imx500->streaming == enable) {
++ mutex_unlock(&imx500->mutex);
++ return 0;
++ }
++
++ if (enable) {
++ /*
++ * Apply default & customized values
++ * and then start streaming.
++ */
++ ret = imx500_start_streaming(imx500);
++ if (ret)
++ goto err_start_streaming;
++ } else {
++ imx500_stop_streaming(imx500);
++ }
++
++ imx500->streaming = enable;
++
++ /* vflip and hflip cannot change during streaming */
++ __v4l2_ctrl_grab(imx500->vflip, enable);
++ __v4l2_ctrl_grab(imx500->hflip, enable);
++
++ mutex_unlock(&imx500->mutex);
++
++ return ret;
++
++err_start_streaming:
++ mutex_unlock(&imx500->mutex);
++
++ return ret;
++}
++
++/* Power/clock management functions */
++static int imx500_power_on(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx500 *imx500 = to_imx500(sd);
++ int ret;
++
++ ret = regulator_bulk_enable(IMX500_NUM_SUPPLIES, imx500->supplies);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable regulators\n",
++ __func__);
++ return ret;
++ }
++
++ /* T4 - 1us
++ * Ambiguous: Regulators rising to INCK start is specified by the datasheet
++ * but also "Presence of INCK during Power off is acceptable"
++ */
++ udelay(2);
++
++ ret = clk_prepare_enable(imx500->xclk);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to enable clock\n", __func__);
++ goto reg_off;
++ }
++
++ /* T5 - 0ms
++ * Ambiguous: Regulators rising to XCLR rising is specified by the datasheet
++ * as 0ms but also "XCLR pin should be set to 'High' after INCK supplied.".
++ * T4 and T5 are shown as overlapping.
++ */
++ gpiod_set_value_cansleep(imx500->reset_gpio, 1);
++
++ /* T7 - 9ms
++ * "INCK start and CXLR rising till Send Streaming Command wait time"
++ */
++ usleep_range(9000, 12000);
++
++ return 0;
++
++reg_off:
++ regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies);
++ return ret;
++}
++
++static int imx500_power_off(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx500 *imx500 = to_imx500(sd);
++
++ /* Datasheet specifies power down sequence as INCK disable, XCLR low,
++ * regulator disable. T1 (XCLR neg-edge to regulator disable) is specified
++ * as 0us.
++ *
++ * Note, this is not the reverse order of power up.
++ */
++ clk_disable_unprepare(imx500->xclk);
++ gpiod_set_value_cansleep(imx500->reset_gpio, 0);
++ regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies);
++
++ /* Force reprogramming of the common registers when powered up again. */
++ imx500->common_regs_written = false;
++
++ return 0;
++}
++
++static int imx500_get_regulators(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ unsigned int i;
++
++ for (i = 0; i < IMX500_NUM_SUPPLIES; i++)
++ imx500->supplies[i].supply = imx500_supply_name[i];
++
++ return devm_regulator_bulk_get(&client->dev, IMX500_NUM_SUPPLIES,
++ imx500->supplies);
++}
++
++/* Verify chip ID */
++static int imx500_identify_module(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ int ret;
++ u64 val;
++
++ ret = cci_read(imx500->regmap, IMX500_REG_CHIP_ID, &val, NULL);
++ if (ret) {
++ dev_err(&client->dev,
++ "failed to read chip id %x, with error %d\n",
++ IMX500_CHIP_ID, ret);
++ return ret;
++ }
++
++ if (val != IMX500_CHIP_ID) {
++ dev_err(&client->dev, "chip id mismatch: %x!=%llx\n",
++ IMX500_CHIP_ID, val);
++ return -EIO;
++ }
++
++ dev_info(&client->dev, "Device found is imx%llx\n", val);
++
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx500_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx500_video_ops = {
++ .s_stream = imx500_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx500_pad_ops = {
++ .enum_mbus_code = imx500_enum_mbus_code,
++ .get_fmt = imx500_get_pad_format,
++ .set_fmt = imx500_set_pad_format,
++ .get_selection = imx500_get_selection,
++ .enum_frame_size = imx500_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx500_subdev_ops = {
++ .core = &imx500_core_ops,
++ .video = &imx500_video_ops,
++ .pad = &imx500_pad_ops,
++};
++
++static const s64 imx500_link_freq_menu[] = {
++ IMX500_DEFAULT_LINK_FREQ,
++};
++
++/* Initialize control handlers */
++static int imx500_init_controls(struct imx500 *imx500)
++{
++ struct v4l2_ctrl_handler *ctrl_hdlr;
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ struct v4l2_fwnode_device_properties props;
++ int ret;
++
++ ctrl_hdlr = &imx500->ctrl_handler;
++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
++ if (ret)
++ return ret;
++
++ mutex_init(&imx500->mutex);
++ ctrl_hdlr->lock = &imx500->mutex;
++
++ /* By default, PIXEL_RATE is read only */
++ imx500->pixel_rate = v4l2_ctrl_new_std(
++ ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_PIXEL_RATE,
++ IMX500_PIXEL_RATE, IMX500_PIXEL_RATE, 1, IMX500_PIXEL_RATE);
++
++ /* LINK_FREQ is also read only */
++ imx500->link_freq =
++ v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx500_ctrl_ops,
++ V4L2_CID_LINK_FREQ,
++ ARRAY_SIZE(imx500_link_freq_menu) - 1, 0,
++ imx500_link_freq_menu);
++ if (imx500->link_freq)
++ imx500->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++ /*
++ * Create the controls here, but mode specific limits are setup
++ * in the imx500_set_framing_limits() call below.
++ */
++ imx500->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
++ V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
++ imx500->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
++ V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
++
++ imx500->exposure = v4l2_ctrl_new_std(
++ ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_EXPOSURE,
++ IMX500_EXPOSURE_MIN, IMX500_EXPOSURE_MAX, IMX500_EXPOSURE_STEP,
++ IMX500_EXPOSURE_DEFAULT);
++
++ v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ IMX500_ANA_GAIN_MIN, IMX500_ANA_GAIN_MAX,
++ IMX500_ANA_GAIN_STEP, IMX500_ANA_GAIN_DEFAULT);
++
++ imx500->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ if (imx500->hflip)
++ imx500->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ imx500->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++ if (imx500->vflip)
++ imx500->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ v4l2_ctrl_new_custom(ctrl_hdlr, &imx500_notify_gains_ctrl, NULL);
++
++ if (ctrl_hdlr->error) {
++ ret = ctrl_hdlr->error;
++ dev_err(&client->dev, "%s control init failed (%d)\n", __func__,
++ ret);
++ goto error;
++ }
++
++ ret = v4l2_fwnode_device_parse(&client->dev, &props);
++ if (ret)
++ goto error;
++
++ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx500_ctrl_ops,
++ &props);
++ if (ret)
++ goto error;
++
++ imx500->sd.ctrl_handler = ctrl_hdlr;
++
++ /* Setup exposure and frame/line length limits. */
++ imx500_set_framing_limits(imx500);
++
++ return 0;
++
++error:
++ v4l2_ctrl_handler_free(ctrl_hdlr);
++ mutex_destroy(&imx500->mutex);
++
++ return ret;
++}
++
++static void imx500_free_controls(struct imx500 *imx500)
++{
++ v4l2_ctrl_handler_free(imx500->sd.ctrl_handler);
++ mutex_destroy(&imx500->mutex);
++}
++
++static int imx500_check_hwcfg(struct device *dev)
++{
++ struct fwnode_handle *endpoint;
++ struct v4l2_fwnode_endpoint ep_cfg = { .bus_type =
++ V4L2_MBUS_CSI2_DPHY };
++ int ret = -EINVAL;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++ if (!endpoint) {
++ dev_err(dev, "endpoint node not found\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++ dev_err(dev, "could not parse endpoint\n");
++ goto error_out;
++ }
++
++ /* Check the number of MIPI CSI2 data lanes */
++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(dev, "only 2 data lanes are currently supported\n");
++ goto error_out;
++ }
++
++ /* Check the link frequency set in device tree */
++ if (!ep_cfg.nr_of_link_frequencies) {
++ dev_err(dev, "link-frequency property not found in DT\n");
++ goto error_out;
++ }
++
++ if (ep_cfg.nr_of_link_frequencies != 1 ||
++ ep_cfg.link_frequencies[0] != IMX500_DEFAULT_LINK_FREQ) {
++ dev_err(dev, "Link frequency not supported: %lld\n",
++ ep_cfg.link_frequencies[0]);
++ goto error_out;
++ }
++
++ ret = 0;
++
++error_out:
++ v4l2_fwnode_endpoint_free(&ep_cfg);
++ fwnode_handle_put(endpoint);
++
++ return ret;
++}
++
++static int imx500_probe(struct i2c_client *client)
++{
++ struct device *dev = &client->dev;
++ struct imx500 *imx500;
++ int ret;
++
++ imx500 = devm_kzalloc(&client->dev, sizeof(*imx500), GFP_KERNEL);
++ if (!imx500)
++ return -ENOMEM;
++
++ imx500->regmap = devm_cci_regmap_init_i2c(client, 16);
++ if (IS_ERR(imx500->regmap))
++ return dev_err_probe(dev, PTR_ERR(imx500->regmap),
++ "failed to initialise CCI\n");
++
++ v4l2_i2c_subdev_init(&imx500->sd, client, &imx500_subdev_ops);
++
++ /* Check the hardware configuration in device tree */
++ if (imx500_check_hwcfg(dev))
++ return -EINVAL;
++
++ /* Get system clock (xclk) */
++ imx500->xclk = devm_clk_get(dev, NULL);
++ if (IS_ERR(imx500->xclk))
++ return dev_err_probe(dev, PTR_ERR(imx500->xclk),
++ "failed to get xclk\n");
++
++ imx500->xclk_freq = clk_get_rate(imx500->xclk);
++ if (imx500->xclk_freq != IMX500_XCLK_FREQ) {
++ dev_err(dev, "xclk frequency not supported: %d Hz\n",
++ imx500->xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = imx500_get_regulators(imx500);
++ if (ret) {
++ dev_err(dev, "failed to get regulators\n");
++ return ret;
++ }
++
++ imx500->reset_gpio =
++ devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
++
++ /*
++ * The sensor must be powered for imx500_identify_module()
++ * to be able to read the CHIP_ID register
++ */
++ ret = imx500_power_on(dev);
++ if (ret)
++ return ret;
++
++ pm_runtime_set_active(dev);
++ pm_runtime_get_noresume(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_set_autosuspend_delay(dev, 5000);
++ pm_runtime_use_autosuspend(dev);
++
++ ret = imx500_identify_module(imx500);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize default format */
++ imx500_set_default_format(imx500);
++
++ /* This needs the pm runtime to be registered. */
++ ret = imx500_init_controls(imx500);
++ if (ret)
++ goto error_power_off;
++
++ /* Initialize subdev */
++ imx500->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++ V4L2_SUBDEV_FL_HAS_EVENTS;
++ imx500->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ /* Initialize source pads */
++ imx500->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&imx500->sd.entity, NUM_PADS,
++ &imx500->pad);
++ if (ret) {
++ dev_err(dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ret = v4l2_async_register_subdev_sensor(&imx500->sd);
++ if (ret < 0) {
++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++
++ return 0;
++
++error_media_entity:
++ media_entity_cleanup(&imx500->sd.entity);
++
++error_handler_free:
++ imx500_free_controls(imx500);
++
++error_power_off:
++ pm_runtime_disable(&client->dev);
++ pm_runtime_put_noidle(&client->dev);
++ imx500_power_off(&client->dev);
++
++ return ret;
++}
++
++static void imx500_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct imx500 *imx500 = to_imx500(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ media_entity_cleanup(&sd->entity);
++ imx500_free_controls(imx500);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ imx500_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id imx500_dt_ids[] = {
++ { .compatible = "sony,imx500" },
++ { /* sentinel */ }
++};
++
++MODULE_DEVICE_TABLE(of, imx500_dt_ids);
++
++static const struct dev_pm_ops imx500_pm_ops = { SET_RUNTIME_PM_OPS(
++ imx500_power_off, imx500_power_on, NULL) };
++
++static struct i2c_driver imx500_i2c_driver = {
++ .driver = {
++ .name = "imx500",
++ .of_match_table = imx500_dt_ids,
++ .pm = &imx500_pm_ops,
++ },
++ .probe = imx500_probe,
++ .remove = imx500_remove,
++};
++
++module_i2c_driver(imx500_i2c_driver);
++
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
++MODULE_DESCRIPTION("Sony IMX500 sensor driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From a32f7dae82774d6064181dcc989c1c1349c4e47e Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Thu, 20 Jun 2024 09:58:32 +0100
+Subject: [PATCH 1245/1350] lib: earlycpio: export symbol find_cpio_data()
+
+Add EXPORT_SYMBOL_GPL() for find_cpio_data() so that loadable modules
+may also parse uncompressed cpio.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ lib/earlycpio.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/lib/earlycpio.c
++++ b/lib/earlycpio.c
+@@ -139,3 +139,4 @@ struct cpio_data find_cpio_data(const ch
+ quit:
+ return cd;
+ }
++EXPORT_SYMBOL_GPL(find_cpio_data);
--- /dev/null
+From 97f4ec49828877642e4a87f9c2f47c7a9dd10b90 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Wed, 24 Jul 2024 13:06:16 +0100
+Subject: [PATCH 1246/1350] media: i2c: imx500: Inbuilt AI processor support
+
+Add support for the IMX500's inbuilt AI processor. The IMX500 program
+loader, AI processor firmware, DNN weights are accessed via the kernel's
+firmware interface on 'open' and are transferred to the IMX500 over SPI.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ drivers/media/i2c/imx500.c | 1322 +++++++++++++++++++++++++++-
+ include/uapi/linux/v4l2-controls.h | 6 +
+ 2 files changed, 1290 insertions(+), 38 deletions(-)
+
+--- a/drivers/media/i2c/imx500.c
++++ b/drivers/media/i2c/imx500.c
+@@ -5,9 +5,14 @@
+ */
+ #include <asm/unaligned.h>
+ #include <linux/clk.h>
++#include <linux/debugfs.h>
+ #include <linux/delay.h>
++#include <linux/earlycpio.h>
++#include <linux/firmware.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
++#include <linux/kernel_read_file.h>
++#include <linux/limits.h>
+ #include <linux/module.h>
+ #include <linux/of_device.h>
+ #include <linux/pm_runtime.h>
+@@ -67,6 +72,138 @@
+ #define IMX500_ANA_GAIN_STEP 1
+ #define IMX500_ANA_GAIN_DEFAULT 0x0
+
++/* Inference windows */
++#define IMX500_REG_DWP_AP_VC_VOFF CCI_REG16(0xD500)
++#define IMX500_REG_DWP_AP_VC_HOFF CCI_REG16(0xD502)
++#define IMX500_REG_DWP_AP_VC_VSIZE CCI_REG16(0xD504)
++#define IMX500_REG_DWP_AP_VC_HSIZE CCI_REG16(0xD506)
++
++#define IMX500_REG_DD_CH06_X_OUT_SIZE \
++ CCI_REG16(0x3054) /* Output pixel count for KPI */
++#define IMX500_REG_DD_CH07_X_OUT_SIZE \
++ CCI_REG16(0x3056) /* Output pixel count for Input Tensor */
++#define IMX500_REG_DD_CH08_X_OUT_SIZE \
++ CCI_REG16(0x3058) /* Output pixel count for Output Tensor */
++#define IMX500_REG_DD_CH09_X_OUT_SIZE \
++ CCI_REG16(0x305A) /* Output pixel count for PQ Settings */
++
++#define IMX500_REG_DD_CH06_Y_OUT_SIZE \
++ CCI_REG16(0x305C) /* Output line count for KPI */
++#define IMX500_REG_DD_CH07_Y_OUT_SIZE \
++ CCI_REG16(0x305E) /* Output line count for Input Tensor */
++#define IMX500_REG_DD_CH08_Y_OUT_SIZE \
++ CCI_REG16(0x3060) /* Output line count for Output Tensor */
++#define IMX500_REG_DD_CH09_Y_OUT_SIZE \
++ CCI_REG16(0x3062) /* Output line count for PQ Settings */
++
++#define IMX500_REG_DD_CH06_VCID \
++ CCI_REG8(0x3064) /* Virtual channel ID for KPI */
++#define IMX500_REG_DD_CH07_VCID \
++ CCI_REG8(0x3065) /* Virtual channel ID for Input Tensor */
++#define IMX500_REG_DD_CH08_VCID \
++ CCI_REG8(0x3066) /* Virtual channel ID for Output Tensor */
++#define IMX500_REG_DD_CH09_VCID \
++ CCI_REG8(0x3067) /* Virtual channel ID for PQ Settings */
++
++#define IMX500_REG_DD_CH06_DT CCI_REG8(0x3068) /* Data Type for KPI */
++#define IMX500_REG_DD_CH07_DT CCI_REG8(0x3069) /* Data Type for Input Tensor */
++#define IMX500_REG_DD_CH08_DT CCI_REG8(0x306A) /* Data Type for Output Tensor */
++#define IMX500_REG_DD_CH09_DT CCI_REG8(0x306B) /* Data Type for PQ Settings */
++
++#define IMX500_REG_DD_CH06_PACKING \
++ CCI_REG8(0x306C) /* Pixel/byte packing for KPI */
++#define IMX500_REG_DD_CH07_PACKING \
++ CCI_REG8(0x306D) /* Pixel/byte packing for Input Tensor */
++#define IMX500_REG_DD_CH08_PACKING \
++ CCI_REG8(0x306E) /* Pixel/byte packing for Output Tensor */
++#define IMX500_REG_DD_CH09_PACKING \
++ CCI_REG8(0x306F) /* Pixel/byte packing for PQ Settings */
++#define IMX500_DD_PACKING_8BPP 2 /* 8 bits/pixel */
++#define IMX500_DD_PACKING_10BPP 3 /* 10 bits/pixel */
++
++/* Interrupt command (start processing command inside IMX500 CPU) */
++#define IMX500_REG_DD_CMD_INT CCI_REG8(0x3080)
++#define IMX500_DD_CMD_INT_ST_TRANS 0
++#define IMX500_DD_CMD_INT_UPDATE 1
++#define IMX500_DD_CMD_INT_FLASH_ERASE 2
++
++/* State transition command type */
++#define IMX500_REG_DD_ST_TRANS_CMD CCI_REG8(0xD000)
++#define IMX500_DD_ST_TRANS_CMD_LOADER_FW 0
++#define IMX500_DD_ST_TRANS_CMD_MAIN_FW 1
++#define IMX500_DD_ST_TRANS_CMD_NW_WEIGHTS 2
++#define IMX500_DD_ST_TRANS_CMD_CLEAR_WEIGHTS 3
++
++/* Network weights update command */
++#define IMX500_REG_DD_UPDATE_CMD CCI_REG8(0xD001)
++#define IMX500_DD_UPDATE_CMD_SRAM 0
++#define IMX500_DD_UPDATE_CMD_FLASH 1
++
++/* Transfer source when loading into RAM */
++#define IMX500_REG_DD_LOAD_MODE CCI_REG8(0xD002)
++#define IMX500_DD_LOAD_MODE_AP 0
++#define IMX500_DD_LOAD_MODE_FLASH 1
++
++/* Image type to transfer */
++#define IMX500_REG_DD_IMAGE_TYPE CCI_REG8(0xD003)
++#define IMX500_DD_IMAGE_TYPE_LOADER_FW 0
++#define IMX500_DD_IMAGE_TYPE_MAIN_FW 1
++#define IMX500_DD_IMAGE_TYPE_NETWORK_WEIGHTS 2
++
++/* Number of divisions of download image file */
++#define IMX500_REG_DD_DOWNLOAD_DIV_NUM CCI_REG8(0xD004)
++
++#define IMX500_REG_DD_FLASH_TYPE CCI_REG8(0xD005)
++
++/* total size of download file (4-byte) */
++#define IMX500_REG_DD_DOWNLOAD_FILE_SIZE CCI_REG32(0xD008)
++
++/* Status notification (4-byte) */
++#define IMX500_REG_DD_REF_STS CCI_REG32(0xD010)
++#define IMX500_DD_REF_STS_FATAL 0xFF
++#define IMX500_DD_REF_STS_DETECT_CNT 0xFF00
++#define IMX500_DD_REF_STS_ERR_CNT 0xFF0000
++#define IMX500_DD_REF_CMD_REPLY_CNT 0xFF000000
++
++/* Command reply status */
++#define IMX500_REG_DD_CMD_REPLY_STS CCI_REG8(0xD014)
++#define IMX500_DD_CMD_REPLY_STS_TRANS_READY 0x00
++#define IMX500_DD_CMD_REPLY_STS_TRANS_DONE 0x01
++#define IMX500_DD_CMD_REPLY_STS_UPDATE_READY 0x10
++#define IMX500_DD_CMD_REPLY_STS_UPDATE_DONE 0x11
++#define IMX500_DD_CMD_REPLY_STS_UPDATE_CANCEL_DONE 0x12
++#define IMX500_DD_CMD_REPLY_STS_STATUS_ERROR 0xFF
++#define IMX500_DD_CMD_REPLY_STS_MAC_AUTH_ERROR 0xFE
++#define IMX500_DD_CMD_REPLY_STS_TIMEOUT_ERROR 0xFD
++#define IMX500_DD_CMD_REPLY_STS_PARAMETER_ERROR 0xFC
++#define IMX500_DD_CMD_REPLY_STS_INTERNAL_ERROR 0xFB
++#define IMX500_DD_CMD_REPLY_STS_PACKET_FMT_ERROR 0xFA
++
++/* Download status */
++#define IMX500_REG_DD_DOWNLOAD_STS CCI_REG8(0xD015)
++#define IMX500_DD_DOWNLOAD_STS_READY 0
++#define IMX500_DD_DOWNLOAD_STS_DOWNLOADING 1
++
++/* Update cancel */
++#define IMX500_REG_DD_UPDATE_CANCEL CCI_REG8(0xD016)
++#define IMX500_DD_UPDATE_CANCEL_NOT_CANCEL 0
++#define IMX500_DD_UPDATE_CANCEL_DO_CANCEL 1
++
++/* Notify error status */
++#define IMX500_REG_DD_ERR_STS CCI_REG8(0xD020)
++#define IMX500_DD_ERR_STS_STATUS_ERROR_BIT 0x1
++#define IMX500_DD_ERR_STS_INTERNAL_ERROR_BIT 0x2
++#define IMX500_DD_ERR_STS_PARAMETER_ERROR_BIT 0x4
++
++/* System state */
++#define IMX500_REG_DD_SYS_STATE CCI_REG8(0xD02A)
++#define IMX500_DD_SYS_STATE_STANDBY_NO_NETWORK 0
++#define IMX500_DD_SYS_STATE_STEAMING_NO_NETWORK 1
++#define IMX500_DD_SYS_STATE_STANDBY_WITH_NETWORK 2
++#define IMX500_DD_SYS_STATE_STREAMING_WITH_NETWORK 3
++
++#define IMX500_REG_MAIN_FW_VERSION CCI_REG32(0xD07C)
++
+ /* Colour balance controls */
+ #define IMX500_REG_COLOUR_BALANCE_R CCI_REG16(0xd804)
+ #define IMX500_REG_COLOUR_BALANCE_GR CCI_REG16(0xd806)
+@@ -81,6 +218,11 @@
+ #define IMX500_MAX_EMBEDDED_SIZE \
+ (2 * ((((IMX500_PIXEL_ARRAY_WIDTH * 10) >> 3) + 15) & ~15))
+
++/* Inference sizes */
++#define IMX500_INFERENCE_LINE_WIDTH 2560
++#define IMX500_NUM_KPI_LINES 1
++#define IMX500_NUM_PQ_LINES 1
++
+ /* IMX500 native and active pixel array size. */
+ #define IMX500_NATIVE_WIDTH 4072U
+ #define IMX500_NATIVE_HEIGHT 3176U
+@@ -89,7 +231,12 @@
+ #define IMX500_PIXEL_ARRAY_WIDTH 4056U
+ #define IMX500_PIXEL_ARRAY_HEIGHT 3040U
+
+-#define NUM_PADS 1
++enum pad_types { IMAGE_PAD, METADATA_PAD, NUM_PADS };
++
++#define V4L2_CID_USER_IMX500_INFERENCE_WINDOW (V4L2_CID_USER_IMX500_BASE + 0)
++#define V4L2_CID_USER_IMX500_NETWORK_FW_FD (V4L2_CID_USER_IMX500_BASE + 1)
++
++#define ONE_MIB (1024 * 1024)
+
+ /* regulator supplies */
+ static const char *const imx500_supply_name[] = {
+@@ -101,6 +248,13 @@ static const char *const imx500_supply_n
+
+ #define IMX500_NUM_SUPPLIES ARRAY_SIZE(imx500_supply_name)
+
++enum imx500_image_type {
++ TYPE_LOADER = 0,
++ TYPE_MAIN = 1,
++ TYPE_NW_WEIGHTS = 2,
++ TYPE_MAX
++};
++
+ struct imx500_reg_list {
+ unsigned int num_of_regs;
+ const struct cci_reg_sequence *regs;
+@@ -135,10 +289,19 @@ static const struct cci_reg_sequence mod
+ { CCI_REG8(0x0106), 0x01 }, /* FAST_STANDBY_CTL */
+ { CCI_REG8(0x0136), 0x1b }, /* EXCLK_FREQ */
+ { CCI_REG8(0x0137), 0x00 },
+- { CCI_REG8(0x0138), 0x01 }, /* TEMP_SENS_CTL */
+ { CCI_REG8(0x0112), 0x0a },
+ { CCI_REG8(0x0113), 0x0a },
+ { CCI_REG8(0x0114), 0x01 }, /* CSI_LANE_MODE */
++ { CCI_REG16(0x3054), IMX500_INFERENCE_LINE_WIDTH },
++ { CCI_REG16(0x3056), IMX500_INFERENCE_LINE_WIDTH },
++ { CCI_REG16(0x3058), IMX500_INFERENCE_LINE_WIDTH },
++ { CCI_REG16(0x305A), IMX500_INFERENCE_LINE_WIDTH }, /* X_OUT */
++ { CCI_REG16(0x305C), IMX500_NUM_KPI_LINES }, /* KPI Y_OUT */
++ { CCI_REG16(0x3062), IMX500_NUM_PQ_LINES }, /* PQ Y_OUT */
++ { CCI_REG8(0x3068), 0x30 },
++ { CCI_REG8(0x3069), 0x31 },
++ { CCI_REG8(0x306A), 0x32 },
++ { CCI_REG8(0x306B), 0x33 }, /* Data Types */
+ };
+
+ /* 12 mpix 15fps */
+@@ -624,6 +787,111 @@ static const struct cci_reg_sequence mod
+ { CCI_REG8(0x0215), 0x00 },
+ };
+
++static const struct cci_reg_sequence metadata_output[] = {
++ { CCI_REG8(0x3050), 1 }, /* MIPI Output enabled */
++ { CCI_REG8(0x3051), 1 }, /* MIPI output frame includes pixels data */
++ { CCI_REG8(0x3052), 1 }, /* MIPI output frame includes meta data */
++ { IMX500_REG_DD_CH06_VCID, 0 },
++ { IMX500_REG_DD_CH07_VCID, 0 },
++ { IMX500_REG_DD_CH08_VCID, 0 },
++ { IMX500_REG_DD_CH09_VCID, 0 },
++ { IMX500_REG_DD_CH06_DT,
++ 0x12 }, /* KPI - User Defined 8-bit Data Type 1 */
++ { IMX500_REG_DD_CH07_DT, 0x12 }, /* Input Tensor - U.D. 8-bit type 2 */
++ { IMX500_REG_DD_CH08_DT, 0x12 }, /* Output Tensor - U.D. 8-bit type 3 */
++ { IMX500_REG_DD_CH09_DT, 0x12 }, /* PQ - U.D. 8-bit type 4 */
++ { IMX500_REG_DD_CH06_PACKING, IMX500_DD_PACKING_8BPP },
++ { IMX500_REG_DD_CH07_PACKING, IMX500_DD_PACKING_8BPP },
++ { IMX500_REG_DD_CH08_PACKING, IMX500_DD_PACKING_8BPP },
++ { IMX500_REG_DD_CH09_PACKING, IMX500_DD_PACKING_8BPP },
++};
++
++static const struct cci_reg_sequence dnn_regs[] = {
++ { CCI_REG8(0xd960), 0x52 },
++ { CCI_REG8(0xd961), 0x52 },
++ { CCI_REG8(0xd962), 0x52 },
++ { CCI_REG8(0xd963), 0x52 },
++ { CCI_REG8(0xd96c), 0x44 },
++ { CCI_REG8(0xd96d), 0x44 },
++ { CCI_REG8(0xd96e), 0x44 },
++ { CCI_REG8(0xd96f), 0x44 },
++ { CCI_REG8(0xd600), 0x20 },
++ /* Black level */
++ { CCI_REG16(0xd80c), 0x100 },
++ { CCI_REG16(0xd80e), 0x100 },
++ { CCI_REG16(0xd810), 0x100 },
++ { CCI_REG16(0xd812), 0x100 },
++ /* Gamma */
++ { CCI_REG8(0xd814), 1 },
++ { CCI_REG32(0xd850), 0x10000 },
++ { CCI_REG32(0xd854), 0x40002 },
++ { CCI_REG32(0xd858), 0x60005 },
++ { CCI_REG32(0xd85c), 0x90008 },
++ { CCI_REG32(0xd860), 0xc000a },
++ { CCI_REG32(0xd864), 0x12000f },
++ { CCI_REG32(0xd868), 0x1c0014 },
++ { CCI_REG32(0xd86c), 0x2a0024 },
++ { CCI_REG32(0xd870), 0x360030 },
++ { CCI_REG32(0xd874), 0x46003c },
++ { CCI_REG32(0xd878), 0x5a0051 },
++ { CCI_REG32(0xd87c), 0x750064 },
++ { CCI_REG32(0xd880), 0x920084 },
++ { CCI_REG32(0xd884), 0xa9009e },
++ { CCI_REG32(0xd888), 0xba00b2 },
++ { CCI_REG32(0xd88c), 0xc700c1 },
++ { CCI_REG32(0xd890), 0xd100cd },
++ { CCI_REG32(0xd894), 0xde00d6 },
++ { CCI_REG32(0xd898), 0xe900e4 },
++ { CCI_REG32(0xd89c), 0xf300ee },
++ { CCI_REG32(0xd8a0), 0xfb00f7 },
++ { CCI_REG16(0xd8a4), 0xff },
++ { CCI_REG32(0xd8a8), 0x10000 },
++ { CCI_REG32(0xd8ac), 0x40002 },
++ { CCI_REG32(0xd8b0), 0x60005 },
++ { CCI_REG32(0xd8b4), 0x90008 },
++ { CCI_REG32(0xd8b8), 0xc000a },
++ { CCI_REG32(0xd8bc), 0x12000f },
++ { CCI_REG32(0xd8c0), 0x1c0014 },
++ { CCI_REG32(0xd8c4), 0x2a0024 },
++ { CCI_REG32(0xd8c8), 0x360030 },
++ { CCI_REG32(0xd8cc), 0x46003c },
++ { CCI_REG32(0xd8d0), 0x5a0051 },
++ { CCI_REG32(0xd8d4), 0x750064 },
++ { CCI_REG32(0xd8d8), 0x920084 },
++ { CCI_REG32(0xd8dc), 0xa9009e },
++ { CCI_REG32(0xd8e0), 0xba00b2 },
++ { CCI_REG32(0xd8e4), 0xc700c1 },
++ { CCI_REG32(0xd8e8), 0xd100cd },
++ { CCI_REG32(0xd8ec), 0xde00d6 },
++ { CCI_REG32(0xd8f0), 0xe900e4 },
++ { CCI_REG32(0xd8f4), 0xf300ee },
++ { CCI_REG32(0xd8f8), 0xfb00f7 },
++ { CCI_REG16(0xd8fc), 0xff },
++ { CCI_REG32(0xd900), 0x10000 },
++ { CCI_REG32(0xd904), 0x40002 },
++ { CCI_REG32(0xd908), 0x60005 },
++ { CCI_REG32(0xd90c), 0x90008 },
++ { CCI_REG32(0xd910), 0xc000a },
++ { CCI_REG32(0xd914), 0x12000f },
++ { CCI_REG32(0xd918), 0x1c0014 },
++ { CCI_REG32(0xd91c), 0x2a0024 },
++ { CCI_REG32(0xd920), 0x360030 },
++ { CCI_REG32(0xd924), 0x46003c },
++ { CCI_REG32(0xd928), 0x5a0051 },
++ { CCI_REG32(0xd92c), 0x750064 },
++ { CCI_REG32(0xd930), 0x920084 },
++ { CCI_REG32(0xd934), 0xa9009e },
++ { CCI_REG32(0xd938), 0xba00b2 },
++ { CCI_REG32(0xd93c), 0xc700c1 },
++ { CCI_REG32(0xd940), 0xd100cd },
++ { CCI_REG32(0xd944), 0xde00d6 },
++ { CCI_REG32(0xd948), 0xe900e4 },
++ { CCI_REG32(0xd94c), 0xf300ee },
++ { CCI_REG32(0xd950), 0xfb00f7 },
++ { CCI_REG16(0xd954), 0xff },
++ { CCI_REG8(0xd826), 1 },
++};
++
+ /* Mode configs */
+ static const struct imx500_mode imx500_supported_modes[] = {
+ {
+@@ -679,9 +947,17 @@ static const u32 codes[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ };
+
++enum imx500_state {
++ IMX500_STATE_RESET = 0,
++ IMX500_STATE_PROGRAM_EMPTY,
++ IMX500_STATE_WITHOUT_NETWORK,
++ IMX500_STATE_WITH_NETWORK,
++};
++
+ struct imx500 {
++ struct dentry *debugfs;
+ struct v4l2_subdev sd;
+- struct media_pad pad;
++ struct media_pad pad[NUM_PADS];
+ struct regmap *regmap;
+
+ unsigned int fmt_code;
+@@ -701,6 +977,9 @@ struct imx500 {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
++ struct v4l2_ctrl *network_fw_ctrl;
++
++ struct v4l2_rect inference_window;
+
+ /* Current mode */
+ const struct imx500_mode *mode;
+@@ -717,8 +996,24 @@ struct imx500 {
+ /* Rewrite common registers on stream on? */
+ bool common_regs_written;
+
++ bool loader_and_main_written;
++ bool network_written;
++
+ /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */
+ unsigned int long_exp_shift;
++
++ struct spi_device *spi_device;
++
++ const struct firmware *fw_loader;
++ const struct firmware *fw_main;
++ const u8 *fw_network;
++ size_t fw_network_size;
++ size_t fw_progress;
++ unsigned int fw_stage;
++
++ enum imx500_state fsm_state;
++
++ u32 num_inference_lines;
+ };
+
+ static inline struct imx500 *to_imx500(struct v4l2_subdev *_sd)
+@@ -726,6 +1021,188 @@ static inline struct imx500 *to_imx500(s
+ return container_of(_sd, struct imx500, sd);
+ }
+
++static bool validate_normalization_yuv(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ /* Some regs are 9-bit, some 8-bit, some 1-bit */
++ switch (reg) {
++ case 0xD62A:
++ case 0xD632:
++ case 0xD63A:
++ case 0xD644:
++ case 0xD648:
++ case 0xD64C:
++ case 0xD650:
++ case 0xD654:
++ case 0xD658:
++ return size == 2 && !(value & ~0x1FF);
++ case 0xD600:
++ case 0xD601:
++ case 0xD602:
++ return size == 1 && !(value & ~0xFF);
++ case 0xD629:
++ case 0xD630:
++ case 0xD638:
++ case 0xD643:
++ case 0xD647:
++ case 0xD64B:
++ case 0xD64F:
++ case 0xD653:
++ case 0xD657:
++ return size == 1 && !(value & ~0x01);
++ default:
++ return false;
++ }
++}
++
++/* Common function as bayer rgb + normalization use the same repeating register
++ * layout
++ */
++static bool validate_bit_pattern(u8 offset, uint8_t size, uint32_t value)
++{
++ /* There are no odd register addresses */
++ if (offset & 1)
++ return false;
++
++ /* Valid register sizes/patterns repeat every 4 */
++ offset = (offset >> 1) & 3;
++
++ if (offset == 1)
++ return size == 1 && !(value & ~1);
++ else
++ return size == 2 && !(value & ~0x1FF);
++}
++
++static bool validate_bayer_rgb_normalization(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ if (reg < 0xD684 || reg >= 0xD6E4)
++ return false;
++ return validate_bit_pattern(reg - 0xD684, size, value);
++}
++
++static bool validate_normalization_registers(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ if (reg < 0xD708 || reg >= 0xD750)
++ return false;
++ return validate_bit_pattern(reg - 0xD708, size, value);
++}
++
++static bool validate_image_format_selection(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ if (size != 1 || value > 5)
++ return false;
++ if (reg < 0xD750 || reg > 0xd752)
++ return false;
++ return true;
++}
++
++static bool validate_yc_conversion_factor(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ static const u32 allowed[9] = {
++ 0x0FFF0FFF, 0x0FFF1FFF, 0x0FFF0FFF, 0x0FFF1FFF, 0x0FFF0FFF,
++ 0x0FFF1FFF, 0x01FF01FF, 0x01FF01FF, 0x01FF01FF,
++ };
++
++ if (size > 4 || size & 1 || reg & 1 || reg < 0x76C || reg > 0xD7FA)
++ return false;
++
++ if (size == 2) {
++ if (reg & 2)
++ reg -= 2;
++ else
++ value <<= 16;
++ }
++
++ /* High registers (clip values) are all 2x 9-bit */
++ if (reg >= 0xD7D8)
++ return !(value & ~0x01FF01FF);
++
++ /* Early registers follow a repeating pattern */
++ reg -= 0xD76C;
++ reg >>= 2;
++ return !(value & ~allowed[reg % sizeof(allowed)]);
++}
++
++static bool validate_dnn_output_setting(u16 reg, uint8_t size,
++ uint32_t value)
++{
++ /* Only Y_OUT_SIZE for Input Tensor / Output Tensor is configurable from
++ * userspace
++ */
++ return (size == 2) && (value < 2046) &&
++ ((reg == CCI_REG_ADDR(IMX500_REG_DD_CH07_Y_OUT_SIZE)) ||
++ (reg == CCI_REG_ADDR(IMX500_REG_DD_CH08_Y_OUT_SIZE)));
++}
++
++static bool __must_check
++imx500_validate_inference_register(const struct cci_reg_sequence *reg)
++{
++ unsigned int i;
++
++ static bool (*const checks[])(uint16_t, uint8_t, uint32_t) = {
++ validate_normalization_yuv,
++ validate_bayer_rgb_normalization,
++ validate_normalization_registers,
++ validate_image_format_selection,
++ validate_yc_conversion_factor,
++ validate_dnn_output_setting,
++ };
++
++ if (!reg)
++ return false;
++
++ for (i = 0; i < ARRAY_SIZE(checks); i++) {
++ if (checks[i](CCI_REG_ADDR(reg->reg),
++ CCI_REG_WIDTH_BYTES(reg->reg), reg->val))
++ return true;
++ }
++
++ return false;
++}
++
++static int imx500_set_inference_window(struct imx500 *imx500)
++{
++ u16 left, top, width, height;
++
++ if (!imx500->inference_window.width ||
++ !imx500->inference_window.height) {
++ width = 4056;
++ height = 3040;
++ left = 0;
++ top = 0;
++ } else {
++ width = min_t(u16, imx500->inference_window.width, 4056);
++ height = min_t(u16, imx500->inference_window.height, 3040);
++ left = min_t(u16, imx500->inference_window.left, 4056);
++ top = min_t(u16, imx500->inference_window.top, 3040);
++ }
++
++ const struct cci_reg_sequence window_regs[] = {
++ { IMX500_REG_DWP_AP_VC_HOFF, left },
++ { IMX500_REG_DWP_AP_VC_VOFF, top },
++ { IMX500_REG_DWP_AP_VC_HSIZE, width },
++ { IMX500_REG_DWP_AP_VC_VSIZE, height },
++ };
++
++ return cci_multi_reg_write(imx500->regmap, window_regs,
++ ARRAY_SIZE(window_regs), NULL);
++}
++
++static int imx500_reg_val_write_cbk(void *arg,
++ const struct cci_reg_sequence *reg)
++{
++ struct imx500 *imx500 = arg;
++
++ if (!imx500_validate_inference_register(reg))
++ return -EINVAL;
++
++ return cci_write(imx500->regmap, reg->reg, reg->val, NULL);
++}
++
+ /* Get bayer order based on flip setting. */
+ static u32 imx500_get_format_code(struct imx500 *imx500)
+ {
+@@ -745,6 +1222,144 @@ static void imx500_set_default_format(st
+ imx500->fmt_code = MEDIA_BUS_FMT_SRGGB10_1X10;
+ }
+
++/* -1 on fail, block size on success */
++static int imx500_validate_fw_block(const char *data, size_t maxlen)
++{
++ const size_t header_size = 32;
++ static const char header_id[] = { '9', '4', '6', '4' };
++
++ const size_t footer_size = 64;
++ static const char footer_id[] = { '3', '6', '9', '5' };
++
++ u32 data_size;
++
++ const char *end = data + maxlen;
++
++ if (!data)
++ return -1;
++
++ if (maxlen < header_size)
++ return -1;
++
++ if (memcmp(data, &header_id, sizeof(header_id)))
++ return -1;
++
++ /* data_size is size of header + body */
++ memcpy(&data_size, data + sizeof(header_id), sizeof(data_size));
++ data_size = ___constant_swab32(data_size);
++
++ if (end - data_size - footer_size < data)
++ return -1;
++ if (memcmp(data + data_size + footer_size - sizeof(footer_id),
++ &footer_id, sizeof(footer_id)))
++ return -1;
++
++ return data_size + footer_size;
++}
++
++/* Parse fw block by block, returning total valid fw size */
++static size_t imx500_valid_fw_bytes(const u8 *fw,
++ const size_t fw_size)
++{
++ int i;
++ size_t bytes = 0;
++
++ const u8 *data = fw;
++ size_t size = fw_size;
++
++ while ((i = imx500_validate_fw_block(data, size)) > 0) {
++ bytes += i;
++ data += i;
++ size -= i;
++ }
++
++ return bytes;
++}
++
++static int imx500_iterate_nw_regs(
++ const u8 *fw, size_t fw_size, void *arg,
++ int (*cbk)(void *arg, const struct cci_reg_sequence *reg))
++{
++ struct cpio_data cd = { NULL, 0, "" };
++ const u8 *read_pos;
++ size_t entries;
++ size_t size;
++
++ if (!fw || !cbk)
++ return -EINVAL;
++
++ size = imx500_valid_fw_bytes(fw, fw_size);
++ cd = find_cpio_data("imx500_regs", (void *)(fw + size),
++ fw_size - size, NULL);
++ if (!cd.data || cd.size % 7)
++ return -EINVAL;
++
++ read_pos = cd.data;
++ entries = cd.size / 7;
++
++ while (entries--) {
++ struct cci_reg_sequence reg = { 0, 0 };
++ u16 addr;
++ u8 len;
++ u32 val;
++ int ret;
++
++ memcpy(&addr, read_pos, sizeof(addr));
++ read_pos += sizeof(addr);
++ memcpy(&len, read_pos, sizeof(len));
++ read_pos += sizeof(len);
++ memcpy(&val, read_pos, sizeof(val));
++ read_pos += sizeof(val);
++
++ reg.reg = ((len << CCI_REG_WIDTH_SHIFT) | addr);
++ reg.val = val;
++
++ ret = cbk(arg, ®);
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++
++static int imx500_reg_tensor_lines_cbk(void *arg,
++ const struct cci_reg_sequence *reg)
++{
++ u16 *tensor_lines = arg;
++
++ if (reg->val < 2046) {
++ switch (reg->reg) {
++ case IMX500_REG_DD_CH07_Y_OUT_SIZE:
++ tensor_lines[0] = reg->val;
++ break;
++ case IMX500_REG_DD_CH08_Y_OUT_SIZE:
++ tensor_lines[1] = reg->val;
++ break;
++ }
++ }
++
++ return 0;
++}
++
++static void imx500_calc_inference_lines(struct imx500 *imx500)
++{
++ u16 tensor_lines[2] = { 0, 0 };
++
++ if (!imx500->fw_network) {
++ imx500->num_inference_lines = 0;
++ return;
++ }
++
++ imx500_iterate_nw_regs(imx500->fw_network, imx500->fw_network_size,
++ tensor_lines, imx500_reg_tensor_lines_cbk);
++
++ /* Full-res mode, embedded lines are actually slightly shorter than inference
++ * lines 2544 vs 2560 (over-allocate with inf. width)
++ */
++ imx500->num_inference_lines = IMX500_NUM_KPI_LINES +
++ IMX500_NUM_PQ_LINES + tensor_lines[0] +
++ tensor_lines[1];
++}
++
+ static void imx500_adjust_exposure_range(struct imx500 *imx500)
+ {
+ int exposure_max, exposure_def;
+@@ -777,6 +1392,99 @@ static int imx500_set_frame_length(struc
+ imx500->long_exp_shift, NULL);
+ }
+
++/* reg is both input and output:
++ * reg->val is the value we're polling until we're NEQ to
++ * It is then populated with the updated value.
++ */
++static int __must_check imx500_poll_status_reg(struct imx500 *state,
++ struct cci_reg_sequence *reg,
++ u8 timeout)
++{
++ u64 read_value;
++ int ret;
++
++ while (timeout) {
++ ret = cci_read(state->regmap, reg->reg, &read_value, NULL);
++ if (ret)
++ return ret;
++
++ if (read_value != reg->val) {
++ reg->val = read_value;
++ return 0;
++ }
++
++ timeout--;
++ mdelay(50);
++ }
++ return -EAGAIN;
++}
++
++static int imx500_prepare_poll_cmd_reply_sts(struct imx500 *imx500,
++ struct cci_reg_sequence *cmd_reply)
++{
++ /* Perform single-byte read of 4-byte IMX500_REG_DD_REF_STS register to
++ * target CMD_REPLY_STS_CNT sub-register
++ */
++ cmd_reply->reg = CCI_REG8(CCI_REG_ADDR(IMX500_REG_DD_REF_STS));
++
++ return cci_read(imx500->regmap, cmd_reply->reg, &cmd_reply->val, NULL);
++}
++
++static int imx500_clear_weights(struct imx500 *imx500)
++{
++ struct cci_reg_sequence cmd_reply_sts_cnt_reg;
++ u64 imx500_fsm_state;
++ u64 cmd_reply;
++ int ret;
++
++ static const struct cci_reg_sequence request_clear[] = {
++ { IMX500_REG_DD_ST_TRANS_CMD,
++ IMX500_DD_ST_TRANS_CMD_CLEAR_WEIGHTS },
++ { IMX500_REG_DD_CMD_INT, IMX500_DD_CMD_INT_ST_TRANS },
++ };
++
++ if (imx500->fsm_state != IMX500_STATE_WITH_NETWORK)
++ return -EINVAL;
++
++ ret = cci_read(imx500->regmap, IMX500_REG_DD_SYS_STATE,
++ &imx500_fsm_state, NULL);
++ if (ret || imx500_fsm_state != IMX500_DD_SYS_STATE_STANDBY_WITH_NETWORK)
++ return ret ? ret : -EREMOTEIO;
++
++ ret = imx500_prepare_poll_cmd_reply_sts(imx500, &cmd_reply_sts_cnt_reg);
++ if (ret)
++ return ret;
++
++ ret = cci_multi_reg_write(imx500->regmap, request_clear,
++ ARRAY_SIZE(request_clear), NULL);
++ if (ret)
++ return ret;
++
++ ret = imx500_poll_status_reg(imx500, &cmd_reply_sts_cnt_reg, 5);
++ if (ret)
++ return ret;
++
++ ret = cci_read(imx500->regmap, IMX500_REG_DD_CMD_REPLY_STS, &cmd_reply,
++ NULL);
++ if (ret || cmd_reply != IMX500_DD_CMD_REPLY_STS_TRANS_DONE)
++ return ret ? ret : -EREMOTEIO;
++
++ imx500->fsm_state = IMX500_STATE_WITHOUT_NETWORK;
++ imx500->network_written = false;
++ return 0;
++}
++
++static void imx500_clear_fw_network(struct imx500 *imx500)
++{
++ /* Remove any previous firmware blob. */
++ if (imx500->fw_network)
++ vfree(imx500->fw_network);
++
++ imx500->fw_network = NULL;
++ imx500->network_written = false;
++ imx500->fw_progress = 0;
++}
++
+ static int imx500_set_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct imx500 *imx500 =
+@@ -784,6 +1492,53 @@ static int imx500_set_ctrl(struct v4l2_c
+ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
+ int ret = 0;
+
++ if (ctrl->id == V4L2_CID_USER_IMX500_NETWORK_FW_FD) {
++ /* Reset state of the control. */
++ if (ctrl->val < 0) {
++ return 0;
++ } else if (ctrl->val == S32_MAX) {
++ ctrl->val = -1;
++ if (pm_runtime_get_if_in_use(&client->dev) == 0)
++ return 0;
++
++ if (imx500->network_written)
++ ret = imx500_clear_weights(imx500);
++ imx500_clear_fw_network(imx500);
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++
++ return ret;
++ }
++
++ imx500_clear_fw_network(imx500);
++ ret = kernel_read_file_from_fd(ctrl->val, 0,
++ (void **)&imx500->fw_network, INT_MAX,
++ &imx500->fw_network_size,
++ 1);
++ /*
++ * Back to reset state, the FD cannot be considered valid after
++ * this IOCTL completes.
++ */
++ ctrl->val = -1;
++
++ if (ret < 0) {
++ dev_err(&client->dev, "%s failed to read fw image: %d\n",
++ __func__, ret);
++ imx500_clear_fw_network(imx500);
++ return ret;
++ }
++ if (ret != imx500->fw_network_size) {
++ dev_err(&client->dev, "%s read fw image size mismatich: got %u, expected %zu\n",
++ __func__, ret, imx500->fw_network_size);
++ imx500_clear_fw_network(imx500);
++ return -EIO;
++ }
++
++ imx500_calc_inference_lines(imx500);
++ return 0;
++ }
++
+ /*
+ * The VBLANK control may change the limits of usable exposure, so check
+ * and adjust if necessary.
+@@ -831,6 +1586,11 @@ static int imx500_set_ctrl(struct v4l2_c
+ cci_write(imx500->regmap, IMX500_REG_COLOUR_BALANCE_R,
+ ctrl->p_new.p_u32[3], &ret);
+ break;
++ case V4L2_CID_USER_IMX500_INFERENCE_WINDOW:
++ memcpy(&imx500->inference_window, ctrl->p_new.p_u32,
++ sizeof(struct v4l2_rect));
++ ret = imx500_set_inference_window(imx500);
++ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id,
+@@ -870,10 +1630,17 @@ static int imx500_enum_mbus_code(struct
+ if (code->pad >= NUM_PADS)
+ return -EINVAL;
+
+- if (code->index != 0)
+- return -EINVAL;
++ if (code->pad == IMAGE_PAD) {
++ if (code->index != 0)
++ return -EINVAL;
+
+- code->code = imx500_get_format_code(imx500);
++ code->code = imx500_get_format_code(imx500);
++ } else {
++ if (code->index > 0)
++ return -EINVAL;
++
++ code->code = MEDIA_BUS_FMT_SENSOR_DATA;
++ }
+
+ return 0;
+ }
+@@ -887,19 +1654,31 @@ static int imx500_enum_frame_size(struct
+ if (fse->pad >= NUM_PADS)
+ return -EINVAL;
+
+- const struct imx500_mode *mode_list = imx500_supported_modes;
+- unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
+-
+- if (fse->index >= num_modes)
+- return -EINVAL;
+-
+- if (fse->code != imx500_get_format_code(imx500))
+- return -EINVAL;
++ if (fse->pad == IMAGE_PAD) {
++ const struct imx500_mode *mode_list = imx500_supported_modes;
++ unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
++
++ if (fse->index >= num_modes)
++ return -EINVAL;
++
++ if (fse->code != imx500_get_format_code(imx500))
++ return -EINVAL;
++
++ fse->min_width = mode_list[fse->index].width;
++ fse->max_width = fse->min_width;
++ fse->min_height = mode_list[fse->index].height;
++ fse->max_height = fse->min_height;
++ } else {
++ if (fse->code != MEDIA_BUS_FMT_SENSOR_DATA || fse->index > 0)
++ return -EINVAL;
+
+- fse->min_width = mode_list[fse->index].width;
+- fse->max_width = fse->min_width;
+- fse->min_height = mode_list[fse->index].height;
+- fse->max_height = fse->min_height;
++ fse->min_width = IMX500_MAX_EMBEDDED_SIZE +
++ imx500->num_inference_lines *
++ IMX500_INFERENCE_LINE_WIDTH;
++ fse->max_width = fse->min_width;
++ fse->min_height = 1;
++ fse->max_height = fse->min_height;
++ }
+
+ return 0;
+ }
+@@ -920,6 +1699,17 @@ static void imx500_update_image_pad_form
+ V4L2_MAP_XFER_FUNC_DEFAULT(fmt->format.colorspace);
+ }
+
++static void imx500_update_metadata_pad_format(const struct imx500 *imx500,
++ struct v4l2_subdev_format *fmt)
++{
++ fmt->format.width =
++ IMX500_MAX_EMBEDDED_SIZE +
++ imx500->num_inference_lines * IMX500_INFERENCE_LINE_WIDTH;
++ fmt->format.height = 1;
++ fmt->format.code = MEDIA_BUS_FMT_SENSOR_DATA;
++ fmt->format.field = V4L2_FIELD_NONE;
++}
++
+ static int imx500_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+@@ -935,11 +1725,18 @@ static int imx500_get_pad_format(struct
+ struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(
+ &imx500->sd, sd_state, fmt->pad);
+ /* update the code which could change due to vflip or hflip */
+- try_fmt->code = imx500_get_format_code(imx500);
++ try_fmt->code = fmt->pad == IMAGE_PAD ?
++ imx500_get_format_code(imx500) :
++ MEDIA_BUS_FMT_SENSOR_DATA;
+ fmt->format = *try_fmt;
+ } else {
+- imx500_update_image_pad_format(imx500, imx500->mode, fmt);
+- fmt->format.code = imx500_get_format_code(imx500);
++ if (fmt->pad == IMAGE_PAD) {
++ imx500_update_image_pad_format(imx500, imx500->mode,
++ fmt);
++ fmt->format.code = imx500_get_format_code(imx500);
++ } else {
++ imx500_update_metadata_pad_format(imx500, fmt);
++ }
+ }
+
+ mutex_unlock(&imx500->mutex);
+@@ -1000,22 +1797,35 @@ static int imx500_set_pad_format(struct
+
+ mutex_lock(&imx500->mutex);
+
+- const struct imx500_mode *mode_list = imx500_supported_modes;
+- unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
++ if (fmt->pad == IMAGE_PAD) {
++ const struct imx500_mode *mode_list = imx500_supported_modes;
++ unsigned int num_modes = ARRAY_SIZE(imx500_supported_modes);
+
+- /* Bayer order varies with flips */
+- fmt->format.code = imx500_get_format_code(imx500);
++ /* Bayer order varies with flips */
++ fmt->format.code = imx500_get_format_code(imx500);
+
+- mode = v4l2_find_nearest_size(mode_list, num_modes, width, height,
+- fmt->format.width, fmt->format.height);
+- imx500_update_image_pad_format(imx500, mode, fmt);
+- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+- framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad);
+- *framefmt = fmt->format;
+- } else if (imx500->mode != mode) {
+- imx500->mode = mode;
+- imx500->fmt_code = fmt->format.code;
+- imx500_set_framing_limits(imx500);
++ mode = v4l2_find_nearest_size(mode_list, num_modes, width,
++ height, fmt->format.width,
++ fmt->format.height);
++ imx500_update_image_pad_format(imx500, mode, fmt);
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, sd_state,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else if (imx500->mode != mode) {
++ imx500->mode = mode;
++ imx500->fmt_code = fmt->format.code;
++ imx500_set_framing_limits(imx500);
++ }
++ } else {
++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++ framefmt = v4l2_subdev_get_try_format(sd, sd_state,
++ fmt->pad);
++ *framefmt = fmt->format;
++ } else {
++ /* Only one embedded data mode is supported */
++ imx500_update_metadata_pad_format(imx500, fmt);
++ }
+ }
+
+ mutex_unlock(&imx500->mutex);
+@@ -1074,6 +1884,243 @@ static int imx500_get_selection(struct v
+ return -EINVAL;
+ }
+
++static int __must_check imx500_spi_write(struct imx500 *state, const u8 *data,
++ size_t size)
++{
++ if (size % 4 || size > ONE_MIB)
++ return -EINVAL;
++
++ if (!state->spi_device)
++ return -ENODEV;
++
++ return spi_write(state->spi_device, data, size);
++}
++
++/* Moves the IMX500 internal state machine between states or updates.
++ *
++ * Prerequisites: Sensor is powered on and not currently streaming
++ */
++static int imx500_state_transition(struct imx500 *imx500, const u8 *fw,
++ size_t fw_size, enum imx500_image_type type,
++ bool update)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ struct cci_reg_sequence cmd_reply_sts_cnt_reg;
++ size_t valid_size;
++ int ret;
++ u64 tmp;
++
++ if (!imx500 || !fw || type >= TYPE_MAX)
++ return -EINVAL;
++
++ if (!update && (int)type != (int)imx500->fsm_state)
++ return -EINVAL;
++
++ /* Validate firmware */
++ valid_size = imx500_valid_fw_bytes(fw, fw_size);
++ if (!valid_size)
++ return -EINVAL;
++
++ ret = imx500_prepare_poll_cmd_reply_sts(imx500, &cmd_reply_sts_cnt_reg);
++ if (ret)
++ return ret;
++
++ struct cci_reg_sequence common_regs[] = {
++ { IMX500_REG_DD_FLASH_TYPE, 0x02 },
++ { IMX500_REG_DD_LOAD_MODE, IMX500_DD_LOAD_MODE_AP },
++ { IMX500_REG_DD_IMAGE_TYPE, type },
++ { IMX500_REG_DD_DOWNLOAD_DIV_NUM, (valid_size - 1) / ONE_MIB },
++ { IMX500_REG_DD_DOWNLOAD_FILE_SIZE, valid_size },
++ };
++
++ struct cci_reg_sequence state_transition_regs[] = {
++ { IMX500_REG_DD_ST_TRANS_CMD, type },
++ { IMX500_REG_DD_CMD_INT, IMX500_DD_CMD_INT_ST_TRANS },
++ };
++
++ struct cci_reg_sequence update_regs[] = {
++ { IMX500_REG_DD_UPDATE_CMD, IMX500_DD_UPDATE_CMD_SRAM },
++ { IMX500_REG_DD_CMD_INT, IMX500_DD_CMD_INT_UPDATE },
++ };
++
++ ret = cci_multi_reg_write(imx500->regmap, common_regs,
++ ARRAY_SIZE(common_regs), NULL);
++
++ cci_multi_reg_write(imx500->regmap,
++ update ? update_regs : state_transition_regs, 2,
++ &ret);
++ if (ret)
++ return ret;
++
++ /* Poll CMD_REPLY_STS_CNT until a response is available */
++ ret = imx500_poll_status_reg(imx500, &cmd_reply_sts_cnt_reg, 5);
++ if (ret) {
++ dev_err(&client->dev, "DD_REF_STS register did not update\n");
++ return ret;
++ }
++
++ /* Read response to state transition / update request */
++ ret = cci_read(imx500->regmap, IMX500_REG_DD_CMD_REPLY_STS, &tmp, NULL);
++ if (ret || tmp != (update ? IMX500_DD_CMD_REPLY_STS_UPDATE_READY :
++ IMX500_DD_CMD_REPLY_STS_TRANS_READY))
++ return ret ? ret : -EBUSY;
++
++ imx500->fw_stage = type;
++ imx500->fw_progress = 0;
++
++ for (size_t i = 0; i <= valid_size / ONE_MIB; i++) {
++ const u8 *data = fw + (i * ONE_MIB);
++ size_t size = valid_size - (i * ONE_MIB);
++ struct cci_reg_sequence download_sts_reg = {
++ IMX500_REG_DD_DOWNLOAD_STS,
++ IMX500_DD_DOWNLOAD_STS_DOWNLOADING,
++ };
++
++ /* Calculate SPI xfer size avoiding 0-sized TXNs */
++ size = min_t(size_t, size, ONE_MIB);
++ if (!size)
++ break;
++
++ /* Poll until device is ready for download */
++ ret = imx500_poll_status_reg(imx500, &download_sts_reg, 100);
++ if (ret) {
++ dev_err(&client->dev,
++ "DD_DOWNLOAD_STS was never ready\n");
++ return ret;
++ }
++
++ /* Do SPI transfer */
++ ret = imx500_spi_write(imx500, data, size);
++ imx500->fw_progress += size;
++
++ if (ret < 0)
++ return ret;
++ }
++
++ /* Poll until another response is available */
++ ret = imx500_poll_status_reg(imx500, &cmd_reply_sts_cnt_reg, 5);
++ if (ret) {
++ dev_err(&client->dev,
++ "DD_REF_STS register did not update after SPI write(s)\n");
++ return ret;
++ }
++
++ /* Verify that state transition / update completed successfully */
++ ret = cci_read(imx500->regmap, IMX500_REG_DD_CMD_REPLY_STS, &tmp, NULL);
++ if (ret || tmp != (update ? IMX500_DD_CMD_REPLY_STS_UPDATE_DONE :
++ IMX500_DD_CMD_REPLY_STS_TRANS_DONE))
++ return ret ? ret : -EREMOTEIO;
++
++ if (!update && imx500->fsm_state < IMX500_STATE_WITH_NETWORK)
++ imx500->fsm_state++;
++
++ imx500->fw_progress = fw_size;
++
++ return 0;
++}
++
++static int imx500_transition_to_standby_wo_network(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ const struct firmware *firmware;
++ u64 fw_ver;
++ int ret;
++
++ firmware = imx500->fw_loader;
++ ret = imx500_state_transition(imx500, firmware->data, firmware->size,
++ TYPE_LOADER, false);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to load loader firmware\n",
++ __func__);
++ return ret;
++ }
++
++ firmware = imx500->fw_main;
++ ret = imx500_state_transition(imx500, firmware->data, firmware->size,
++ TYPE_MAIN, false);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to load main firmware\n",
++ __func__);
++ return ret;
++ }
++
++ ret = cci_read(imx500->regmap, IMX500_REG_MAIN_FW_VERSION, &fw_ver,
++ NULL);
++ if (ret) {
++ dev_err(&client->dev,
++ "%s: could not read main firmware version\n", __func__);
++ return ret;
++ }
++
++ dev_info(&client->dev,
++ "main firmware version: %llu%llu.%llu%llu.%llu%llu\n",
++ (fw_ver >> 20) & 0xF, (fw_ver >> 16) & 0xF,
++ (fw_ver >> 12) & 0xF, (fw_ver >> 8) & 0xF, (fw_ver >> 4) & 0xF,
++ fw_ver & 0xF);
++
++ ret = cci_multi_reg_write(imx500->regmap, metadata_output,
++ ARRAY_SIZE(metadata_output), NULL);
++ if (ret) {
++ dev_err(&client->dev,
++ "%s: failed to configure MIPI output for DNN\n",
++ __func__);
++ return ret;
++ }
++
++ ret = cci_multi_reg_write(imx500->regmap, dnn_regs,
++ ARRAY_SIZE(dnn_regs), NULL);
++ if (ret) {
++ dev_err(&client->dev, "%s: unable to write DNN regs\n",
++ __func__);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int imx500_transition_to_network(struct imx500 *imx500)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&imx500->sd);
++ u64 imx500_fsm_state;
++ int ret;
++
++ ret = imx500_iterate_nw_regs(imx500->fw_network,
++ imx500->fw_network_size, imx500,
++ imx500_reg_val_write_cbk);
++ if (ret) {
++ dev_err(&client->dev,
++ "%s: unable to apply register writes from firmware\n",
++ __func__);
++ return ret;
++ }
++
++ /* Read IMX500 state to determine whether transition or update is required */
++ ret = cci_read(imx500->regmap, IMX500_REG_DD_SYS_STATE,
++ &imx500_fsm_state, NULL);
++ if (ret || imx500_fsm_state & 1)
++ return ret ? ret : -EREMOTEIO;
++
++ ret = imx500_state_transition(
++ imx500, imx500->fw_network, imx500->fw_network_size,
++ TYPE_NW_WEIGHTS,
++ imx500_fsm_state == IMX500_DD_SYS_STATE_STANDBY_WITH_NETWORK);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to load network weights\n",
++ __func__);
++ return ret;
++ }
++
++ /* Select network 0 */
++ ret = cci_write(imx500->regmap, CCI_REG8(0xD701), 0, NULL);
++ if (ret) {
++ dev_err(&client->dev, "%s: failed to select network 0\n",
++ __func__);
++ return ret;
++ }
++
++ return ret;
++}
++
+ /* Start streaming */
+ static int imx500_start_streaming(struct imx500 *imx500)
+ {
+@@ -1086,7 +2133,8 @@ static int imx500_start_streaming(struct
+ return ret;
+
+ ret = cci_write(imx500->regmap, IMX500_REG_IMAGE_ONLY_MODE,
+- IMX500_IMAGE_ONLY_TRUE,
++ imx500->fw_network ? IMX500_IMAGE_ONLY_FALSE :
++ IMX500_IMAGE_ONLY_TRUE,
+ NULL);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set image mode\n",
+@@ -1094,6 +2142,30 @@ static int imx500_start_streaming(struct
+ return ret;
+ }
+
++ /* Acquire loader and main firmware if needed */
++ if (imx500->fw_network) {
++ if (!imx500->fw_loader) {
++ ret = request_firmware(&imx500->fw_loader,
++ "imx500_loader.fpk",
++ &client->dev);
++ if (ret) {
++ dev_err(&client->dev,
++ "Unable to acquire firmware loader\n");
++ return ret;
++ }
++ }
++ if (!imx500->fw_main) {
++ ret = request_firmware(&imx500->fw_main,
++ "imx500_firmware.fpk",
++ &client->dev);
++ if (ret) {
++ dev_err(&client->dev,
++ "Unable to acquire main firmware\n");
++ return ret;
++ }
++ }
++ }
++
+ if (!imx500->common_regs_written) {
+ ret = cci_multi_reg_write(imx500->regmap, mode_common_regs,
+ ARRAY_SIZE(mode_common_regs), NULL);
+@@ -1107,6 +2179,38 @@ static int imx500_start_streaming(struct
+ imx500->common_regs_written = true;
+ }
+
++ if (imx500->fw_network && !imx500->loader_and_main_written) {
++ ret = imx500_transition_to_standby_wo_network(imx500);
++ if (ret) {
++ dev_err(&client->dev,
++ "%s failed to transition from program empty state\n",
++ __func__);
++ return ret;
++ }
++ imx500->loader_and_main_written = true;
++ }
++
++ if (imx500->fw_network && !imx500->network_written) {
++ ret = imx500_transition_to_network(imx500);
++ if (ret) {
++ dev_err(&client->dev,
++ "%s failed to transition to network loaded\n",
++ __func__);
++ return ret;
++ }
++ imx500->network_written = true;
++ }
++
++ /* Enable DNN */
++ if (imx500->fw_network) {
++ ret = cci_write(imx500->regmap, CCI_REG8(0xD100), 4, NULL);
++ if (ret) {
++ dev_err(&client->dev, "%s failed to enable DNN\n",
++ __func__);
++ return ret;
++ }
++ }
++
+ /* Apply default values of current mode */
+ reg_list = &imx500->mode->reg_list;
+ ret = cci_multi_reg_write(imx500->regmap, reg_list->regs,
+@@ -1141,6 +2245,11 @@ static void imx500_stop_streaming(struct
+ if (ret)
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
+
++ /* Disable DNN */
++ ret = cci_write(imx500->regmap, CCI_REG8(0xD100), 0, NULL);
++ if (ret)
++ dev_err(&client->dev, "%s failed to disable DNN\n", __func__);
++
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
+ }
+@@ -1173,6 +2282,7 @@ static int imx500_set_stream(struct v4l2
+ /* vflip and hflip cannot change during streaming */
+ __v4l2_ctrl_grab(imx500->vflip, enable);
+ __v4l2_ctrl_grab(imx500->hflip, enable);
++ __v4l2_ctrl_grab(imx500->network_fw_ctrl, enable);
+
+ mutex_unlock(&imx500->mutex);
+
+@@ -1247,7 +2357,10 @@ static int imx500_power_off(struct devic
+ regulator_bulk_disable(IMX500_NUM_SUPPLIES, imx500->supplies);
+
+ /* Force reprogramming of the common registers when powered up again. */
++ imx500->fsm_state = IMX500_STATE_RESET;
+ imx500->common_regs_written = false;
++ imx500->loader_and_main_written = false;
++ imx500_clear_fw_network(imx500);
+
+ return 0;
+ }
+@@ -1317,6 +2430,36 @@ static const s64 imx500_link_freq_menu[]
+ IMX500_DEFAULT_LINK_FREQ,
+ };
+
++/* Custom control for inference window */
++static const struct v4l2_ctrl_config inf_window_ctrl = {
++ .name = "IMX500 Inference Windows",
++ .id = V4L2_CID_USER_IMX500_INFERENCE_WINDOW,
++ .dims[0] = 4,
++ .ops = &imx500_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_U32,
++ .elem_size = sizeof(u32),
++ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE |
++ V4L2_CTRL_FLAG_HAS_PAYLOAD,
++ .def = 0,
++ .min = 0x00,
++ .max = 4032,
++ .step = 1,
++};
++
++/* Custom control for network firmware file FD */
++static const struct v4l2_ctrl_config network_fw_fd = {
++ .name = "IMX500 Network Firmware File FD",
++ .id = V4L2_CID_USER_IMX500_NETWORK_FW_FD,
++ .ops = &imx500_ctrl_ops,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE |
++ V4L2_CTRL_FLAG_WRITE_ONLY,
++ .min = -1,
++ .max = S32_MAX,
++ .step = 1,
++ .def = -1,
++};
++
+ /* Initialize control handlers */
+ static int imx500_init_controls(struct imx500 *imx500)
+ {
+@@ -1376,6 +2519,9 @@ static int imx500_init_controls(struct i
+ imx500->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ v4l2_ctrl_new_custom(ctrl_hdlr, &imx500_notify_gains_ctrl, NULL);
++ v4l2_ctrl_new_custom(ctrl_hdlr, &inf_window_ctrl, NULL);
++ imx500->network_fw_ctrl =
++ v4l2_ctrl_new_custom(ctrl_hdlr, &network_fw_fd, NULL);
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+@@ -1459,12 +2605,35 @@ error_out:
+ return ret;
+ }
+
++static int fw_progress_show(struct seq_file *s, void *data)
++{
++ struct imx500 *imx500 = s->private;
++
++ seq_printf(s, "%d %zu %zu\n", imx500->fw_stage, imx500->fw_progress,
++ imx500->fw_network_size);
++ return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(fw_progress);
++
+ static int imx500_probe(struct i2c_client *client)
+ {
+ struct device *dev = &client->dev;
++ struct spi_device *spi = NULL;
++ char debugfs_name[128];
+ struct imx500 *imx500;
+ int ret;
+
++ struct device_node *spi_node = of_parse_phandle(dev->of_node, "spi", 0);
++
++ if (spi_node) {
++ struct device *tmp =
++ bus_find_device_by_of_node(&spi_bus_type, spi_node);
++ of_node_put(spi_node);
++ spi = tmp ? to_spi_device(tmp) : NULL;
++ if (!spi)
++ return -EPROBE_DEFER;
++ }
++
+ imx500 = devm_kzalloc(&client->dev, sizeof(*imx500), GFP_KERNEL);
+ if (!imx500)
+ return -ENOMEM;
+@@ -1474,6 +2643,8 @@ static int imx500_probe(struct i2c_clien
+ return dev_err_probe(dev, PTR_ERR(imx500->regmap),
+ "failed to initialise CCI\n");
+
++ imx500->spi_device = spi;
++
+ v4l2_i2c_subdev_init(&imx500->sd, client, &imx500_subdev_ops);
+
+ /* Check the hardware configuration in device tree */
+@@ -1534,10 +2705,10 @@ static int imx500_probe(struct i2c_clien
+ imx500->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pads */
+- imx500->pad.flags = MEDIA_PAD_FL_SOURCE;
++ imx500->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
++ imx500->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+- ret = media_entity_pads_init(&imx500->sd.entity, NUM_PADS,
+- &imx500->pad);
++ ret = media_entity_pads_init(&imx500->sd.entity, NUM_PADS, imx500->pad);
+ if (ret) {
+ dev_err(dev, "failed to init entity pads: %d\n", ret);
+ goto error_handler_free;
+@@ -1549,6 +2720,12 @@ static int imx500_probe(struct i2c_clien
+ goto error_media_entity;
+ }
+
++ snprintf(debugfs_name, sizeof(debugfs_name), "imx500-fw:%s",
++ dev_name(dev));
++ imx500->debugfs = debugfs_create_dir(debugfs_name, NULL);
++ debugfs_create_file("fw_progress", 0444, imx500->debugfs, imx500,
++ &fw_progress_fops);
++
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
+
+@@ -1573,10 +2750,23 @@ static void imx500_remove(struct i2c_cli
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx500 *imx500 = to_imx500(sd);
+
++ if (imx500->spi_device)
++ put_device(&imx500->spi_device->dev);
++
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ imx500_free_controls(imx500);
+
++ if (imx500->fw_loader)
++ release_firmware(imx500->fw_loader);
++
++ if (imx500->fw_main)
++ release_firmware(imx500->fw_main);
++
++ imx500->fw_loader = NULL;
++ imx500->fw_main = NULL;
++ imx500_clear_fw_network(imx500);
++
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ imx500_power_off(&client->dev);
+@@ -1603,7 +2793,63 @@ static struct i2c_driver imx500_i2c_driv
+ .remove = imx500_remove,
+ };
+
+-module_i2c_driver(imx500_i2c_driver);
++static int imx500_spi_probe(struct spi_device *spi)
++{
++ int result;
++
++ spi->bits_per_word = 8;
++ spi->max_speed_hz = 35000000;
++ spi->mode = SPI_MODE_3;
++
++ result = spi_setup(spi);
++ if (result < 0)
++ return dev_err_probe(&spi->dev, result, "spi_setup() failed");
++
++ return 0;
++}
++
++static void imx500_spi_remove(struct spi_device *spi)
++{
++}
++
++static const struct spi_device_id imx500_spi_id[] = {
++ { "imx500", 0 },
++ {},
++};
++MODULE_DEVICE_TABLE(spi, imx500_spi_id);
++
++static struct spi_driver imx500_spi_driver = {
++ .driver = {
++ .name = "imx500",
++ .of_match_table = imx500_dt_ids,
++ },
++ .probe = imx500_spi_probe,
++ .remove = imx500_spi_remove,
++ .id_table = imx500_spi_id,
++};
++
++static int __init imx500_driver_init(void)
++{
++ int ret;
++
++ ret = spi_register_driver(&imx500_spi_driver);
++ if (ret)
++ return ret;
++
++ ret = i2c_add_driver(&imx500_i2c_driver);
++ if (ret)
++ spi_unregister_driver(&imx500_spi_driver);
++
++ return ret;
++}
++module_init(imx500_driver_init);
++
++static void __exit imx500_driver_exit(void)
++{
++ i2c_del_driver(&imx500_i2c_driver);
++ spi_unregister_driver(&imx500_spi_driver);
++}
++module_exit(imx500_driver_exit);
+
+ MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+ MODULE_DESCRIPTION("Sony IMX500 sensor driver");
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -207,6 +207,12 @@ enum v4l2_colorfx {
+ * We reserve 16 controls for this driver. */
+ #define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10e0)
+
++/*
++ * The base for IMX500 driver controls.
++ * We reserve 16 controls for this driver.
++ */
++#define V4L2_CID_USER_IMX500_BASE (V4L2_CID_USER_BASE + 0x2000)
++
+ /* MPEG-class control IDs */
+ /* The MPEG controls are applicable to all codec controls
+ * and the 'MPEG' part of the define is historical */
--- /dev/null
+From 15cc525406cf311399d182ffcc18711651b7c8b2 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Fri, 12 Jul 2024 13:32:30 +0100
+Subject: [PATCH 1247/1350] dts: bcm{283x,2712}: /clocks as "simple-bus"
+
+Make /clocks node compatible with "simple-bus" to ensure that all children
+(not just those that are "fixed-clock" compatible) are automatically
+probed.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm283x.dtsi | 4 ++++
+ arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 4 ++++
+ 2 files changed, 8 insertions(+)
+
+--- a/arch/arm/boot/dts/broadcom/bcm283x.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm283x.dtsi
+@@ -478,6 +478,10 @@
+ };
+
+ clocks {
++ compatible = "simple-bus";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
+ /* The oscillator is the root of the clock tree. */
+ clk_osc: clk-osc {
+ compatible = "fixed-clock";
+--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+@@ -1258,6 +1258,10 @@
+ };
+
+ clocks {
++ compatible = "simple-bus";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
+ /* The oscillator is the root of the clock tree. */
+ clk_osc: clk-osc {
+ compatible = "fixed-clock";
--- /dev/null
+From 4113b1097fe40ff3a64ee7a9ade7c258e9ecdb5b Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Mon, 3 Jun 2024 14:10:27 +0100
+Subject: [PATCH 1248/1350] dts: Add 'AI Camera' support
+
+The AI Camera combines an IMX500 and a RP2040 GPIO bridge on a single
+module. The Raspberry Pi camera regulator is used for both IMX500 and
+RP2040. SPI, clocks, and reset (required by IMX500) are provided by the
+RP2040 GPIO bridge.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 2 +
+ arch/arm/boot/dts/overlays/README | 21 +++
+ arch/arm/boot/dts/overlays/imx500-overlay.dts | 119 +++++++++++++++++
+ .../boot/dts/overlays/imx500-pi5-overlay.dts | 124 ++++++++++++++++++
+ arch/arm/boot/dts/overlays/imx500.dtsi | 28 ++++
+ arch/arm/boot/dts/overlays/overlay_map.dts | 10 ++
+ .../dts/overlays/rpi-rp2040-gpio-bridge.dtsi | 21 +++
+ 7 files changed, 325 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/imx500-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/imx500.dtsi
+ create mode 100644 arch/arm/boot/dts/overlays/rpi-rp2040-gpio-bridge.dtsi
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -136,6 +136,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ imx378.dtbo \
+ imx462.dtbo \
+ imx477.dtbo \
++ imx500.dtbo \
++ imx500-pi5.dtbo \
+ imx519.dtbo \
+ imx708.dtbo \
+ interludeaudio-analog.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2818,6 +2818,27 @@ Params: rotation Mounting
+ sync-sink Configure as vsync sink
+
+
++Name: imx500
++Info: Sony IMX500 camera module.
++ Uses Unicam 1, which is the standard camera connector on most Pi
++ variants.
++Load: dtoverlay=imx500,<param>=<val>
++Params: rotation Mounting rotation of the camera sensor (0 or
++ 180, default 0)
++ orientation Sensor orientation (0 = front, 1 = rear,
++ 2 = external, default external)
++ media-controller Configure use of Media Controller API for
++ configuring the sensor (default on)
++ cam0 Adopt the default configuration for CAM0 on a
++ Compute Module (CSI0, i2c_vc, and cam0_reg).
++ bypass-cache Do save blocks of data to flash when using
++ rp2040-gpio-bridge for SPI transfers.
++
++
++Name: imx500-pi5
++Info: See imx500 (this is the Pi 5 version)
++
++
+ Name: imx519
+ Info: Sony IMX519 camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/imx500-overlay.dts
+@@ -0,0 +1,119 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for IMX500 camera module on VC I2C bus
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/gpio/gpio.h>
++
++/{
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2c0if>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ reg_frag: fragment@2 {
++ target = <&cam1_reg>;
++ cam_reg: __overlay__ {
++ startup-delay-us = <300000>;
++ };
++ };
++
++ i2c_frag: fragment@100 {
++ target = <&i2c_csi_dsi>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ #include "imx500.dtsi"
++ #include "rpi-rp2040-gpio-bridge.dtsi"
++ };
++ };
++
++ csi_frag: fragment@101 {
++ target = <&csi1>;
++ csi: __overlay__ {
++ status = "okay";
++ brcm,media-controller;
++
++ port {
++ csi_ep: endpoint {
++ remote-endpoint = <&cam_endpoint>;
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ };
++ };
++ };
++ };
++
++ spi_frag: fragment@102 {
++ target = <&spi_bridgedev0>;
++ __overlay__ {
++ compatible = "sony,imx500";
++ };
++ };
++
++ chosen_frag: fragment@103 {
++ target = <&chosen>;
++ __overlay__ {
++ core_freq_fixed;
++ };
++ };
++
++ clocks_frag: fragment@104 {
++ target-path = "/clocks";
++ __overlay__ {
++ clk_aicam: clk-aicam {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ clk_aicam_gated: clk-aicam-gated {
++ compatible = "gpio-gate-clock";
++ clocks = <&clk_aicam>;
++ #clock-cells = <0>;
++ enable-gpios = <&spi_bridge 21 GPIO_ACTIVE_HIGH>;
++ };
++ };
++ };
++
++ __overrides__ {
++ rotation = <&cam_node>,"rotation:0";
++ orientation = <&cam_node>,"orientation:0";
++ media-controller = <&csi>,"brcm,media-controller?";
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&csi_frag>, "target:0=",<&csi0>,
++ <&spi_bridge>, "power-supply:0=",<&cam0_reg>,
++ <®_frag>, "target:0=",<&cam0_reg>,
++ <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++ bypass-cache = <&spi_bridge>,"bypass-cache?";
++ };
++};
++
++&cam_node {
++ status = "okay";
++ reset-gpios = <&spi_bridge 20 GPIO_ACTIVE_HIGH>;
++ clocks = <&clk_aicam_gated>;
++ spi = <&spi_bridgedev0>;
++};
++
++&spi_bridge {
++ status = "okay";
++};
++
++&cam_endpoint {
++ remote-endpoint = <&csi_ep>;
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
+@@ -0,0 +1,124 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for IMX500 camera module on VC I2C bus
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/gpio/gpio.h>
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&i2c0if>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@1 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ reg_frag: fragment@2 {
++ target = <&cam1_reg>;
++ cam_reg: __overlay__ {
++ startup-delay-us = <300000>;
++ };
++ };
++
++ i2c_frag: fragment@100 {
++ target = <&i2c_csi_dsi>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ #include "imx500.dtsi"
++ #include "rpi-rp2040-gpio-bridge.dtsi"
++ };
++ };
++
++ csi_frag: fragment@101 {
++ target = <&csi1>;
++ csi: __overlay__ {
++ status = "okay";
++ brcm,media-controller;
++
++ port {
++ csi_ep: endpoint {
++ remote-endpoint = <&cam_endpoint>;
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ };
++ };
++ };
++ };
++
++ spi_frag: fragment@102 {
++ target = <&spi_bridge>;
++ spi_frag_overlay: __overlay__ {
++ fast_xfer_requires_i2c_lock = <1>;
++ fast_xfer_recv_gpio_base = <11>;
++ fast_xfer-gpios = <&rp1_gpio 40 0>, // CD1_SDA (used as data)
++ <&rp1_gpio 48 0>; // CD1_IO1_MICDAT1 (clock)
++ };
++ };
++
++ spi_bridge_frag: fragment@103 {
++ target = <&spi_bridgedev0>;
++ __overlay__ {
++ compatible = "sony,imx500";
++ };
++ };
++
++ clocks_frag: fragment@104 {
++ target-path = "/clocks";
++ __overlay__ {
++ clk_aicam: clk-aicam {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24000000>;
++ };
++
++ clk_aicam_gated: clk-aicam-gated {
++ compatible = "gpio-gate-clock";
++ clocks = <&clk_aicam>;
++ #clock-cells = <0>;
++ enable-gpios = <&spi_bridge 21 GPIO_ACTIVE_HIGH>;
++ };
++ };
++ };
++
++ __overrides__ {
++ rotation = <&cam_node>,"rotation:0";
++ orientation = <&cam_node>,"orientation:0";
++ media-controller = <&csi>,"brcm,media-controller?";
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&csi_frag>, "target:0=",<&csi0>,
++ <&spi_frag_overlay>, "fast_xfer-gpios:4=38", // CD0_SDA (data)
++ <&spi_frag_overlay>, "fast_xfer-gpios:16=35", // CD0_IO1_MICDAT0 (clock)
++ <&spi_bridge>, "power-supply:0=",<&cam0_reg>,
++ <®_frag>, "target:0=",<&cam0_reg>,
++ <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++ bypass-cache = <&spi_bridge>,"bypass-cache?";
++ };
++};
++
++&cam_node {
++ status = "okay";
++ reset-gpios = <&spi_bridge 20 GPIO_ACTIVE_HIGH>;
++ clocks = <&clk_aicam_gated>;
++ spi = <&spi_bridgedev0>;
++};
++
++&spi_bridge {
++ status = "okay";
++};
++
++&cam_endpoint {
++ remote-endpoint = <&csi_ep>;
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/imx500.dtsi
+@@ -0,0 +1,28 @@
++// SPDX-License-Identifier: GPL-2.0-only
++cam_node: imx500@1a {
++ reg = <0x1a>;
++ compatible = "sony,imx500";
++ status = "disabled";
++
++ clocks = <&cam1_clk>;
++ clock-names = "inck";
++
++ vana-supply = <&cam1_reg>; /* 2.7v */
++ vdig-supply = <&cam_dummy_reg>; /* 0.84v */
++ vif-supply = <&cam_dummy_reg>; /* 1.8v */
++
++ reset-gpios = <&gpio 255 GPIO_ACTIVE_HIGH>;
++
++ rotation = <0>;
++ orientation = <2>;
++
++ port {
++ cam_endpoint: endpoint {
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ clock-noncontinuous;
++ link-frequencies =
++ /bits/ 64 <444000000>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -118,6 +118,16 @@
+ bcm2711;
+ };
+
++ imx500 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "imx500-pi5";
++ };
++
++ imx500-pi5 {
++ bcm2712;
++ };
++
+ lirc-rpi {
+ deprecated = "use gpio-ir";
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/rpi-rp2040-gpio-bridge.dtsi
+@@ -0,0 +1,21 @@
++// SPDX-License-Identifier: GPL-2.0-only
++spi_bridge: spi@40 {
++ reg = <0x40>;
++ compatible = "raspberrypi,rp2040-gpio-bridge";
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ power-supply = <&cam1_reg>;
++
++ #gpio-cells = <2>;
++ gpio-controller;
++
++ spi_bridgedev0: spidev@0{
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <35000000>;
++ };
++};
--- /dev/null
+From 62faabb61ae152e8289a00ffb3445f4c47777b33 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Mon, 3 Jun 2024 16:02:41 +0100
+Subject: [PATCH 1250/1350] media: i2c: imx500: Enable LED during SPI transfers
+
+The Raspberry Pi 'AI Camera' is equipped with an LED. Enable this LED
+during SPI transfers to indicate to the end-user that progress is being
+made during large tramsfers.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx500-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts | 1 +
+ drivers/media/i2c/imx500.c | 6 ++++++
+ 3 files changed, 8 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/imx500-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx500-overlay.dts
+@@ -105,6 +105,7 @@
+
+ &cam_node {
+ status = "okay";
++ led-gpios = <&spi_bridge 19 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&spi_bridge 20 GPIO_ACTIVE_HIGH>;
+ clocks = <&clk_aicam_gated>;
+ spi = <&spi_bridgedev0>;
+--- a/arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
+@@ -110,6 +110,7 @@
+
+ &cam_node {
+ status = "okay";
++ led-gpios = <&spi_bridge 19 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&spi_bridge 20 GPIO_ACTIVE_HIGH>;
+ clocks = <&clk_aicam_gated>;
+ spi = <&spi_bridgedev0>;
+--- a/drivers/media/i2c/imx500.c
++++ b/drivers/media/i2c/imx500.c
+@@ -965,6 +965,7 @@ struct imx500 {
+ struct clk *xclk;
+ u32 xclk_freq;
+
++ struct gpio_desc *led_gpio;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[IMX500_NUM_SUPPLIES];
+
+@@ -1990,7 +1991,10 @@ static int imx500_state_transition(struc
+ }
+
+ /* Do SPI transfer */
++ gpiod_set_value_cansleep(imx500->led_gpio, 1);
+ ret = imx500_spi_write(imx500, data, size);
++ gpiod_set_value_cansleep(imx500->led_gpio, 0);
++
+ imx500->fw_progress += size;
+
+ if (ret < 0)
+@@ -2670,6 +2674,8 @@ static int imx500_probe(struct i2c_clien
+ return ret;
+ }
+
++ imx500->led_gpio = devm_gpiod_get_optional(dev, "led", GPIOD_OUT_LOW);
++
+ imx500->reset_gpio =
+ devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+
--- /dev/null
+From cc50cdbcf3e8f065bd7798a92689f54578b4169f Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+Date: Wed, 24 Jul 2024 15:48:22 +0100
+Subject: [PATCH 1251/1350] spi: rp2040-gpio-bridge: add missing MD5 dependency
+
+rp2040-gpio-bridge relies on the md5 crypto driver. This dependency
+cannot be determined automatically as rp2040-gpio-bridge does not
+use any of md5's symbols directly.
+
+Declare a soft 'pre' dependency on md5 to ensure that it is included and
+loaded before rp2040-gpio-bridge.
+
+Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
+---
+ drivers/spi/spi-rp2040-gpio-bridge.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spi-rp2040-gpio-bridge.c
++++ b/drivers/spi/spi-rp2040-gpio-bridge.c
+@@ -1241,3 +1241,4 @@ module_i2c_driver(rp2040_gbdg_driver);
+ MODULE_AUTHOR("Richard Oliver <richard.oliver@raspberrypi.com>");
+ MODULE_DESCRIPTION("Raspberry Pi RP2040 GPIO Bridge");
+ MODULE_LICENSE("GPL");
++MODULE_SOFTDEP("pre: md5");
--- /dev/null
+From eab19e7bde679f56241db0c51f94f056fcffd6a9 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 4 Sep 2024 19:28:50 +0100
+Subject: [PATCH 1252/1350] drivers: drm: rp1-vec: Increase width limit, for
+ PAL 16:9 @ 18MHz
+
+There was no technical reason for the DRM mode's width limit of 848;
+increase it to 960 (720*18MHz/13.5MHz) to support ~square pixels on
+16:9 screens. Tweak the PAL active window to start slightly earlier.
+(The maximum number of visible columns at 18MHz is about 942.)
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 4 ++--
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -508,8 +508,8 @@ static int rp1vec_platform_probe(struct
+
+ vec->drm.mode_config.min_width = 256;
+ vec->drm.mode_config.min_height = 128;
+- vec->drm.mode_config.max_width = 848; /* for System E */
+- vec->drm.mode_config.max_height = 738; /* for System E */
++ vec->drm.mode_config.max_width = 960; /* for "widescreen" @ 18MHz */
++ vec->drm.mode_config.max_height = 738; /* for System E only */
+ vec->drm.mode_config.preferred_depth = 32;
+ vec->drm.mode_config.prefer_shadow = 0;
+ vec->drm.mode_config.quirk_addfb_prefer_host_byte_order = true;
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -195,7 +195,7 @@ static const struct rp1vec_hwmode rp1vec
+ .misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync, PAL */
+ .nco_freq = 0x0a8262b2cc48c1d1,
+ .timing_regs = {
+- 0x046e0cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
++ 0x04660cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
+ 0x026c0270, 0x00000004, 0x00050009, 0x00070135,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00170136, 0x00000000,
+@@ -218,7 +218,7 @@ static const struct rp1vec_hwmode rp1vec
+ .misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
+ .nco_freq = 0x0a8262b2cc48c1d1,
+ .timing_regs = {
+- 0x046e0cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
++ 0x04660cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
+ 0x026c0270, 0x00000004, 0x00050009, 0x00070135,
+ 0x013f026d, 0x00060136, 0x0140026e, 0x0150026e,
+ 0x00180136, 0x026f0017,
--- /dev/null
+From d6a12dd8f4e9362f7dd355969dd046adc44b1f47 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 1 Mar 2021 09:12:44 +0000
+Subject: [PATCH 1255/1350] Revert "Bluetooth: Always request for user
+ confirmation for Just Works (LE SC)"
+
+This reverts commit ffee202a78c2980688bc5d2f7d56480e69a5e0c9.
+
+The commit "Bluetooth: Always request for user confirmation for Just
+Works" prevents BLE devices pairing in (at least) the Raspberry Pi OS
+GUI. After reverting it, pairing works again. Although this companion
+commit ("... (LE SC)") has not been demonstrated to be problematic,
+it follows the same logic and therefore could affect some use cases.
+
+If another solution to the problem is found then this reversion will
+be removed.
+
+See: https://github.com/raspberrypi/linux/issues/4139
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ net/bluetooth/smp.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/net/bluetooth/smp.c
++++ b/net/bluetooth/smp.c
+@@ -2208,7 +2208,7 @@ mackey_and_ltk:
+ if (err)
+ return SMP_UNSPECIFIED;
+
+- if (smp->method == REQ_OOB) {
++ if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+ if (test_bit(SMP_FLAG_INITIATOR, &smp->flags)) {
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+@@ -2223,9 +2223,6 @@ mackey_and_ltk:
+ confirm_hint = 0;
+
+ confirm:
+- if (smp->method == JUST_WORKS)
+- confirm_hint = 1;
+-
+ err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
+ hcon->dst_type, passkey, confirm_hint);
+ if (err)
--- /dev/null
+From e4c2a7731efcd7abe7554debef783b5358712d6d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Sep 2024 16:41:12 +0100
+Subject: [PATCH 1256/1350] media: i2c: ov5647: Add control of V4L2_CID_HBLANK
+
+The driver did expose V4L2_CID_HBLANK, but as a READ_ONLY control.
+
+The sensor only uses the HTS register to control the line length,
+so convert this control to read/write, with the appropriate ranges.
+Adopt the old fixed values as the minimum values permitted in each
+mode to avoid issues of it not streaming.
+
+This should allow exposure times up to ~3 seconds (up from ~1sec).
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 26 +++++++++++++-------------
+ 1 file changed, 13 insertions(+), 13 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -53,6 +53,8 @@
+ #define OV5647_REG_AEC_AGC 0x3503
+ #define OV5647_REG_GAIN_HI 0x350a
+ #define OV5647_REG_GAIN_LO 0x350b
++#define OV5647_REG_HTS_HI 0x380c
++#define OV5647_REG_HTS_LO 0x380d
+ #define OV5647_REG_VTS_HI 0x380e
+ #define OV5647_REG_VTS_LO 0x380f
+ #define OV5647_REG_VFLIP 0x3820
+@@ -79,6 +81,8 @@
+ #define OV5647_VBLANK_MIN 24
+ #define OV5647_VTS_MAX 32767
+
++#define OV5647_HTS_MAX 0x1fff
++
+ #define OV5647_EXPOSURE_MIN 4
+ #define OV5647_EXPOSURE_STEP 1
+ #define OV5647_EXPOSURE_DEFAULT 1000
+@@ -188,8 +192,6 @@ static struct regval_list ov5647_2592x19
+ {0x3a19, 0xf8},
+ {0x3c01, 0x80},
+ {0x3b07, 0x0c},
+- {0x380c, 0x0b},
+- {0x380d, 0x1c},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+@@ -277,8 +279,6 @@ static struct regval_list ov5647_1080p30
+ {0x3a19, 0xf8},
+ {0x3c01, 0x80},
+ {0x3b07, 0x0c},
+- {0x380c, 0x09},
+- {0x380d, 0x70},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+@@ -376,8 +376,6 @@ static struct regval_list ov5647_2x2binn
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+- {0x380c, 0x07},
+- {0x380d, 0x68},
+ {0x3811, 0x0c},
+ {0x3813, 0x06},
+ {0x3814, 0x31},
+@@ -451,8 +449,6 @@ static struct regval_list ov5647_640x480
+ {0x3a19, 0xf8},
+ {0x3c01, 0x80},
+ {0x3b07, 0x0c},
+- {0x380c, 0x07},
+- {0x380d, 0x3c},
+ {0x3814, 0x35},
+ {0x3815, 0x35},
+ {0x3708, 0x64},
+@@ -1079,7 +1075,8 @@ static int ov5647_set_pad_fmt(struct v4l
+ mode->pixel_rate, 1, mode->pixel_rate);
+
+ hblank = mode->hts - mode->format.width;
+- __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1,
++ __v4l2_ctrl_modify_range(sensor->hblank, hblank,
++ OV5647_HTS_MAX - mode->format.width, 1,
+ hblank);
+
+ vblank = mode->vts - mode->format.height;
+@@ -1343,6 +1340,10 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+ ret = ov5647_write16(sd, OV5647_REG_VTS_HI,
+ sensor->mode->format.height + ctrl->val);
+ break;
++ case V4L2_CID_HBLANK:
++ ret = ov5647_write16(sd, OV5647_REG_HTS_HI,
++ sensor->mode->format.width + ctrl->val);
++ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5647_write(sd, OV5647_REG_ISPCTRL3D,
+ ov5647_test_pattern_val[ctrl->val]);
+@@ -1350,7 +1351,6 @@ static int ov5647_s_ctrl(struct v4l2_ctr
+
+ /* Read-only, but we adjust it based on mode. */
+ case V4L2_CID_PIXEL_RATE:
+- case V4L2_CID_HBLANK:
+ /* Read-only, but we adjust it based on mode. */
+ break;
+
+@@ -1427,10 +1427,11 @@ static int ov5647_init_controls(struct o
+ sensor->mode->pixel_rate, 1,
+ sensor->mode->pixel_rate);
+
+- /* By default, HBLANK is read only, but it does change per mode. */
+ hblank = sensor->mode->hts - sensor->mode->format.width;
+ sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+- V4L2_CID_HBLANK, hblank, hblank, 1,
++ V4L2_CID_HBLANK, hblank,
++ OV5647_HTS_MAX -
++ sensor->mode->format.width, 1,
+ hblank);
+
+ sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+@@ -1464,7 +1465,6 @@ static int ov5647_init_controls(struct o
+ goto handler_free;
+
+ sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+- sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->sd.ctrl_handler = &sensor->ctrls;
+
+ return 0;
--- /dev/null
+From d6a3a3f106010d9576a700148220842cac4c5739 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 25 Jul 2024 15:42:41 +0100
+Subject: [PATCH 1257/1350] drm/vc4: Add support for per plane scaling filter
+ selection
+
+Seeing as the HVS can be configured with regard the scaling filter,
+and DRM now supports selecting scaling filters at a per CRTC or
+per plane level, we can implement it.
+
+Default remains as the Mitchell/Netravali filter, but nearest
+neighbour is now also implemented.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++--
+ drivers/gpu/drm/vc4/vc4_plane.c | 48 ++++++++++++++++++++++++++-------
+ 3 files changed, 52 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -358,6 +358,7 @@ struct vc4_hvs {
+ struct work_struct free_dlist_work;
+
+ struct drm_mm_node mitchell_netravali_filter;
++ struct drm_mm_node nearest_neighbour_filter;
+
+ struct debugfs_regset32 regset;
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -469,6 +469,9 @@ static int vc4_hvs_debugfs_dlist_allocs(
+ static const u32 mitchell_netravali_1_3_1_3_kernel[] =
+ VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18,
+ 50, 82, 119, 155, 187, 213, 227);
++static const u32 nearest_neighbour_kernel[] =
++ VC4_LINEAR_PHASE_KERNEL(0, 0, 0, 0, 0, 0, 0, 0,
++ 1, 1, 1, 1, 255, 255, 255, 255);
+
+ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
+ struct drm_mm_node *space,
+@@ -2255,14 +2258,19 @@ static int vc4_hvs_bind(struct device *d
+ if (ret)
+ return ret;
+
+- /* Upload filter kernels. We only have the one for now, so we
+- * keep it around for the lifetime of the driver.
++ /* Upload filter kernels. We only have the two for now, so we
++ * keep them around for the lifetime of the driver.
+ */
+ ret = vc4_hvs_upload_linear_kernel(hvs,
+ &hvs->mitchell_netravali_filter,
+ mitchell_netravali_1_3_1_3_kernel);
+ if (ret)
+ return ret;
++ ret = vc4_hvs_upload_linear_kernel(hvs,
++ &hvs->nearest_neighbour_filter,
++ nearest_neighbour_kernel);
++ if (ret)
++ return ret;
+
+ ret = vc4_hvs_cob_init(hvs);
+ if (ret)
+@@ -2288,6 +2296,8 @@ static void vc4_hvs_unbind(struct device
+
+ if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter))
+ drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter);
++ if (drm_mm_node_allocated(&vc4->hvs->nearest_neighbour_filter))
++ drm_mm_remove_node(&vc4->hvs->nearest_neighbour_filter);
+
+ drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm)
+ drm_mm_remove_node(node);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -582,7 +582,9 @@ static void vc4_write_tpz(struct vc4_pla
+ /* phase magnitude bits */
+ #define PHASE_BITS 6
+
+-static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel, int chroma_offset)
++static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst,
++ u32 xy, int channel, int chroma_offset,
++ bool no_interpolate)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(vc4_state->base.plane->dev);
+ u32 scale = src / dst;
+@@ -621,6 +623,7 @@ static void vc4_write_ppf(struct vc4_pla
+ phase &= SCALER_PPF_IPHASE_MASK;
+
+ vc4_dlist_write(vc4_state,
++ no_interpolate ? SCALER_PPF_NOINTERP : 0 |
+ SCALER_PPF_AGC |
+ VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
+ /*
+@@ -815,15 +818,17 @@ static void vc4_write_scaling_parameters
+ /* Ch0 H-PPF Word 0: Scaling Parameters */
+ if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
+ vc4_write_ppf(vc4_state,
+- vc4_state->src_w[channel], vc4_state->crtc_w, vc4_state->src_x, channel,
+- state->chroma_siting_h);
++ vc4_state->src_w[channel], vc4_state->crtc_w, vc4_state->src_x,
++ channel, state->chroma_siting_h,
++ state->scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR);
+ }
+
+ /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
+ if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) {
+ vc4_write_ppf(vc4_state,
+- vc4_state->src_h[channel], vc4_state->crtc_h, vc4_state->src_y, channel,
+- state->chroma_siting_v);
++ vc4_state->src_h[channel], vc4_state->crtc_h, vc4_state->src_y,
++ channel, state->chroma_siting_v,
++ state->scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR);
+ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+ }
+
+@@ -1573,7 +1578,18 @@ static int vc4_plane_mode_set(struct drm
+ vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
+ vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
+ vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
+- u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
++ struct drm_mm_node *filter;
++
++ switch (state->scaling_filter) {
++ case DRM_SCALING_FILTER_DEFAULT:
++ default:
++ filter = &vc4->hvs->mitchell_netravali_filter;
++ break;
++ case DRM_SCALING_FILTER_NEAREST_NEIGHBOR:
++ filter = &vc4->hvs->nearest_neighbour_filter;
++ break;
++ }
++ u32 kernel = VC4_SET_FIELD(filter->start,
+ SCALER_PPF_KERNEL_OFFSET);
+
+ /* HPPF plane 0 */
+@@ -1984,9 +2000,19 @@ static int vc6_plane_mode_set(struct drm
+ vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
+ vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
+ vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
+- u32 kernel =
+- VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
+- SCALER_PPF_KERNEL_OFFSET);
++ struct drm_mm_node *filter;
++
++ switch (state->scaling_filter) {
++ case DRM_SCALING_FILTER_DEFAULT:
++ default:
++ filter = &vc4->hvs->mitchell_netravali_filter;
++ break;
++ case DRM_SCALING_FILTER_NEAREST_NEIGHBOR:
++ filter = &vc4->hvs->nearest_neighbour_filter;
++ break;
++ }
++ u32 kernel = VC4_SET_FIELD(filter->start,
++ SCALER_PPF_KERNEL_OFFSET);
+
+ /* HPPF plane 0 */
+ vc4_dlist_write(vc4_state, kernel);
+@@ -2468,6 +2494,10 @@ struct drm_plane *vc4_plane_init(struct
+ DRM_COLOR_YCBCR_BT709,
+ DRM_COLOR_YCBCR_LIMITED_RANGE);
+
++ drm_plane_create_scaling_filter_property(plane,
++ BIT(DRM_SCALING_FILTER_DEFAULT) |
++ BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR));
++
+ drm_plane_create_chroma_siting_properties(plane, 0, 0);
+
+ if (type == DRM_PLANE_TYPE_PRIMARY)
--- /dev/null
+From 7a3d1c22ecbdac458883991beae0461137cbfc8a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 12 Sep 2024 18:04:58 +0100
+Subject: [PATCH 1258/1350] drm: panel: waveshare: Remove duplicated sentinel
+ on compatible list
+
+Remove the duplicated sentinel that got added, otherwise we have
+an extra blank compatible string match in the module, and that matches
+everything.
+
+$ modinfo panel_waveshare_dsi
+filename: /lib/modules/6.6.50-v8+/kernel/drivers/gpu/drm/panel/panel-waveshare-dsi.ko.xz
+license: GPL
+description: Waveshare DSI panel driver
+author: Dave Stevenson <dave.stevenson@raspberrypi.com>
+srcversion: E767180DABD8B00B45571AF
+alias: of:N*T*C*
+alias: of:N*T*
+alias: of:N*T*Cwaveshare,8.8inch-panelC*
+alias: of:N*T*Cwaveshare,8.8inch-panel
+
+Fixes: f955b7838f9c ("drivers:gpu:drm:panel: Added waveshare 5.0inch, 6.25inch, and 8.8inch dsi screen devices")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -467,7 +467,6 @@ static const struct of_device_id ws_pane
+ .compatible = "waveshare,8.8inch-panel",
+ .data = &ws_panel_8_8_mode,
+ }, {
+- }, {
+ /* sentinel */
+ }
+ };
--- /dev/null
+From c9b61d1d83073761871c7acba332216494e6f0bb Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 8 Apr 2024 16:09:52 +0100
+Subject: [PATCH 1260/1350] drivers: mmc: disable write-caching on Samsung 2023
+ model year SD cards
+
+Samsung EVO Plus, Pro Plus and Evo Ultimate cards of this era appear to
+have a broken cache-flush implementation when operating in CQ mode.
+
+Unfortunately the cards seem to use a separate CID name string for every
+variant and capacity, so nobble the cache feature for this MANFID, OEMID
+and year. Turning this off seems to have negligible impact on
+random-write throughput in non-CQ mode.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/card.h | 1 +
+ drivers/mmc/core/quirks.h | 8 ++++++++
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/mmc/core/card.h
++++ b/drivers/mmc/core/card.h
+@@ -84,6 +84,7 @@ struct mmc_fixup {
+ #define CID_MANFID_TOSHIBA 0x11
+ #define CID_MANFID_MICRON 0x13
+ #define CID_MANFID_SAMSUNG 0x15
++#define CID_MANFID_SAMSUNG_SD 0x1b
+ #define CID_MANFID_APACER 0x27
+ #define CID_MANFID_KINGSTON 0x70
+ #define CID_MANFID_HYNIX 0x90
+--- a/drivers/mmc/core/quirks.h
++++ b/drivers/mmc/core/quirks.h
+@@ -25,6 +25,14 @@ static const struct mmc_fixup __maybe_un
+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
+ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
+
++ /*
++ * Samsung Pro Plus/EVO Plus/Pro Ultimate SD cards (2023) claim to cache
++ * flush OK, but become unresponsive afterwards.
++ */
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_SAMSUNG_SD, 0x534d, 2023, CID_MONTH_ANY,
++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
+ END_FIXUP
+ };
+
--- /dev/null
+From 536f2097dc397e4ebd0566e8f00219c02d7c8073 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 12 Sep 2024 10:06:50 +0100
+Subject: [PATCH 1261/1350] mm/vmscan: Maintain TLB coherency in LRU code
+
+As a workaround (and possibly a fix) for CPU spins observed on BCM2837,
+use ptep_clear_flush_young instead of ptep_test_and_clear_young inside
+lru_gen_look_around in order to expose PTE changes to the MMU. Note that
+on architectures that don't require an explicit flush,
+ptep_clear_flush_young just calls ptep_test_and_clear_young.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ mm/vmscan.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/mm/vmscan.c
++++ b/mm/vmscan.c
+@@ -4716,7 +4716,7 @@ void lru_gen_look_around(struct page_vma
+ if (!folio)
+ continue;
+
+- if (!ptep_test_and_clear_young(vma, addr, pte + i))
++ if (!ptep_clear_flush_young(vma, addr, pte + i))
+ VM_WARN_ON_ONCE(true);
+
+ young++;
--- /dev/null
+From c763cf351ea25b7fb31f2d388dab0838565d1b75 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 13 Sep 2024 14:44:08 +0100
+Subject: [PATCH 1262/1350] drm: panel: ili9881: Correct symmetry on
+ enable/disable return codes
+
+ili9881c_enable is always returning 0.
+
+ili9881c_disable was returning the error code from
+mipi_dsi_dcs_set_display_off.
+If non-zero, the drm_panel framework will leave the panel marked as
+enabled, and not run the enable hook next time around. That isn't
+helpful, particularly as we're expecting unprepare to disable
+resets and regulators.
+
+Change ili9881c_disable to match enable in always returning 0.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
++++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+@@ -1745,7 +1745,9 @@ static int ili9881c_disable(struct drm_p
+ {
+ struct ili9881c *ctx = panel_to_ili9881c(panel);
+
+- return mipi_dsi_dcs_set_display_off(ctx->dsi);
++ mipi_dsi_dcs_set_display_off(ctx->dsi);
++
++ return 0;
+ }
+
+ static int ili9881c_unprepare(struct drm_panel *panel)
--- /dev/null
+From 77773aec03f65758c760ec5b43a79ad6edeb211b Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+Date: Thu, 12 Sep 2024 16:44:12 +0200
+Subject: [PATCH 1263/1350] dtoverlays: adds the definitions for the HiFiBerry
+ 8-channel ADC
+
+Additions and changes for the 8 channel ADC card. This card uses only
+HW-controlled devices which allows the uses of the 'dummy-dai'.
+It will run only on a PI5 as it requires the designware I2S0 module.
+
+The necessary output lanes I2S0_DI[0..3] are claimed from within the
+DT overlay.
+
+Signed-off-by: j-schambacher <joerg@hifiberry.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 +++
+ .../dts/overlays/hifiberry-adc8x-overlay.dts | 50 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++
+ 4 files changed, 61 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/hifiberry-adc8x-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -86,6 +86,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ hd44780-lcd.dtbo \
+ hdmi-backlight-hwhack-gpio.dtbo \
+ hifiberry-adc.dtbo \
++ hifiberry-adc8x.dtbo \
+ hifiberry-amp.dtbo \
+ hifiberry-amp100.dtbo \
+ hifiberry-amp3.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1778,6 +1778,12 @@ Params: leds_off If set t
+ is switched off at all times.
+
+
++Name: hifiberry-adc8x
++Info: Configures the HifiBerry ADC8X audio card (only on Pi5)
++Load: dtoverlay=hifiberry-adc8x
++Params: <None>
++
++
+ Name: hifiberry-amp
+ Info: Configures the HifiBerry Amp and Amp+ audio cards
+ Load: dtoverlay=hifiberry-amp
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/hifiberry-adc8x-overlay.dts
+@@ -0,0 +1,50 @@
++// Definitions for HiFiBerry ADC8x
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&gpio>;
++ __overlay__ {
++ rp1_i2s0_adc8x: rp1_i2s0_adc8x {
++ function = "i2s0";
++ pins = "gpio18", "gpio19", "gpio20",
++ "gpio22", "gpio24", "gpio26";
++ bias-disable;
++ status = "okay";
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&i2s_clk_producer>;
++ __overlay__ {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2s0_adc8x>;
++ status = "okay";
++ };
++ };
++
++ fragment@2 {
++ target-path = "/";
++ __overlay__ {
++ dummy-codec {
++ #sound-dai-cells = <0>;
++ compatible = "snd-soc-dummy";
++ status = "okay";
++ };
++ };
++ };
++
++ fragment@3 {
++ target = <&sound>;
++ __overlay__ {
++ compatible = "hifiberry,hifiberry-adc8x";
++ i2s-controller = <&i2s_clk_producer>;
++ status = "okay";
++ };
++ };
++
++};
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -48,6 +48,10 @@
+ bcm2712;
+ };
+
++ hifiberry-adc8x {
++ bcm2712;
++ };
++
+ hifiberry-dac8x {
+ bcm2712;
+ };
--- /dev/null
+From 4d2eaa194d77588fa42567ba174c3c14c5798027 Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+Date: Thu, 12 Sep 2024 17:42:13 +0200
+Subject: [PATCH 1264/1350] ASoC: add HiFiBerry ADC8x 8-channel ADC to
+ simple-card-driver
+
+Definitions for the 8 channel ADC card. The card uses only
+HW-controlled devices which allows the uses of the 'dummy-dai'.
+It will run only on a PI5 as it requires the designware I2S0 module.
+
+The necessary output lanes I2S0_DI[0..3] are claimed from within the
+DT overlay.
+
+Signed-off-by: j-schambacher <joerg@hifiberry.com>
+---
+ sound/soc/bcm/Kconfig | 7 ++++++
+ sound/soc/bcm/rpi-simple-soundcard.c | 37 ++++++++++++++++++++++++++++
+ 2 files changed, 44 insertions(+)
+
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -47,6 +47,13 @@ config SND_BCM2708_SOC_HIFIBERRY_ADC
+ Say Y or M if you want to add support for HifiBerry ADC.
+ Use this module for HiFiBerry's ADC-only sound cards
+
++config SND_BCM2708_SOC_HIFIBERRY_ADC8X
++ tristate "Support for HifiBerry ADC8X"
++ select SND_RPI_SIMPLE_SOUNDCARD
++ help
++ Say Y or M if you want to add support for HifiBerry ADC8X.
++ Note: ADC8X only works on PI5
++
+ config SND_BCM2708_SOC_HIFIBERRY_DAC
+ tristate "Support for HifiBerry DAC and DAC8X"
+ select SND_SOC_PCM5102A
+--- a/sound/soc/bcm/rpi-simple-soundcard.c
++++ b/sound/soc/bcm/rpi-simple-soundcard.c
+@@ -254,6 +254,41 @@ static struct snd_rpi_simple_drvdata drv
+ .dai = snd_hifiberrydacplusdsp_soundcard_dai,
+ };
+
++SND_SOC_DAILINK_DEFS(hifiberry_adc,
++ DAILINK_COMP_ARRAY(COMP_EMPTY()),
++ DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")),
++ DAILINK_COMP_ARRAY(COMP_EMPTY()));
++
++static int hifiberry_adc8x_init(struct snd_soc_pcm_runtime *rtd)
++{
++ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
++
++ /* set limits of 8 channels and 192ksps sample rate
++ */
++ codec_dai->driver->capture.channels_max = 8;
++ codec_dai->driver->capture.rates = SNDRV_PCM_RATE_8000_192000;
++
++ return 0;
++}
++
++static struct snd_soc_dai_link snd_hifiberry_adc8x_dai[] = {
++ {
++ .name = "HifiBerry ADC8x",
++ .stream_name = "HifiBerry ADC8x HiFi",
++ .dai_fmt = SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF |
++ SND_SOC_DAIFMT_CBS_CFS,
++ .init = hifiberry_adc8x_init,
++ SND_SOC_DAILINK_REG(hifiberry_adc),
++ },
++};
++
++static struct snd_rpi_simple_drvdata drvdata_hifiberry_adc8x = {
++ .card_name = "snd_rpi_hifiberry_adc8x",
++ .dai = snd_hifiberry_adc8x_dai,
++ .fixed_bclk_ratio = 64,
++};
++
+ SND_SOC_DAILINK_DEFS(hifiberry_amp,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC("tas5713.1-001b", "tas5713-hifi")),
+@@ -445,6 +480,8 @@ static const struct of_device_id snd_rpi
+ .data = (void *) &drvdata_googlevoicehat },
+ { .compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard",
+ .data = (void *) &drvdata_hifiberrydacplusdsp },
++ { .compatible = "hifiberry,hifiberry-adc8x",
++ .data = (void *) &drvdata_hifiberry_adc8x },
+ { .compatible = "hifiberry,hifiberry-amp",
+ .data = (void *) &drvdata_hifiberry_amp },
+ { .compatible = "hifiberry,hifiberry-amp3",
--- /dev/null
+From 8c9ca647449ba9df7e3b300c480242f9b5bdd867 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 10 Sep 2024 16:58:56 +0100
+Subject: [PATCH 1265/1350] vc04_services: codec: Allocate the max number of
+ buffers on the VPU
+
+The VPU's API can't match the use of VIDIOC_CREATE_BUFS to add buffers
+to the internal pool whilst a port is enabled, therefore allocate
+the maximum number of buffers possible in V4L2 to avoid the issue.
+As these are only buffer headers, the overhead is relatively small.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -2871,8 +2871,14 @@ static int bcm2835_codec_queue_setup(str
+
+ if (*nbuffers < port->minimum_buffer.num)
+ *nbuffers = port->minimum_buffer.num;
+- /* Add one buffer to take an EOS */
+- port->current_buffer.num = *nbuffers + 1;
++
++ /*
++ * The VPU uses this number to allocate a pool of headers at port_enable.
++ * We can't increase it later, so use of CREATE_BUFS is going to result
++ * in bad things happening. Adopt worst-case allocation, and add one
++ * buffer to take an EOS
++ */
++ port->current_buffer.num = VB2_MAX_FRAME + 1;
+
+ return 0;
+ }
--- /dev/null
+From c1321370c9af9681e0604c4d3363cf362fb48598 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 16 Sep 2024 15:56:51 +0100
+Subject: [PATCH 1266/1350] drm: vc4: Fix interpolate bit for nearest neighbour
+ filter
+
+Operator precedence resulted in the wrong value being written
+for nearest neighbour mode. Correct it.
+
+Fixes: d6a3a3f10601 ("drm/vc4: Add support for per plane scaling filter selection")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -623,7 +623,7 @@ static void vc4_write_ppf(struct vc4_pla
+ phase &= SCALER_PPF_IPHASE_MASK;
+
+ vc4_dlist_write(vc4_state,
+- no_interpolate ? SCALER_PPF_NOINTERP : 0 |
++ (no_interpolate ? SCALER_PPF_NOINTERP : 0) |
+ SCALER_PPF_AGC |
+ VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
+ /*
--- /dev/null
+From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From f6c0447dfb915538a0d5fa966fc26ca022cf49c8 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 20 Sep 2024 08:25:58 +0100
+Subject: [PATCH 1269/1350] drivers: media: imx500: Fixes for vblank control
+
+Reduce the default/max framerate of the 2x2 binned mode to 30fps.
+The current limit of 50fps can cause the sensor to produce corrupt
+frames and cause missing framing events.
+
+Also fixup the vblank control min/max/default/step paramters when
+setting up.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx500.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/i2c/imx500.c
++++ b/drivers/media/i2c/imx500.c
+@@ -47,7 +47,7 @@
+ /* V_TIMING internal */
+ #define IMX500_REG_FRAME_LENGTH CCI_REG16(0x0340)
+ #define IMX500_FRAME_LENGTH_MAX 0xffdc
+-#define IMX500_VBLANK_MIN 4
++#define IMX500_VBLANK_MIN 1117
+
+ /* H_TIMING internal */
+ #define IMX500_REG_LINE_LENGTH CCI_REG16(0x0342)
+@@ -922,7 +922,7 @@ static const struct imx500_mode imx500_s
+ .width = 4056,
+ .height = 3040,
+ },
+- .framerate_default = 40,
++ .framerate_default = 30,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs),
+ .regs = mode_2028x1520_regs,
+@@ -1771,10 +1771,10 @@ static void imx500_set_framing_limits(st
+
+ /* Update limits and set FPS to default */
+ __v4l2_ctrl_modify_range(
+- imx500->vblank, 1,
++ imx500->vblank, IMX500_VBLANK_MIN,
+ ((1 << IMX500_LONG_EXP_SHIFT_MAX) * IMX500_FRAME_LENGTH_MAX) -
+ mode->height,
+- IMX500_VBLANK_MIN, frm_length_default - mode->height);
++ 1, frm_length_default - mode->height);
+
+ /* Setting this will adjust the exposure limits as well. */
+ __v4l2_ctrl_s_ctrl(imx500->vblank, frm_length_default - mode->height);
--- /dev/null
+From d62184f6b06627b4fe922b08132e38c181d389a0 Mon Sep 17 00:00:00 2001
+From: Jagan Teki <jagan@amarulasolutions.com>
+Date: Tue, 28 Mar 2023 22:37:52 +0530
+Subject: [PATCH 1270/1350] drm/bridge: Document bridge init order with
+ pre_enable_prev_first
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Upstream commit 113cc3ad8566e06d6c8ef4fc0075a938dedefab5
+
+In order to satisfy the MIPI DSI initialization sequence the bridge
+init order has been altered with the help of pre_enable_prev_first
+in pre_enable and post_disable bridge operations.
+
+Document the affected bridge init order with an example on the
+bridge operations helpers.
+
+Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
+Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Robert Foss <rfoss@kernel.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230328170752.1102347-2-jagan@amarulasolutions.com
+---
+ drivers/gpu/drm/drm_bridge.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/drm_bridge.c
++++ b/drivers/gpu/drm/drm_bridge.c
+@@ -657,6 +657,13 @@ static void drm_atomic_bridge_call_post_
+ * bridge will be called before the previous one to reverse the @pre_enable
+ * calling direction.
+ *
++ * Example:
++ * Bridge A ---> Bridge B ---> Bridge C ---> Bridge D ---> Bridge E
++ *
++ * With pre_enable_prev_first flag enable in Bridge B, D, E then the resulting
++ * @post_disable order would be,
++ * Bridge B, Bridge A, Bridge E, Bridge D, Bridge C.
++ *
+ * Note: the bridge passed should be the one closest to the encoder
+ */
+ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
+@@ -753,6 +760,13 @@ static void drm_atomic_bridge_call_pre_e
+ * If a bridge sets @pre_enable_prev_first, then the pre_enable for the
+ * prev bridge will be called before pre_enable of this bridge.
+ *
++ * Example:
++ * Bridge A ---> Bridge B ---> Bridge C ---> Bridge D ---> Bridge E
++ *
++ * With pre_enable_prev_first flag enable in Bridge B, D, E then the resulting
++ * @pre_enable order would be,
++ * Bridge C, Bridge D, Bridge E, Bridge A, Bridge B.
++ *
+ * Note: the bridge passed should be the one closest to the encoder
+ */
+ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
--- /dev/null
+From ef34d0100e0f2f137ad0e6af333aad4e0d5c3319 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 2 May 2024 12:03:58 +0100
+Subject: [PATCH 1271/1350] drm/vc4: dsi: Don't reset the host until
+ post_disable
+
+Some DSI peripheral drivers wish to send commands in the
+post_disable or panel unprepare callback. These are called
+after the DSI host's disable call, but before the host's
+post_disable if pre_enable_prev_first is set.
+
+Don't reset the block until post_disable to allow these
+commands to be sent.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_dsi.c | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_dsi.c
++++ b/drivers/gpu/drm/vc4/vc4_dsi.c
+@@ -818,6 +818,13 @@ static void vc4_dsi_bridge_disable(struc
+ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
+ disp0_ctrl &= ~DSI_DISP0_ENABLE;
+ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
++}
++
++static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
++ struct drm_bridge_state *state)
++{
++ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
++ struct device *dev = &dsi->pdev->dev;
+
+ /* Reset the DSI and all its fifos. */
+ DSI_PORT_WRITE(CTRL, DSI_CTRL_SOFT_RESET_CFG |
+@@ -828,14 +835,6 @@ static void vc4_dsi_bridge_disable(struc
+ DSI_PORT_BIT(PHY_AFEC0_PD) |
+ DSI_PORT_BIT(AFEC0_PD_ALL_LANES));
+
+-}
+-
+-static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
+- struct drm_bridge_state *state)
+-{
+- struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+- struct device *dev = &dsi->pdev->dev;
+-
+ clk_disable_unprepare(dsi->pll_phy_clock);
+ clk_disable_unprepare(dsi->escape_clock);
+ clk_disable_unprepare(dsi->pixel_clock);
--- /dev/null
+From 6da70162dd1e729c04e2dc25472b39390868af79 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Sep 2024 12:05:18 +0100
+Subject: [PATCH 1272/1350] drm: vc4: dsi: enable video and then retry failed
+ transfers
+
+The DSI block appears to be able to come up stuck in a condition where
+it leaves the lanes in HS mode or just jabbering. This stops LP
+transfers from completing as there is no LP time available. This is
+signalled via the LP1 contention error.
+
+Enabling video briefly clears that condition, so if we detect the
+error condition, enable video mode and then retry.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_dsi.c | 54 +++++++++++++++++++++++++++++------
+ 1 file changed, 46 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_dsi.c
++++ b/drivers/gpu/drm/vc4/vc4_dsi.c
+@@ -286,6 +286,8 @@
+ DSI1_INT_PR_TO)
+
+ #define DSI0_STAT 0x2c
++# define DSI0_STAT_ERR_CONT_LP1 BIT(6)
++# define DSI0_STAT_ERR_CONT_LP0 BIT(5)
+ #define DSI0_HSTX_TO_CNT 0x30
+ #define DSI0_LPRX_TO_CNT 0x34
+ #define DSI0_TA_TO_CNT 0x38
+@@ -1203,10 +1205,9 @@ static int vc4_dsi_bridge_attach(struct
+ &dsi->bridge, flags);
+ }
+
+-static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
+- const struct mipi_dsi_msg *msg)
++static ssize_t vc4_dsi_transfer(struct vc4_dsi *dsi,
++ const struct mipi_dsi_msg *msg, bool log_error)
+ {
+- struct vc4_dsi *dsi = host_to_dsi(host);
+ struct mipi_dsi_packet packet;
+ u32 pkth = 0, pktc = 0;
+ int i, ret;
+@@ -1315,10 +1316,12 @@ static ssize_t vc4_dsi_host_transfer(str
+ DSI_PORT_WRITE(TXPKT1C, pktc);
+
+ if (!wait_for_completion_timeout(&dsi->xfer_completion,
+- msecs_to_jiffies(1000))) {
+- dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout");
+- dev_err(&dsi->pdev->dev, "instat: 0x%08x\n",
+- DSI_PORT_READ(INT_STAT));
++ msecs_to_jiffies(500))) {
++ if (log_error) {
++ dev_err(&dsi->pdev->dev, "transfer interrupt wait timeout");
++ dev_err(&dsi->pdev->dev, "instat: 0x%08x, stat: 0x%08x\n",
++ DSI_PORT_READ(INT_STAT), DSI_PORT_READ(INT_STAT));
++ }
+ ret = -ETIMEDOUT;
+ } else {
+ ret = dsi->xfer_result;
+@@ -1361,7 +1364,8 @@ static ssize_t vc4_dsi_host_transfer(str
+ return ret;
+
+ reset_fifo_and_return:
+- DRM_ERROR("DSI transfer failed, resetting: %d\n", ret);
++ if (log_error)
++ DRM_ERROR("DSI transfer failed, resetting: %d\n", ret);
+
+ DSI_PORT_WRITE(TXPKT1C, DSI_PORT_READ(TXPKT1C) & ~DSI_TXPKT1C_CMD_EN);
+ udelay(1);
+@@ -1374,6 +1378,40 @@ reset_fifo_and_return:
+ return ret;
+ }
+
++static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
++ const struct mipi_dsi_msg *msg)
++{
++ struct vc4_dsi *dsi = host_to_dsi(host);
++ u32 stat, disp0_ctrl;
++ int ret;
++
++ ret = vc4_dsi_transfer(dsi, msg, false);
++
++ if (ret == -ETIMEDOUT) {
++ stat = DSI_PORT_READ(STAT);
++ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
++
++ DSI_PORT_WRITE(STAT, DSI_PORT_BIT(STAT_ERR_CONT_LP1));
++ if (!(disp0_ctrl & DSI_DISP0_ENABLE)) {
++ /* If video mode not enabled, then try recovering by
++ * enabling it briefly to clear FIFOs and the state.
++ */
++ disp0_ctrl |= DSI_DISP0_ENABLE;
++ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
++ msleep(30);
++ disp0_ctrl &= ~DSI_DISP0_ENABLE;
++ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
++ msleep(30);
++
++ ret = vc4_dsi_transfer(dsi, msg, true);
++ } else {
++ DRM_ERROR("DSI transfer failed whilst in HS mode stat: 0x%08x\n",
++ stat);
++ }
++ }
++ return ret;
++}
++
+ static const struct component_ops vc4_dsi_ops;
+ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
--- /dev/null
+From d644270369cc6bf39012cce735dde6b86ad01424 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 18 Sep 2024 16:09:28 +0100
+Subject: [PATCH 1273/1350] drm: panel: ili9881: Add option to reconfigure
+ setup commands
+
+The driver is typically asking for LP commands, but then tries
+to send set_display_[on|off] from enable/disable when the host
+will be in HS mode.
+It also sends shutdown commands just before it asserts reset and
+disables the regulator, which is rather redundant.
+
+Add an option to configure these two choices from the panel_desc.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 32 ++++++++++++++++---
+ 1 file changed, 28 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
++++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+@@ -40,12 +40,19 @@ struct ili9881c_instr {
+ } arg;
+ };
+
++enum ili9881_desc_flags {
++ ILI9881_FLAGS_NO_SHUTDOWN_CMDS = BIT(0),
++ ILI9881_FLAGS_PANEL_ON_IN_PREPARE = BIT(1),
++ ILI9881_FLAGS_MAX = BIT(31),
++};
++
+ struct ili9881c_desc {
+ const struct ili9881c_instr *init;
+ const size_t init_length;
+ const struct drm_display_mode *mode;
+ const unsigned long mode_flags;
+ unsigned int lanes;
++ enum ili9881_desc_flags flags;
+ };
+
+ struct ili9881c {
+@@ -1727,6 +1734,12 @@ static int ili9881c_prepare(struct drm_p
+ if (ret)
+ return ret;
+
++ if (ctx->desc->flags & ILI9881_FLAGS_PANEL_ON_IN_PREPARE) {
++ msleep(120);
++
++ ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
++ }
++
+ return 0;
+ }
+
+@@ -1734,9 +1747,11 @@ static int ili9881c_enable(struct drm_pa
+ {
+ struct ili9881c *ctx = panel_to_ili9881c(panel);
+
+- msleep(120);
++ if (!(ctx->desc->flags & ILI9881_FLAGS_PANEL_ON_IN_PREPARE)) {
++ msleep(120);
+
+- mipi_dsi_dcs_set_display_on(ctx->dsi);
++ mipi_dsi_dcs_set_display_on(ctx->dsi);
++ }
+
+ return 0;
+ }
+@@ -1745,7 +1760,8 @@ static int ili9881c_disable(struct drm_p
+ {
+ struct ili9881c *ctx = panel_to_ili9881c(panel);
+
+- mipi_dsi_dcs_set_display_off(ctx->dsi);
++ if (!(ctx->desc->flags & ILI9881_FLAGS_PANEL_ON_IN_PREPARE))
++ mipi_dsi_dcs_set_display_off(ctx->dsi);
+
+ return 0;
+ }
+@@ -1754,7 +1770,13 @@ static int ili9881c_unprepare(struct drm
+ {
+ struct ili9881c *ctx = panel_to_ili9881c(panel);
+
+- mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
++ if (!(ctx->desc->flags & ILI9881_FLAGS_NO_SHUTDOWN_CMDS)) {
++ if (ctx->desc->flags & ILI9881_FLAGS_PANEL_ON_IN_PREPARE)
++ mipi_dsi_dcs_set_display_off(ctx->dsi);
++
++ mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
++ }
++
+ regulator_disable(ctx->power);
+ gpiod_set_value_cansleep(ctx->reset, 1);
+
+@@ -2066,6 +2088,8 @@ static const struct ili9881c_desc rpi_7i
+ .mode = &rpi_7inch_default_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+ .lanes = 2,
++ .flags = ILI9881_FLAGS_NO_SHUTDOWN_CMDS |
++ ILI9881_FLAGS_PANEL_ON_IN_PREPARE,
+ };
+
+ static const struct of_device_id ili9881c_of_match[] = {
--- /dev/null
+From b510e5cbbf8b8e2da3198cf931452290629876b7 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Sep 2024 18:32:11 +0100
+Subject: [PATCH 1274/1350] regulator/rpi-panel: Remove the ID read
+
+Reading from the Atmel has always been troublesome due to
+clock stretching, and the driver does nothing with it anyway.
+
+Remove the read and assume that if the overlay has been
+configured (most likely through the firmware autodetection)
+that the hardware is present.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../regulator/rpi-panel-attiny-regulator.c | 50 -------------------
+ 1 file changed, 50 deletions(-)
+
+--- a/drivers/regulator/rpi-panel-attiny-regulator.c
++++ b/drivers/regulator/rpi-panel-attiny-regulator.c
+@@ -229,39 +229,6 @@ static void attiny_gpio_set(struct gpio_
+ mutex_unlock(&state->lock);
+ }
+
+-static int attiny_i2c_read(struct i2c_client *client, u8 reg, unsigned int *buf)
+-{
+- struct i2c_msg msgs[1];
+- u8 addr_buf[1] = { reg };
+- u8 data_buf[1] = { 0, };
+- int ret;
+-
+- /* Write register address */
+- msgs[0].addr = client->addr;
+- msgs[0].flags = 0;
+- msgs[0].len = ARRAY_SIZE(addr_buf);
+- msgs[0].buf = addr_buf;
+-
+- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+- if (ret != ARRAY_SIZE(msgs))
+- return -EIO;
+-
+- usleep_range(5000, 10000);
+-
+- /* Read data from register */
+- msgs[0].addr = client->addr;
+- msgs[0].flags = I2C_M_RD;
+- msgs[0].len = 1;
+- msgs[0].buf = data_buf;
+-
+- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+- if (ret != ARRAY_SIZE(msgs))
+- return -EIO;
+-
+- *buf = data_buf[0];
+- return 0;
+-}
+-
+ /*
+ * I2C driver interface functions
+ */
+@@ -273,7 +240,6 @@ static int attiny_i2c_probe(struct i2c_c
+ struct regulator_dev *rdev;
+ struct attiny_lcd *state;
+ struct regmap *regmap;
+- unsigned int data;
+ int ret;
+
+ state = devm_kzalloc(&i2c->dev, sizeof(*state), GFP_KERNEL);
+@@ -291,22 +257,6 @@ static int attiny_i2c_probe(struct i2c_c
+ goto error;
+ }
+
+- ret = attiny_i2c_read(i2c, REG_ID, &data);
+- if (ret < 0) {
+- dev_err(&i2c->dev, "Failed to read REG_ID reg: %d\n", ret);
+- goto error;
+- }
+-
+- switch (data) {
+- case 0xde: /* ver 1 */
+- case 0xc3: /* ver 2 */
+- break;
+- default:
+- dev_err(&i2c->dev, "Unknown Atmel firmware revision: 0x%02x\n", data);
+- ret = -ENODEV;
+- goto error;
+- }
+-
+ regmap_write(regmap, REG_POWERON, 0);
+ msleep(30);
+ regmap_write(regmap, REG_PWM, 0);
--- /dev/null
+From 2f9fad762e8ed78aa4220d582b4d1856808f4a7a Mon Sep 17 00:00:00 2001
+From: Charles Mirabile <cmirabil@redhat.com>
+Date: Tue, 19 Apr 2022 16:51:53 -0400
+Subject: [PATCH 1275/1350] drivers/mfd: sensehat: Add Raspberry Pi Sense HAT
+ to simple_mfd_i2c
+
+This patch adds the compatible string for the Sense HAT device to
+the list of compatible strings in the simple_mfd_i2c driver so that
+it can match against the device and load its children and their drivers
+
+Co-developed-by: Mwesigwa Guma <mguma@redhat.com>
+Signed-off-by: Mwesigwa Guma <mguma@redhat.com>
+Co-developed-by: Joel Savitz <jsavitz@redhat.com>
+Signed-off-by: Joel Savitz <jsavitz@redhat.com>
+Signed-off-by: Charles Mirabile <cmirabil@redhat.com>
+---
+ drivers/mfd/simple-mfd-i2c.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mfd/simple-mfd-i2c.c
++++ b/drivers/mfd/simple-mfd-i2c.c
+@@ -98,6 +98,7 @@ static const struct of_device_id simple_
+ { .compatible = "maxim,max5970", .data = &maxim_max5970},
+ { .compatible = "maxim,max5978", .data = &maxim_max5970},
+ { .compatible = "raspberrypi,poe-core", &rpi_poe_core },
++ { .compatible = "raspberrypi,sensehat" },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match);
--- /dev/null
+From 78518a36d7c87bafdbb06478e6b54be5df4b355b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Sep 2024 17:49:00 +0100
+Subject: [PATCH 1281/1350] dtoverlays: Correct pinctrl assignment on mcp23017
+
+The pinctrl definition used "pinctrl-name" which is missing
+an "s" from the end.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/mcp23017-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -34,7 +34,7 @@
+ target = <&mcp23017>;
+ mcp23017_irq: __overlay__ {
+ #interrupt-cells=<2>;
+- pinctrl-name = "default";
++ pinctrl-names = "default";
+ pinctrl-0 = <&mcp23017_pins>;
+ interrupt-parent = <&gpio>;
+ interrupts = <4 2>;
--- /dev/null
+From 0fb3c83a9fa3011cb735ec011b7582d4749957b2 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 20 Sep 2024 12:21:27 +0100
+Subject: [PATCH 1282/1350] tty/serial: pl011: Also unregister
+ pl011_axi_platform_driver
+
+See: https://github.com/raspberrypi/linux/issues/6379
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ 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
+@@ -3122,6 +3122,7 @@ static int __init pl011_init(void)
+ static void __exit pl011_exit(void)
+ {
+ platform_driver_unregister(&arm_sbsa_uart_platform_driver);
++ platform_driver_unregister(&pl011_axi_platform_driver);
+ amba_driver_unregister(&pl011_driver);
+ }
+
--- /dev/null
+From 409a0e2fd41da7a4b04672136aa7d749988faf61 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 25 Sep 2024 16:41:55 +0100
+Subject: [PATCH 1283/1350] overlays: wm8960-soundcard: Fix clock declaration
+
+How did this ever work? The static-clock declaration is fine, but the
+reference to it is attached to part of the simple-audio-card node,
+rather than the instantiation of the codec driver (a subnode of the
+I2C controller).
+
+Move the clock reference where it should be, and fix a few minor
+cosmetic issues at the same time.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../arm/boot/dts/overlays/wm8960-soundcard-overlay.dts | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
+@@ -13,7 +13,7 @@
+ };
+
+ fragment@1 {
+- target-path="/";
++ target-path = "/";
+ __overlay__ {
+ wm8960_mclk: wm8960_mclk {
+ compatible = "fixed-clock";
+@@ -29,17 +29,18 @@
+ #size-cells = <0>;
+ status = "okay";
+
+- wm8960: wm8960 {
++ wm8960: wm8960@1a {
+ compatible = "wlf,wm8960";
+ reg = <0x1a>;
+ #sound-dai-cells = <0>;
+ AVDD-supply = <&vdd_5v0_reg>;
+ DVDD-supply = <&vdd_3v3_reg>;
++ clocks = <&wm8960_mclk>;
++ clock-names = "mclk";
+ };
+ };
+ };
+
+-
+ fragment@3 {
+ target = <&sound>;
+ slave_overlay: __overlay__ {
+@@ -67,10 +68,9 @@
+ simple-audio-card,cpu {
+ sound-dai = <&i2s_clk_producer>;
+ };
++
+ dailink0_slave: simple-audio-card,codec {
+ sound-dai = <&wm8960>;
+- clocks = <&wm8960_mclk>;
+- clock-names = "mclk";
+ };
+ };
+ };
--- /dev/null
+From d52d2bd85ad4d1cfc37a87a6b7bfcc37207c6025 Mon Sep 17 00:00:00 2001
+From: Ali Tekin <140681099+alitekin-saha@users.noreply.github.com>
+Date: Thu, 26 Sep 2024 10:51:13 +0300
+Subject: [PATCH 1284/1350] googlevoicehat: Fix playback muting when recording
+ is stopped
+
+Fixed audio amplifier control logic in the voicehat trigger function
+Resolves improper handling of the SDMODE pin, which caused issues when the microphone and speaker were used simultaneously. The playback stream check is now correctly based on `substream->stream == SNDRV_PCM_STREAM_PLAYBACK`, ensuring proper control of the audio amplifier.
+
+Signed-off-by: Ali Tekin <ali.tekin@saharobotik.com>
+---
+ sound/soc/bcm/googlevoicehat-codec.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/sound/soc/bcm/googlevoicehat-codec.c
++++ b/sound/soc/bcm/googlevoicehat-codec.c
+@@ -95,8 +95,7 @@ static int voicehat_daiops_trigger(struc
+ struct snd_soc_dai *dai)
+ {
+ struct snd_soc_component *component = dai->component;
+- struct voicehat_priv *voicehat =
+- snd_soc_component_get_drvdata(component);
++ struct voicehat_priv *voicehat = snd_soc_component_get_drvdata(component);
+
+ if (voicehat->sdmode_delay_jiffies == 0)
+ return 0;
+@@ -109,7 +108,7 @@ static int voicehat_daiops_trigger(struc
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+- if (dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active) {
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_info(dai->dev, "Enabling audio amp...\n");
+ queue_delayed_work(
+ system_power_efficient_wq,
+@@ -120,7 +119,7 @@ static int voicehat_daiops_trigger(struc
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+- if (dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active) {
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ cancel_delayed_work(&voicehat->enable_sdmode_work);
+ dev_info(dai->dev, "Disabling audio amp...\n");
+ gpiod_set_value(voicehat->sdmode_gpio, 0);
--- /dev/null
+From a382d82b99a078f2ce65e78df9ba6db0d01c8ca3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 24 Sep 2024 20:34:54 +0100
+Subject: [PATCH 1285/1350] ASoC: bcm2835-i2s: Set the PERIOD_BYTES min to 256
+
+Commit [1] set the minimum PERIOD_BYTES value to the product of the DMA
+burst size and 8. For the I2S interface on BCM2835 that equates to 16
+where it used to be 256. ffmpeg uses the minimum value as its preferred
+value, leading to many, many very small periods, which affects
+performance and leads to complaints about DTS timestamps.
+
+Restore the previous behaviour and performance by making the bcm2835-i2s
+driver set a minimum PERIOD_BYTES value of 256.
+
+Link: https://github.com/raspberrypi/linux/issues/5709
+
+[1] 300689fb04b3 ("ASoC: soc-generic-dmaengine-pcm: set
+period_bytes_min based on maxburst")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/bcm2835-i2s.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/sound/soc/bcm/bcm2835-i2s.c
++++ b/sound/soc/bcm/bcm2835-i2s.c
+@@ -619,6 +619,10 @@ static int bcm2835_i2s_prepare(struct sn
+ struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ uint32_t cs_reg;
+
++ snd_pcm_hw_constraint_minmax(substream->runtime,
++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 256,
++ ~0);
++
+ /*
+ * Clear both FIFOs if the one that should be started
+ * is not empty at the moment. This should only happen
+@@ -700,6 +704,10 @@ static int bcm2835_i2s_startup(struct sn
+ /* Should this still be running stop it */
+ bcm2835_i2s_stop_clock(dev);
+
++ snd_pcm_hw_constraint_minmax(substream->runtime,
++ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
++ 256, ~0);
++
+ /* Enable PCM block */
+ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
+ BCM2835_I2S_EN, BCM2835_I2S_EN);
--- /dev/null
+From d290bef1f99e24cd590c64db149d007c6a631b65 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 16 Sep 2024 11:48:08 +0100
+Subject: [PATCH 1286/1350] clk: clk-rp1: Don't crash on duplicate clocks
+
+When using DTBs that don't match the kernel version, it's possible for
+clock registration to fail. Handle that failure, in the hope that the
+system can continue to boot, with a suitable error message.
+
+Link: https://github.com/raspberrypi/linux/issues/6321
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/clk/clk-rp1.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/clk/clk-rp1.c
++++ b/drivers/clk/clk-rp1.c
+@@ -2454,6 +2454,11 @@ static int rp1_clk_probe(struct platform
+ desc = &clk_desc_array[i];
+ if (desc->clk_register && desc->data) {
+ hws[i] = desc->clk_register(clockman, desc->data);
++ if (IS_ERR_OR_NULL(hws[i])) {
++ pr_err("Failed to register RP1 clock '%s' (%ld) - wrong dtbs?\n", *(char **)desc->data, PTR_ERR(hws[i]));
++ hws[i] = NULL;
++ continue;
++ }
+ if (!strcmp(clk_hw_get_name(hws[i]), "clk_i2s")) {
+ clk_i2s = hws[i];
+ clk_xosc = clk_hw_get_parent_by_index(clk_i2s, 0);
--- /dev/null
+From 0094eba3f2a4338cfa6854b0b5104d02ba0fa01f Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 26 Sep 2024 13:12:23 +0100
+Subject: [PATCH 1287/1350] drivers: media: imx500: Simplify the vblank control
+ init
+
+Set the VBLANK control minimum and default values to IMX500_VBLANK_MIN
+unconditionally everywhere.
+
+Remove the mode specific framerate_default parameter, it is now unused.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx500.c | 32 +++++---------------------------
+ 1 file changed, 5 insertions(+), 27 deletions(-)
+
+--- a/drivers/media/i2c/imx500.c
++++ b/drivers/media/i2c/imx500.c
+@@ -274,9 +274,6 @@ struct imx500_mode {
+ /* Analog crop rectangle. */
+ struct v4l2_rect crop;
+
+- /* Default framerate. */
+- unsigned int framerate_default;
+-
+ /* Default register values */
+ struct imx500_reg_list reg_list;
+ };
+@@ -905,7 +902,6 @@ static const struct imx500_mode imx500_s
+ .width = 4056,
+ .height = 3040,
+ },
+- .framerate_default = 10,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_4056x3040_regs),
+ .regs = mode_4056x3040_regs,
+@@ -922,7 +918,6 @@ static const struct imx500_mode imx500_s
+ .width = 4056,
+ .height = 3040,
+ },
+- .framerate_default = 30,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2028x1520_regs),
+ .regs = mode_2028x1520_regs,
+@@ -1744,28 +1739,11 @@ static int imx500_get_pad_format(struct
+ return 0;
+ }
+
+-static unsigned int imx500_get_frame_length(const struct imx500_mode *mode,
+- unsigned int framerate_default)
+-{
+- u64 frame_length;
+-
+- frame_length = IMX500_PIXEL_RATE;
+- do_div(frame_length, (u64)framerate_default * mode->line_length_pix);
+-
+- if (WARN_ON(frame_length > IMX500_FRAME_LENGTH_MAX))
+- frame_length = IMX500_FRAME_LENGTH_MAX;
+-
+- return max_t(unsigned int, frame_length, mode->height);
+-}
+-
+ static void imx500_set_framing_limits(struct imx500 *imx500)
+ {
+- unsigned int frm_length_default, hblank_min;
++ unsigned int hblank_min;
+ const struct imx500_mode *mode = imx500->mode;
+
+- frm_length_default =
+- imx500_get_frame_length(mode, mode->framerate_default);
+-
+ /* Default to no long exposure multiplier. */
+ imx500->long_exp_shift = 0;
+
+@@ -1773,11 +1751,10 @@ static void imx500_set_framing_limits(st
+ __v4l2_ctrl_modify_range(
+ imx500->vblank, IMX500_VBLANK_MIN,
+ ((1 << IMX500_LONG_EXP_SHIFT_MAX) * IMX500_FRAME_LENGTH_MAX) -
+- mode->height,
+- 1, frm_length_default - mode->height);
++ mode->height, 1, IMX500_VBLANK_MIN);
+
+ /* Setting this will adjust the exposure limits as well. */
+- __v4l2_ctrl_s_ctrl(imx500->vblank, frm_length_default - mode->height);
++ __v4l2_ctrl_s_ctrl(imx500->vblank, IMX500_VBLANK_MIN);
+
+ hblank_min = mode->line_length_pix - mode->width;
+ __v4l2_ctrl_modify_range(imx500->hblank, hblank_min, hblank_min, 1,
+@@ -2499,7 +2476,8 @@ static int imx500_init_controls(struct i
+ * in the imx500_set_framing_limits() call below.
+ */
+ imx500->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
+- V4L2_CID_VBLANK, 0, 0xffff, 1, 0);
++ V4L2_CID_VBLANK, IMX500_VBLANK_MIN,
++ 0xffff, 1, IMX500_VBLANK_MIN);
+ imx500->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx500_ctrl_ops,
+ V4L2_CID_HBLANK, 0, 0xffff, 1, 0);
+
--- /dev/null
+From 9557336c4fc4ac4606ac9e78c239aa689c26e870 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 26 Sep 2024 17:46:25 +0100
+Subject: [PATCH 1288/1350] dts: overlay_map: ramoops-pi4 works on Pi 5
+
+Add the necessary overlay_map entries so that ramoops-pi4 is loaded
+with dtoverlay=ramoops on Pi 5.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/overlay_map.dts | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -247,10 +247,12 @@
+ ramoops {
+ bcm2835;
+ bcm2711 = "ramoops-pi4";
++ bcm2712 = "ramoops-pi4";
+ };
+
+ ramoops-pi4 {
+ bcm2711;
++ bcm2712;
+ };
+
+ rpi-cirrus-wm5102 {
--- /dev/null
+From 2b5de12af9bb390239d5f3385c49e0c34f335de8 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 27 Sep 2024 10:59:02 +0100
+Subject: [PATCH 1289/1350] dts: align PCI BAR allocation on bcm2711 and
+ bcm2712 to start at 2GB
+
+Fold the Pi 5 mmio-hi compatibility option into the base DTB, and
+shuffle the single MMIO window on bcm2711 to match.
+
+Certain devices cannot handle low addresses, e.g. by failing to
+enumerate or failing to route the traffic appropriately.
+
+Link: https://github.com/raspberrypi/linux/issues/6134
+Link: https://github.com/raspberrypi/linux/issues/6278
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ .../arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 6 +++---
+ arch/arm/boot/dts/overlays/README | 2 --
+ .../overlays/pciex1-compat-pi5-overlay.dts | 20 -------------------
+ arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 10 ++++++----
+ 4 files changed, 9 insertions(+), 29 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
+@@ -123,7 +123,7 @@
+
+ ranges = <0x0 0x7c000000 0x0 0xfc000000 0x0 0x03800000>,
+ <0x0 0x40000000 0x0 0xff800000 0x0 0x00800000>,
+- <0x6 0x00000000 0x6 0x00000000 0x0 0x40000000>,
++ <0x6 0x00000000 0x6 0x00000000 0x0 0x80000000>,
+ <0x0 0x00000000 0x0 0x00000000 0x0 0xfc000000>;
+ dma-ranges = <0x4 0x7c000000 0x0 0xfc000000 0x0 0x03800000>,
+ <0x0 0x00000000 0x0 0x00000000 0x4 0x00000000>;
+@@ -167,8 +167,8 @@
+
+ &pcie0 {
+ reg = <0x0 0x7d500000 0x0 0x9310>;
+- ranges = <0x02000000 0x0 0xc0000000 0x6 0x00000000
+- 0x0 0x40000000>;
++ ranges = <0x02000000 0x0 0x80000000 0x6 0x00000000
++ 0x0 0x80000000>;
+ };
+
+ &genet {
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3617,8 +3617,6 @@ Params: l1ss Enable A
+ the MSI-MIP peripheral. Use if a) more than 8
+ interrupt vectors are required or b) the EP
+ requires DMA and MSI addresses to be 32bit.
+- mmio-hi Move the start of outbound 32bit addresses to
+- 2GB and expand 64bit outbound space to 14GB.
+
+
+ [ The pcf2127-rtc overlay has been deleted. See i2c-rtc. ]
+--- a/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts
+@@ -32,29 +32,9 @@
+ };
+ };
+
+- /*
+- * Shift the start of the 32bit outbound window to 2GB,
+- * so there are no BARs starting at 0x0. Expand the 64bit
+- * outbound window to use the spare 2GB.
+- */
+- fragment@3 {
+- target = <&pciex1>;
+- __dormant__ {
+- #address-cells = <3>;
+- #size-cells = <2>;
+- ranges = <0x02000000 0x00 0x80000000
+- 0x1b 0x80000000
+- 0x00 0x7ffffffc>,
+- <0x43000000 0x04 0x00000000
+- 0x18 0x00000000
+- 0x03 0x80000000>;
+- };
+- };
+-
+ __overrides__ {
+ l1ss = <0>, "+0";
+ no-l0s = <0>, "+1";
+ no-mip = <0>, "+2";
+- mmio-hi = <0>, "+3";
+ };
+ };
+--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+@@ -1052,12 +1052,14 @@
+ msi-controller;
+ msi-parent = <&mip1>;
+
+- ranges = <0x02000000 0x00 0x00000000
+- 0x1b 0x00000000
+- 0x00 0xfffffffc>,
++ // 2GB, 32-bit, non-prefetchable at PCIe 00_80000000
++ ranges = <0x02000000 0x00 0x80000000
++ 0x1b 0x80000000
++ 0x00 0x80000000>,
++ // 14GB, 64-bit, prefetchable at PCIe 04_00000000
+ <0x43000000 0x04 0x00000000
+ 0x18 0x00000000
+- 0x03 0x00000000>;
++ 0x03 0x80000000>;
+
+ dma-ranges = <0x03000000 0x10 0x00000000
+ 0x00 0x00000000
--- /dev/null
+From 35b3f98bb2bfb80a718c52ba49c167da6c78d2ea Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20Gapi=C5=84ski?=
+ <mikegapinski@users.noreply.github.com>
+Date: Mon, 30 Sep 2024 16:47:20 +0200
+Subject: [PATCH 1290/1350] overlays: Add Pineboards HatDrive! POE+ (#6257)
+
+overlays: Add Pineboards HatDrive! POE+
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 ++++++
+ .../pineboards-hatdrive-poe-plus-overlay.dts | 19 +++++++++++++++++++
+ 3 files changed, 26 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pineboards-hatdrive-poe-plus-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -204,6 +204,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ pifi-mini-210.dtbo \
+ piglow.dtbo \
+ pineboards-hat-ai.dtbo \
++ pineboards-hatdrive-poe-plus.dtbo \
+ piscreen.dtbo \
+ piscreen2r.dtbo \
+ pisound.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3703,6 +3703,12 @@ Load: dtoverlay=pineboards-hat-ai
+ Params: <None>
+
+
++Name: pineboards-hatdrive-poe-plus
++Info: Configures the Pineboards HatDrive! PoE+
++Load: dtoverlay=pineboards-hatdrive-poe-plus
++Params: <None>
++
++
+ Name: piscreen
+ Info: PiScreen display by OzzMaker.com
+ Load: dtoverlay=piscreen,<param>=<val>
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pineboards-hatdrive-poe-plus-overlay.dts
+@@ -0,0 +1,19 @@
++/*
++ * Device Tree overlay for Pineboards HatDrive! PoE+.
++ */
++
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/chosen";
++ __overlay__ {
++ power: power {
++ hat_current_supply = <5000>;
++ };
++ };
++ };
++};
--- /dev/null
+From 50a6e5cf28a24e7f4192ad6f70f472eca2097cc6 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 2 Oct 2024 16:36:45 +0100
+Subject: [PATCH 1296/1350] dtoverlays: Correct vc4-kms-dpi-generic for
+ [width|height]-mm
+
+These two overrides were updating the &panel node from
+vc4-kms-dpi.dtsi, when fragment0 from the vc4-kms-dpi-generic
+was also updating the same node. Application order meant that
+the override value was overwritten.
+
+Correct the target.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
+@@ -60,8 +60,8 @@
+ de-invert = <&timing>, "de-active:0=0";
+ pixclk-invert = <&timing>, "pixelclk-active:0=0";
+
+- width-mm = <&panel>, "width-mm:0";
+- height-mm = <&panel>, "height-mm:0";
++ width-mm = <&panel_generic>, "width-mm:0";
++ height-mm = <&panel_generic>, "height-mm:0";
+
+ rgb565 = <&panel_generic>, "bus-format:0=0x1017",
+ <&dpi_node_generic>, "pinctrl-0:0=",<&dpi_16bit_gpio0>;
--- /dev/null
+From e044f6af1b2b41bc2212551b4fd6353469cb7263 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 12 Sep 2024 16:29:57 +0100
+Subject: [PATCH 1297/1350] dts: bcm2712-rpi: Add four missing GPIOs to 2712D0
+
+It's useful for gpioinfo and pinctrl to distinguish between unused and
+absent GPIOs. We use "-" for the former and "" for the latter.
+With this convention, gpioinfo shows absent GPIOs as "unnamed", while
+pinctrl omits them altogether.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts
+@@ -15,10 +15,10 @@
+ "", // GPIO_007
+ "", // GPIO_008
+ "", // GPIO_009
+- "", // GPIO_010
+- "", // GPIO_011
+- "", // GPIO_012
+- "", // GPIO_013
++ "-", // GPIO_010
++ "-", // GPIO_011
++ "-", // GPIO_012
++ "-", // GPIO_013
+ "PCIE_SDA", // GPIO_014
+ "PCIE_SCL", // GPIO_015
+ "", // GPIO_016
--- /dev/null
+From 3be1a52ad9e3ae7b0e16eb20c77d9df60e29f139 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 10 Sep 2024 14:07:16 +0100
+Subject: [PATCH 1298/1350] arm64: dts: Add preliminary bcm2712-rpi-500 dts
+
+Add the first DTS file for Pi 500.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/Makefile | 1 +
+ .../boot/dts/broadcom/bcm2712-rpi-500.dts | 142 ++++++++++++++++++
+ 2 files changed, 143 insertions(+)
+ create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
+
+--- a/arch/arm64/boot/dts/broadcom/Makefile
++++ b/arch/arm64/boot/dts/broadcom/Makefile
+@@ -22,6 +22,7 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rp
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4s.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-5-b.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2712d0-rpi-5-b.dtb
++dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-500.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-cm5-cm5io.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-cm5-cm4io.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-cm5l-cm5io.dtb
+--- /dev/null
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
+@@ -0,0 +1,142 @@
++// SPDX-License-Identifier: GPL-2.0
++#include "bcm2712d0-rpi-5-b.dts"
++
++/ {
++ compatible = "raspberrypi,500", "brcm,bcm2712";
++ model = "Raspberry Pi 500";
++};
++
++&pwr_key {
++ debounce-interval = <400>;
++};
++
++&gio {
++ gpio-line-names =
++ "", // GPIO_000
++ "2712_BOOT_CS_N", // GPIO_001
++ "2712_BOOT_MISO", // GPIO_002
++ "2712_BOOT_MOSI", // GPIO_003
++ "2712_BOOT_SCLK", // GPIO_004
++ "", // GPIO_005
++ "", // GPIO_006
++ "", // GPIO_007
++ "", // GPIO_008
++ "", // GPIO_009
++ "-", // GPIO_010
++ "-", // GPIO_011
++ "-", // GPIO_012
++ "-", // GPIO_013
++ "M2_DET_WAKE", // GPIO_014
++ "M2_PWR_EN", // GPIO_015
++ "", // GPIO_016
++ "", // GPIO_017
++ "KEYB_BOOTSEL", // GPIO_018
++ "-", // GPIO_019
++ "PWR_GPIO", // GPIO_020
++ "KEYB_RUN", // GPIO_021
++ "-", // GPIO_022
++ "-", // GPIO_023
++ "BT_RTS", // GPIO_024
++ "BT_CTS", // GPIO_025
++ "BT_TXD", // GPIO_026
++ "BT_RXD", // GPIO_027
++ "WL_ON", // GPIO_028
++ "BT_ON", // GPIO_029
++ "WIFI_SDIO_CLK", // GPIO_030
++ "WIFI_SDIO_CMD", // GPIO_031
++ "WIFI_SDIO_D0", // GPIO_032
++ "WIFI_SDIO_D1", // GPIO_033
++ "WIFI_SDIO_D2", // GPIO_034
++ "WIFI_SDIO_D3"; // GPIO_035
++};
++
++&gio_aon {
++ gpio-line-names =
++ "RP1_SDA", // AON_GPIO_00
++ "RP1_SCL", // AON_GPIO_01
++ "RP1_RUN", // AON_GPIO_02
++ "SD_IOVDD_SEL", // AON_GPIO_03
++ "SD_PWR_ON", // AON_GPIO_04
++ "SD_CDET_N", // AON_GPIO_05
++ "SD_FLG_N", // AON_GPIO_06
++ "", // AON_GPIO_07
++ "2712_WAKE", // AON_GPIO_08
++ "2712_STAT_LED", // AON_GPIO_09
++ "", // AON_GPIO_10
++ "", // AON_GPIO_11
++ "PMIC_INT", // AON_GPIO_12
++ "UART_TX_FS", // AON_GPIO_13
++ "UART_RX_FS", // AON_GPIO_14
++ "", // AON_GPIO_15
++ "", // AON_GPIO_16
++
++ // Pad bank0 out to 32 entries
++ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
++
++ "HDMI0_SCL", // AON_SGPIO_00
++ "HDMI0_SDA", // AON_SGPIO_01
++ "HDMI1_SCL", // AON_SGPIO_02
++ "HDMI1_SDA", // AON_SGPIO_03
++ "PMIC_SCL", // AON_SGPIO_04
++ "PMIC_SDA"; // AON_SGPIO_05
++};
++
++&rp1_gpio {
++ gpio-line-names =
++ "ID_SDA", // GPIO0
++ "ID_SCL", // GPIO1
++ "GPIO2", // GPIO2
++ "GPIO3", // GPIO3
++ "GPIO4", // GPIO4
++ "GPIO5", // GPIO5
++ "GPIO6", // GPIO6
++ "GPIO7", // GPIO7
++ "GPIO8", // GPIO8
++ "GPIO9", // GPIO9
++ "GPIO10", // GPIO10
++ "GPIO11", // GPIO11
++ "GPIO12", // GPIO12
++ "GPIO13", // GPIO13
++ "GPIO14", // GPIO14
++ "GPIO15", // GPIO15
++ "GPIO16", // GPIO16
++ "GPIO17", // GPIO17
++ "GPIO18", // GPIO18
++ "GPIO19", // GPIO19
++ "GPIO20", // GPIO20
++ "GPIO21", // GPIO21
++ "GPIO22", // GPIO22
++ "GPIO23", // GPIO23
++ "GPIO24", // GPIO24
++ "GPIO25", // GPIO25
++ "GPIO26", // GPIO26
++ "GPIO27", // GPIO27
++
++ "PCIE_RP1_WAKE", // GPIO28
++ "-", // GPIO29
++ "HOST_SDA", // GPIO30
++ "HOST_SCL", // GPIO31
++ "ETH_RST_N", // GPIO32
++ "PCIE_DET_WAKE", // GPIO33
++
++ "-", // GPIO34
++ "-", // GPIO35
++ "RP1_PCIE_CLKREQ_N", // GPIO36
++ "-", // GPIO37
++ "-", // GPIO38
++ "-", // GPIO39
++ "CD1_SDA", // GPIO40
++ "CD1_SCL", // GPIO41
++ "USB_VBUS_EN", // GPIO42
++ "USB_OC_N", // GPIO43
++ "RP1_STAT_LED", // GPIO44
++ "-", // GPIO45
++ "-", // GPIO46
++ "HOST_WAKE", // GPIO47
++ "-", // GPIO48
++ "EN_MAX_USB_CUR", // GPIO49
++ "-", // GPIO50
++ "-", // GPIO51
++ "-", // GPIO52
++ "-"; // GPIO53
++};
--- /dev/null
+From 3edaa3875fbeb0b2effd77c62baabf2933efc6ef Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 13 Sep 2024 17:23:58 +0100
+Subject: [PATCH 1301/1350] dts: 2712: Reduce default cma usage on Pi5
+
+Significant cma shouldn't really be needed on Pi5 as the hardware
+blocks support iommu and can access system memory.
+
+We've migrated codec and camera support to system memory, and 3d
+has always (even on Pi4) used system memory.
+
+A large cma block causes issues with enabling NUMA on a low
+memory (2G) Pi5, as cma cannot span numa regions.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
+@@ -3,7 +3,7 @@
+ #include "cma-overlay.dts"
+
+ &frag0 {
+- size = <((320-4)*1024*1024)>;
++ size = <(64*1024*1024)>;
+ };
+
+ / {
--- /dev/null
+From d69bca1f0425be1a42f7ad1e3cff5313ba7c122a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 9 Oct 2024 15:13:14 +0100
+Subject: [PATCH 1305/1350] dts: bcm2712d0: Add non-d0 vc6 compatible string
+
+Although the VC4/VC6 driver requires a special compatible string for the
+"d0" stepping, the removal of the old compatible string upsets Mesa.
+
+Satisfy both requirements by adding the old "brcm,bcm2712-vc6" string
+as a fallback.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/bcm2712d0-overlay.dts | 2 +-
+ arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/bcm2712d0-overlay.dts
++++ b/arch/arm/boot/dts/overlays/bcm2712d0-overlay.dts
+@@ -41,7 +41,7 @@
+ fragment@4 {
+ target = <&vc4>;
+ __overlay__ {
+- compatible = "brcm,bcm2712d0-vc6";
++ compatible = "brcm,bcm2712d0-vc6", "brcm,bcm2712-vc6";
+ };
+ };
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712d0-rpi-5-b.dts
+@@ -87,7 +87,7 @@
+ };
+
+ &vc4 {
+- compatible = "brcm,bcm2712d0-vc6";
++ compatible = "brcm,bcm2712d0-vc6", "brcm,bcm2712-vc6";
+ };
+
+ &uart10 {
--- /dev/null
+From 9a86952570b925e68dfd30a12642000353242745 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 10 Oct 2024 17:16:08 +0100
+Subject: [PATCH 1312/1350] Reapply "dtoverlays: Convert SenseHAT overlays to
+ use MFD and upstream drivers"
+
+This reverts commit 82a50e430ef1d6eb37d78e25aa572c1f6ea56160.
+---
+ .../boot/dts/overlays/rpi-sense-overlay.dts | 28 +++++++++++++++++--
+ .../dts/overlays/rpi-sense-v2-overlay.dts | 28 +++++++++++++++++--
+ 2 files changed, 50 insertions(+), 6 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpi-sense-overlay.dts
+@@ -12,11 +12,23 @@
+ #size-cells = <0>;
+ status = "okay";
+
+- rpi-sense@46 {
+- compatible = "rpi,rpi-sense";
++ sensehat@46 {
++ compatible = "raspberrypi,sensehat";
+ reg = <0x46>;
+- keys-int-gpios = <&gpio 23 1>;
++ interrupt-parent = <&gpio>;
+ status = "okay";
++
++ display {
++ compatible = "raspberrypi,rpi-sense-fb";
++ status = "okay";
++ };
++ joystick {
++ compatible = "raspberrypi,sensehat-joystick";
++ interrupts = <23 1>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sensehat_pins>;
++ status = "okay";
++ };
+ };
+
+ lsm9ds1-magn@1c {
+@@ -44,4 +56,14 @@
+ };
+ };
+ };
++
++ fragment@1 {
++ target = <&gpio>;
++ __overlay__ {
++ sensehat_pins: sensehat_pins {
++ brcm,pins = <23>;
++ brcm,function = <0>;
++ };
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/rpi-sense-v2-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpi-sense-v2-overlay.dts
+@@ -12,11 +12,23 @@
+ #size-cells = <0>;
+ status = "okay";
+
+- rpi-sense@46 {
+- compatible = "rpi,rpi-sense";
++ sensehat@46 {
++ compatible = "raspberrypi,sensehat";
+ reg = <0x46>;
+- keys-int-gpios = <&gpio 23 1>;
++ interrupt-parent = <&gpio>;
+ status = "okay";
++
++ display {
++ compatible = "raspberrypi,rpi-sense-fb";
++ status = "okay";
++ };
++ joystick {
++ compatible = "raspberrypi,sensehat-joystick";
++ interrupts = <23 1>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&sensehat_pins>;
++ status = "okay";
++ };
+ };
+
+ lsm9ds1-magn@1c {
+@@ -44,4 +56,14 @@
+ };
+ };
+ };
++
++ fragment@1 {
++ target = <&gpio>;
++ __overlay__ {
++ sensehat_pins: sensehat_pins {
++ brcm,pins = <23>;
++ brcm,function = <0>;
++ };
++ };
++ };
+ };
--- /dev/null
+From 9fb57f6b8920a8aceb74ceb3e171a7e5769205a5 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 10 Oct 2024 17:18:39 +0100
+Subject: [PATCH 1313/1350] Reapply "drivers: Remove downstream SenseHAT core
+ and joystick drivers"
+
+This reverts commit e6493e2d6a1f572fbb4a1d724e54715cb748b424.
+---
+ drivers/input/joystick/Kconfig | 8 --
+ drivers/input/joystick/Makefile | 1 -
+ drivers/input/joystick/rpisense-js.c | 153 ---------------------
+ drivers/mfd/Kconfig | 8 --
+ drivers/mfd/Makefile | 1 -
+ drivers/mfd/rpisense-core.c | 163 -----------------------
+ drivers/video/fbdev/Kconfig | 4 +-
+ drivers/video/fbdev/rpisense-fb.c | 53 ++++----
+ include/linux/mfd/rpisense/core.h | 47 -------
+ include/linux/mfd/rpisense/framebuffer.h | 5 +-
+ 10 files changed, 32 insertions(+), 411 deletions(-)
+ delete mode 100644 drivers/input/joystick/rpisense-js.c
+ delete mode 100644 drivers/mfd/rpisense-core.c
+ delete mode 100644 include/linux/mfd/rpisense/core.h
+
+--- a/drivers/input/joystick/Kconfig
++++ b/drivers/input/joystick/Kconfig
+@@ -412,12 +412,4 @@ config JOYSTICK_SENSEHAT
+ To compile this driver as a module, choose M here: the
+ module will be called sensehat_joystick.
+
+-config JOYSTICK_RPISENSE
+- tristate "Raspberry Pi Sense HAT joystick"
+- depends on GPIOLIB && INPUT
+- select MFD_RPISENSE_CORE
+-
+- help
+- This is the joystick driver for the Raspberry Pi Sense HAT
+-
+ endif
+--- a/drivers/input/joystick/Makefile
++++ b/drivers/input/joystick/Makefile
+@@ -40,4 +40,3 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warri
+ obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
+ obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
+ obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
+-obj-$(CONFIG_JOYSTICK_RPISENSE) += rpisense-js.o
+--- a/drivers/input/joystick/rpisense-js.c
++++ /dev/null
+@@ -1,153 +0,0 @@
+-/*
+- * Raspberry Pi Sense HAT joystick driver
+- * http://raspberrypi.org
+- *
+- * Copyright (C) 2015 Raspberry Pi
+- *
+- * Author: Serge Schneider
+- *
+- * 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; either version 2 of the License, or (at your
+- * option) any later version.
+- *
+- */
+-
+-#include <linux/module.h>
+-
+-#include <linux/mfd/rpisense/joystick.h>
+-#include <linux/mfd/rpisense/core.h>
+-
+-static struct rpisense *rpisense;
+-static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
+-
+-static void keys_work_fn(struct work_struct *work)
+-{
+- int i;
+- static s32 prev_keys;
+- struct rpisense_js *rpisense_js = &rpisense->joystick;
+- s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS);
+- s32 changes = keys ^ prev_keys;
+-
+- prev_keys = keys;
+- for (i = 0; i < 5; i++) {
+- if (changes & 1) {
+- input_report_key(rpisense_js->keys_dev,
+- keymap[i], keys & 1);
+- }
+- changes >>= 1;
+- keys >>= 1;
+- }
+- input_sync(rpisense_js->keys_dev);
+-}
+-
+-static irqreturn_t keys_irq_handler(int irq, void *pdev)
+-{
+- struct rpisense_js *rpisense_js = &rpisense->joystick;
+-
+- schedule_work(&rpisense_js->keys_work_s);
+- return IRQ_HANDLED;
+-}
+-
+-static int rpisense_js_probe(struct platform_device *pdev)
+-{
+- int ret;
+- int i;
+- struct rpisense_js *rpisense_js;
+-
+- rpisense = rpisense_get_dev();
+- rpisense_js = &rpisense->joystick;
+-
+- INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn);
+-
+- rpisense_js->keys_dev = input_allocate_device();
+- if (!rpisense_js->keys_dev) {
+- dev_err(&pdev->dev, "Could not allocate input device.\n");
+- return -ENOMEM;
+- }
+-
+- rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY);
+- for (i = 0; i < ARRAY_SIZE(keymap); i++) {
+- set_bit(keymap[i],
+- rpisense_js->keys_dev->keybit);
+- }
+-
+- rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
+- rpisense_js->keys_dev->phys = "rpi-sense-joy/input0";
+- rpisense_js->keys_dev->id.bustype = BUS_I2C;
+- rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+- rpisense_js->keys_dev->keycode = keymap;
+- rpisense_js->keys_dev->keycodesize = sizeof(unsigned char);
+- rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap);
+-
+- ret = input_register_device(rpisense_js->keys_dev);
+- if (ret) {
+- dev_err(&pdev->dev, "Could not register input device.\n");
+- goto err_keys_alloc;
+- }
+-
+- ret = gpiod_direction_input(rpisense_js->keys_desc);
+- if (ret) {
+- dev_err(&pdev->dev, "Could not set keys-int direction.\n");
+- goto err_keys_reg;
+- }
+-
+- rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc);
+- if (rpisense_js->keys_irq < 0) {
+- dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n");
+- ret = rpisense_js->keys_irq;
+- goto err_keys_reg;
+- }
+-
+- ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq,
+- keys_irq_handler, IRQF_TRIGGER_RISING,
+- "keys", &pdev->dev);
+- if (ret) {
+- dev_err(&pdev->dev, "IRQ request failed.\n");
+- goto err_keys_reg;
+- }
+- return 0;
+-err_keys_reg:
+- input_unregister_device(rpisense_js->keys_dev);
+-err_keys_alloc:
+- input_free_device(rpisense_js->keys_dev);
+- return ret;
+-}
+-
+-static int rpisense_js_remove(struct platform_device *pdev)
+-{
+- struct rpisense_js *rpisense_js = &rpisense->joystick;
+-
+- input_unregister_device(rpisense_js->keys_dev);
+- input_free_device(rpisense_js->keys_dev);
+- return 0;
+-}
+-
+-#ifdef CONFIG_OF
+-static const struct of_device_id rpisense_js_id[] = {
+- { .compatible = "rpi,rpi-sense-js" },
+- { },
+-};
+-MODULE_DEVICE_TABLE(of, rpisense_js_id);
+-#endif
+-
+-static struct platform_device_id rpisense_js_device_id[] = {
+- { .name = "rpi-sense-js" },
+- { },
+-};
+-MODULE_DEVICE_TABLE(platform, rpisense_js_device_id);
+-
+-static struct platform_driver rpisense_js_driver = {
+- .probe = rpisense_js_probe,
+- .remove = rpisense_js_remove,
+- .driver = {
+- .name = "rpi-sense-js",
+- .owner = THIS_MODULE,
+- },
+-};
+-
+-module_platform_driver(rpisense_js_driver);
+-
+-MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+-MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+-MODULE_LICENSE("GPL");
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -11,14 +11,6 @@ config MFD_CORE
+ select IRQ_DOMAIN
+ default n
+
+-config MFD_RPISENSE_CORE
+- tristate "Raspberry Pi Sense HAT core functions"
+- depends on I2C
+- select MFD_CORE
+- help
+- This is the core driver for the Raspberry Pi Sense HAT. This provides
+- the necessary functions to communicate with the hardware.
+-
+ config MFD_CS5535
+ tristate "AMD CS5535 and CS5536 southbridge core functions"
+ select MFD_CORE
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -268,7 +268,6 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o
+ obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
+ obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
+ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o
+-obj-$(CONFIG_MFD_RPISENSE_CORE) += rpisense-core.o
+
+ obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
+ obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
+--- a/drivers/mfd/rpisense-core.c
++++ /dev/null
+@@ -1,163 +0,0 @@
+-/*
+- * Raspberry Pi Sense HAT core driver
+- * http://raspberrypi.org
+- *
+- * Copyright (C) 2015 Raspberry Pi
+- *
+- * Author: Serge Schneider
+- *
+- * 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; either version 2 of the License, or (at your
+- * option) any later version.
+- *
+- * This driver is based on wm8350 implementation.
+- */
+-
+-#include <linux/module.h>
+-#include <linux/moduleparam.h>
+-#include <linux/err.h>
+-#include <linux/init.h>
+-#include <linux/i2c.h>
+-#include <linux/platform_device.h>
+-#include <linux/mfd/rpisense/core.h>
+-#include <linux/slab.h>
+-
+-static struct rpisense *rpisense;
+-
+-static void rpisense_client_dev_register(struct rpisense *rpisense,
+- const char *name,
+- struct platform_device **pdev)
+-{
+- int ret;
+-
+- *pdev = platform_device_alloc(name, -1);
+- if (*pdev == NULL) {
+- dev_err(rpisense->dev, "Failed to allocate %s\n", name);
+- return;
+- }
+-
+- (*pdev)->dev.parent = rpisense->dev;
+- platform_set_drvdata(*pdev, rpisense);
+- ret = platform_device_add(*pdev);
+- if (ret != 0) {
+- dev_err(rpisense->dev, "Failed to register %s: %d\n",
+- name, ret);
+- platform_device_put(*pdev);
+- *pdev = NULL;
+- }
+-}
+-
+-static int rpisense_probe(struct i2c_client *i2c)
+-{
+- int ret;
+- struct rpisense_js *rpisense_js;
+-
+- rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL);
+- if (rpisense == NULL)
+- return -ENOMEM;
+-
+- i2c_set_clientdata(i2c, rpisense);
+- rpisense->dev = &i2c->dev;
+- rpisense->i2c_client = i2c;
+-
+- ret = rpisense_reg_read(rpisense, RPISENSE_WAI);
+- if (ret > 0) {
+- if (ret != 's')
+- return -EINVAL;
+- } else {
+- return ret;
+- }
+- ret = rpisense_reg_read(rpisense, RPISENSE_VER);
+- if (ret < 0)
+- return ret;
+-
+- dev_info(rpisense->dev,
+- "Raspberry Pi Sense HAT firmware version %i\n", ret);
+-
+- rpisense_js = &rpisense->joystick;
+- rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev,
+- "keys-int", GPIOD_IN);
+- if (IS_ERR(rpisense_js->keys_desc)) {
+- dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n");
+- rpisense_js->keys_desc = gpio_to_desc(23);
+- if (rpisense_js->keys_desc == NULL) {
+- dev_err(&i2c->dev, "GPIO23 fallback failed.\n");
+- return PTR_ERR(rpisense_js->keys_desc);
+- }
+- }
+- rpisense_client_dev_register(rpisense, "rpi-sense-js",
+- &(rpisense->joystick.pdev));
+- rpisense_client_dev_register(rpisense, "rpi-sense-fb",
+- &(rpisense->framebuffer.pdev));
+-
+- return 0;
+-}
+-
+-static void rpisense_remove(struct i2c_client *i2c)
+-{
+- struct rpisense *rpisense = i2c_get_clientdata(i2c);
+-
+- platform_device_unregister(rpisense->joystick.pdev);
+-}
+-
+-struct rpisense *rpisense_get_dev(void)
+-{
+- return rpisense;
+-}
+-EXPORT_SYMBOL_GPL(rpisense_get_dev);
+-
+-s32 rpisense_reg_read(struct rpisense *rpisense, int reg)
+-{
+- int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);
+-
+- if (ret < 0)
+- dev_err(rpisense->dev, "Read from reg %d failed\n", reg);
+- /* Due to the BCM270x I2C clock stretching bug, some values
+- * may have MSB set. Clear it to avoid incorrect values.
+- * */
+- return ret & 0x7F;
+-}
+-EXPORT_SYMBOL_GPL(rpisense_reg_read);
+-
+-int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
+-{
+- int ret = i2c_master_send(rpisense->i2c_client, buf, count);
+-
+- if (ret < 0)
+- dev_err(rpisense->dev, "Block write failed\n");
+- return ret;
+-}
+-EXPORT_SYMBOL_GPL(rpisense_block_write);
+-
+-static const struct i2c_device_id rpisense_i2c_id[] = {
+- { "rpi-sense", 0 },
+- { }
+-};
+-MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id);
+-
+-#ifdef CONFIG_OF
+-static const struct of_device_id rpisense_core_id[] = {
+- { .compatible = "rpi,rpi-sense" },
+- { },
+-};
+-MODULE_DEVICE_TABLE(of, rpisense_core_id);
+-#endif
+-
+-
+-static struct i2c_driver rpisense_driver = {
+- .driver = {
+- .name = "rpi-sense",
+- .owner = THIS_MODULE,
+- },
+- .probe = rpisense_probe,
+- .remove = rpisense_remove,
+- .id_table = rpisense_i2c_id,
+-};
+-
+-module_i2c_driver(rpisense_driver);
+-
+-MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver");
+-MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+-MODULE_LICENSE("GPL");
+-
+--- a/drivers/video/fbdev/Kconfig
++++ b/drivers/video/fbdev/Kconfig
+@@ -1967,8 +1967,8 @@ config FB_SM712
+
+ config FB_RPISENSE
+ tristate "Raspberry Pi Sense HAT framebuffer"
+- depends on FB
+- select MFD_RPISENSE_CORE
++ depends on FB && I2C
++ select MFD_SIMPLE_MFD_I2C
+ select FB_SYS_FOPS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+--- a/drivers/video/fbdev/rpisense-fb.c
++++ b/drivers/video/fbdev/rpisense-fb.c
+@@ -23,16 +23,14 @@
+ #include <linux/delay.h>
+ #include <linux/fb.h>
+ #include <linux/init.h>
++#include <linux/platform_device.h>
+
+ #include <linux/mfd/rpisense/framebuffer.h>
+-#include <linux/mfd/rpisense/core.h>
+
+ static bool lowlight;
+ module_param(lowlight, bool, 0);
+ MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
+
+-static struct rpisense *rpisense;
+-
+ struct rpisense_fb_param {
+ char __iomem *vmem;
+ u8 *vmem_work;
+@@ -116,26 +114,26 @@ static void rpisense_fb_imageblit(struct
+ }
+
+ static void rpisense_fb_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++ struct list_head *pagelist)
+ {
+ int i;
+ int j;
+ u8 *vmem_work = rpisense_fb_param.vmem_work;
+ u16 *mem = (u16 *)rpisense_fb_param.vmem;
+ u8 *gamma = rpisense_fb_param.gamma;
++ struct rpisense_fb *rpisense_fb = info->par;
+
+- vmem_work[0] = 0;
+ for (j = 0; j < 8; j++) {
+ for (i = 0; i < 8; i++) {
+- vmem_work[(j * 24) + i + 1] =
++ vmem_work[(j * 24) + i] =
+ gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
+- vmem_work[(j * 24) + (i + 8) + 1] =
++ vmem_work[(j * 24) + (i + 8)] =
+ gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
+- vmem_work[(j * 24) + (i + 16) + 1] =
++ vmem_work[(j * 24) + (i + 16)] =
+ gamma[(mem[(j * 8) + i]) & 0x1F];
+ }
+ }
+- rpisense_block_write(rpisense, vmem_work, 193);
++ regmap_bulk_write(rpisense_fb->regmap, 0, vmem_work, 192);
+ }
+
+ static struct fb_deferred_io rpisense_fb_defio = {
+@@ -200,8 +198,22 @@ static int rpisense_fb_probe(struct plat
+ int ret = -ENOMEM;
+ struct rpisense_fb *rpisense_fb;
+
+- rpisense = rpisense_get_dev();
+- rpisense_fb = &rpisense->framebuffer;
++ info = framebuffer_alloc(sizeof(*rpisense_fb), &pdev->dev);
++ if (!info) {
++ dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
++ goto err_malloc;
++ }
++
++ rpisense_fb = info->par;
++ platform_set_drvdata(pdev, rpisense_fb);
++
++ rpisense_fb->pdev = pdev;
++ rpisense_fb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
++ if (!rpisense_fb->regmap) {
++ dev_err(&pdev->dev,
++ "unable to get sensehat regmap");
++ return -ENODEV;
++ }
+
+ rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
+ if (!rpisense_fb_param.vmem)
+@@ -211,12 +223,6 @@ static int rpisense_fb_probe(struct plat
+ if (!rpisense_fb_param.vmem_work)
+ goto err_malloc;
+
+- info = framebuffer_alloc(0, &pdev->dev);
+- if (!info) {
+- dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
+- goto err_malloc;
+- }
+- rpisense_fb->info = info;
+
+ rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
+ rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
+@@ -253,7 +259,7 @@ err_malloc:
+
+ static int rpisense_fb_remove(struct platform_device *pdev)
+ {
+- struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
++ struct rpisense_fb *rpisense_fb = platform_get_drvdata(pdev);
+ struct fb_info *info = rpisense_fb->info;
+
+ if (info) {
+@@ -266,19 +272,11 @@ static int rpisense_fb_remove(struct pla
+ return 0;
+ }
+
+-#ifdef CONFIG_OF
+ static const struct of_device_id rpisense_fb_id[] = {
+- { .compatible = "rpi,rpi-sense-fb" },
++ { .compatible = "raspberrypi,rpi-sense-fb" },
+ { },
+ };
+ MODULE_DEVICE_TABLE(of, rpisense_fb_id);
+-#endif
+-
+-static struct platform_device_id rpisense_fb_device_id[] = {
+- { .name = "rpi-sense-fb" },
+- { },
+-};
+-MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
+
+ static struct platform_driver rpisense_fb_driver = {
+ .probe = rpisense_fb_probe,
+@@ -286,6 +284,7 @@ static struct platform_driver rpisense_f
+ .driver = {
+ .name = "rpi-sense-fb",
+ .owner = THIS_MODULE,
++ .of_match_table = rpisense_fb_id,
+ },
+ };
+
+--- a/include/linux/mfd/rpisense/core.h
++++ /dev/null
+@@ -1,47 +0,0 @@
+-/*
+- * Raspberry Pi Sense HAT core driver
+- * http://raspberrypi.org
+- *
+- * Copyright (C) 2015 Raspberry Pi
+- *
+- * Author: Serge Schneider
+- *
+- * 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; either version 2 of the License, or (at your
+- * option) any later version.
+- *
+- */
+-
+-#ifndef __LINUX_MFD_RPISENSE_CORE_H_
+-#define __LINUX_MFD_RPISENSE_CORE_H_
+-
+-#include <linux/mfd/rpisense/joystick.h>
+-#include <linux/mfd/rpisense/framebuffer.h>
+-
+-/*
+- * Register values.
+- */
+-#define RPISENSE_FB 0x00
+-#define RPISENSE_WAI 0xF0
+-#define RPISENSE_VER 0xF1
+-#define RPISENSE_KEYS 0xF2
+-#define RPISENSE_EE_WP 0xF3
+-
+-#define RPISENSE_ID 's'
+-
+-struct rpisense {
+- struct device *dev;
+- struct i2c_client *i2c_client;
+-
+- /* Client devices */
+- struct rpisense_js joystick;
+- struct rpisense_fb framebuffer;
+-};
+-
+-struct rpisense *rpisense_get_dev(void);
+-s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
+-int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
+-int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
+-
+-#endif
+--- a/include/linux/mfd/rpisense/framebuffer.h
++++ b/include/linux/mfd/rpisense/framebuffer.h
+@@ -16,6 +16,8 @@
+ #ifndef __LINUX_RPISENSE_FB_H_
+ #define __LINUX_RPISENSE_FB_H_
+
++#include <linux/regmap.h>
++
+ #define SENSEFB_FBIO_IOC_MAGIC 0xF1
+
+ #define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0)
+@@ -25,8 +27,9 @@
+ struct rpisense;
+
+ struct rpisense_fb {
+- struct platform_device *pdev;
+ struct fb_info *info;
++ struct platform_device *pdev;
++ struct regmap *regmap;
+ };
+
+ #endif
--- /dev/null
+From 3a7ab92b9be0f8849941ed66049b9c3744cbd5aa Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 10 Oct 2024 17:18:57 +0100
+Subject: [PATCH 1315/1350] Reapply "Input: sensehat-joystick : Revert to
+ downstream keymap"
+
+This reverts commit bdb00151ff537c119cea7125e665a9bee1f76c58.
+---
+ drivers/input/joystick/sensehat-joystick.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/input/joystick/sensehat-joystick.c
++++ b/drivers/input/joystick/sensehat-joystick.c
+@@ -28,7 +28,7 @@ struct sensehat_joystick {
+ };
+
+ static const unsigned int keymap[] = {
+- BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
++ KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT
+ };
+
+ static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
--- /dev/null
+From 2132f8aad4e978a9789a84f667567ce2bc93cb3c Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 10 Oct 2024 17:19:06 +0100
+Subject: [PATCH 1316/1350] Reapply "dtoverlays: Add Sense Hat to hat_map"
+
+This reverts commit 14fc8b7994220e9b3d85c07b53c5704d5e082944.
+---
+ arch/arm/boot/dts/overlays/hat_map.dts | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/hat_map.dts
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -95,4 +95,14 @@
+ uuid = [ 1c955808 681f 4bbc a2ef b7ea47cd388e ];
+ overlay = "recalboxrgbdual";
+ };
++
++ sensehat-v1 {
++ uuid = [ 5d960035 8e87 428f 95d8 59852d697754 ];
++ overlay = "rpi-sense";
++ };
++
++ sensehat-v2 {
++ uuid = [ 1aa9c428 72eb 48da 9306 8c3706ed3653 ];
++ overlay = "rpi-sense-v2";
++ };
+ };
--- /dev/null
+From e53eefbc711622f0702e887f88d69f867aa0bf1a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 1 Oct 2024 12:14:57 +0100
+Subject: [PATCH 1317/1350] overlays: hat_map: Add Sense and Hailo AI HATs
+
+Add mappings to overlays for the Sense HATs and the Hailo AI HATs. Note
+that mapping by product names (and not UUIDs) as used here requires an
+updated firmware.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/hat_map.dts | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/hat_map.dts
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -1,6 +1,18 @@
+ /dts-v1/;
+
+ / {
++ hailo-8 {
++ product = "Raspberry Pi AI Hat, Model Hailo-8";
++ vendor = "Hailo Technologies";
++ overlay = "none,pciex1,pciex1_gen=3";
++ };
++
++ hailo-8l {
++ product = "Raspberry Pi AI Hat, Model Hailo-8L";
++ vendor = "Hailo Technologies";
++ overlay = "none,pciex1,pciex1_gen=3";
++ };
++
+ hifiberry-amp100-1 {
+ uuid = [ 5eb863b8 12f9 41ad 978f 4cee1b3eca62 ];
+ overlay = "hifiberry-amp100";
+@@ -97,12 +109,16 @@
+ };
+
+ sensehat-v1 {
+- uuid = [ 5d960035 8e87 428f 95d8 59852d697754 ];
++ product = "Sense HAT";
++ vendor = "Raspberry Pi";
++ pver = < 0x0001 >;
+ overlay = "rpi-sense";
+ };
+
+ sensehat-v2 {
+- uuid = [ 1aa9c428 72eb 48da 9306 8c3706ed3653 ];
++ product = "Sense HAT";
++ vendor = "Raspberry Pi";
++ pver = < 0x0002 >;
+ overlay = "rpi-sense-v2";
+ };
+ };
--- /dev/null
+From e2cafea49115af21f84e315e228121ec10dd4cb3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 11 Oct 2024 12:32:46 +0100
+Subject: [PATCH 1318/1350] drivers: media: imx500: Enable LS correction
+
+This correction is calibrated to approx 5000K.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx500.c | 387 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 387 insertions(+)
+
+--- a/drivers/media/i2c/imx500.c
++++ b/drivers/media/i2c/imx500.c
+@@ -887,6 +887,393 @@ static const struct cci_reg_sequence dnn
+ { CCI_REG32(0xd950), 0xfb00f7 },
+ { CCI_REG16(0xd954), 0xff },
+ { CCI_REG8(0xd826), 1 },
++ /* LSC */
++ { CCI_REG32(0xe000), 0x2e502a0 },
++ { CCI_REG32(0xe004), 0x2c80283 },
++ { CCI_REG32(0xe008), 0x2700233 },
++ { CCI_REG32(0xe00c), 0x22d01f6 },
++ { CCI_REG32(0xe010), 0x1f401c3 },
++ { CCI_REG32(0xe014), 0x1c5019c },
++ { CCI_REG32(0xe018), 0x1bb0192 },
++ { CCI_REG32(0xe01c), 0x1ba0192 },
++ { CCI_REG32(0xe020), 0x1b90192 },
++ { CCI_REG32(0xe024), 0x1ba0192 },
++ { CCI_REG32(0xe028), 0x1ca019f },
++ { CCI_REG32(0xe02c), 0x1fb01c8 },
++ { CCI_REG32(0xe030), 0x23601fb },
++ { CCI_REG32(0xe034), 0x27a0239 },
++ { CCI_REG32(0xe038), 0x2d5028a },
++ { CCI_REG32(0xe03c), 0x2f302a8 },
++ { CCI_REG32(0xe040), 0x2c60283 },
++ { CCI_REG32(0xe044), 0x27c0240 },
++ { CCI_REG32(0xe048), 0x22d01f6 },
++ { CCI_REG32(0xe04c), 0x1fd01cd },
++ { CCI_REG32(0xe050), 0x1c4019c },
++ { CCI_REG32(0xe054), 0x19c017b },
++ { CCI_REG32(0xe058), 0x1810165 },
++ { CCI_REG32(0xe05c), 0x175015c },
++ { CCI_REG32(0xe060), 0x175015c },
++ { CCI_REG32(0xe064), 0x1840167 },
++ { CCI_REG32(0xe068), 0x1a0017e },
++ { CCI_REG32(0xe06c), 0x1cc01a1 },
++ { CCI_REG32(0xe070), 0x20501d1 },
++ { CCI_REG32(0xe074), 0x23601fc },
++ { CCI_REG32(0xe078), 0x2890246 },
++ { CCI_REG32(0xe07c), 0x2d3028a },
++ { CCI_REG32(0xe080), 0x2800243 },
++ { CCI_REG32(0xe084), 0x245020e },
++ { CCI_REG32(0xe088), 0x1ff01ce },
++ { CCI_REG32(0xe08c), 0x1c4019c },
++ { CCI_REG32(0xe090), 0x19a017b },
++ { CCI_REG32(0xe094), 0x1650150 },
++ { CCI_REG32(0xe098), 0x14a013a },
++ { CCI_REG32(0xe09c), 0x13f0131 },
++ { CCI_REG32(0xe0a0), 0x1400131 },
++ { CCI_REG32(0xe0a4), 0x14d013c },
++ { CCI_REG32(0xe0a8), 0x16a0154 },
++ { CCI_REG32(0xe0ac), 0x1a1017e },
++ { CCI_REG32(0xe0b0), 0x1cc01a1 },
++ { CCI_REG32(0xe0b4), 0x20801d3 },
++ { CCI_REG32(0xe0b8), 0x2510214 },
++ { CCI_REG32(0xe0bc), 0x28b0249 },
++ { CCI_REG32(0xe0c0), 0x2640229 },
++ { CCI_REG32(0xe0c4), 0x22101ed },
++ { CCI_REG32(0xe0c8), 0x1dc01b0 },
++ { CCI_REG32(0xe0cc), 0x19c017c },
++ { CCI_REG32(0xe0d0), 0x1650150 },
++ { CCI_REG32(0xe0d4), 0x148013a },
++ { CCI_REG32(0xe0d8), 0x123011c },
++ { CCI_REG32(0xe0dc), 0x1190113 },
++ { CCI_REG32(0xe0e0), 0x1190113 },
++ { CCI_REG32(0xe0e4), 0x1280120 },
++ { CCI_REG32(0xe0e8), 0x14c013c },
++ { CCI_REG32(0xe0ec), 0x16b0154 },
++ { CCI_REG32(0xe0f0), 0x1a30181 },
++ { CCI_REG32(0xe0f4), 0x1e601b6 },
++ { CCI_REG32(0xe0f8), 0x22c01f3 },
++ { CCI_REG32(0xe0fc), 0x2700230 },
++ { CCI_REG32(0xe100), 0x257021d },
++ { CCI_REG32(0xe104), 0x20901d8 },
++ { CCI_REG32(0xe108), 0x1c4019d },
++ { CCI_REG32(0xe10c), 0x1820167 },
++ { CCI_REG32(0xe110), 0x14b013b },
++ { CCI_REG32(0xe114), 0x124011c },
++ { CCI_REG32(0xe118), 0x1170113 },
++ { CCI_REG32(0xe11c), 0x1010101 },
++ { CCI_REG32(0xe120), 0x1030102 },
++ { CCI_REG32(0xe124), 0x1190113 },
++ { CCI_REG32(0xe128), 0x1280120 },
++ { CCI_REG32(0xe12c), 0x14f013f },
++ { CCI_REG32(0xe130), 0x189016c },
++ { CCI_REG32(0xe134), 0x1ce01a3 },
++ { CCI_REG32(0xe138), 0x21601df },
++ { CCI_REG32(0xe13c), 0x2630224 },
++ { CCI_REG32(0xe140), 0x257021d },
++ { CCI_REG32(0xe144), 0x20101d0 },
++ { CCI_REG32(0xe148), 0x1ba0194 },
++ { CCI_REG32(0xe14c), 0x176015d },
++ { CCI_REG32(0xe150), 0x13e0132 },
++ { CCI_REG32(0xe154), 0x1190114 },
++ { CCI_REG32(0xe158), 0x1010101 },
++ { CCI_REG32(0xe15c), 0x1000100 },
++ { CCI_REG32(0xe160), 0x1010100 },
++ { CCI_REG32(0xe164), 0x1040103 },
++ { CCI_REG32(0xe168), 0x11d0118 },
++ { CCI_REG32(0xe16c), 0x1450136 },
++ { CCI_REG32(0xe170), 0x17d0163 },
++ { CCI_REG32(0xe174), 0x1c4019a },
++ { CCI_REG32(0xe178), 0x20d01d6 },
++ { CCI_REG32(0xe17c), 0x2630224 },
++ { CCI_REG32(0xe180), 0x257021d },
++ { CCI_REG32(0xe184), 0x20001d0 },
++ { CCI_REG32(0xe188), 0x1b90194 },
++ { CCI_REG32(0xe18c), 0x175015d },
++ { CCI_REG32(0xe190), 0x13e0132 },
++ { CCI_REG32(0xe194), 0x1180114 },
++ { CCI_REG32(0xe198), 0x1040103 },
++ { CCI_REG32(0xe19c), 0x1000100 },
++ { CCI_REG32(0xe1a0), 0x1030102 },
++ { CCI_REG32(0xe1a4), 0x1050103 },
++ { CCI_REG32(0xe1a8), 0x11d0118 },
++ { CCI_REG32(0xe1ac), 0x1450136 },
++ { CCI_REG32(0xe1b0), 0x17d0163 },
++ { CCI_REG32(0xe1b4), 0x1c4019a },
++ { CCI_REG32(0xe1b8), 0x20d01d6 },
++ { CCI_REG32(0xe1bc), 0x2640224 },
++ { CCI_REG32(0xe1c0), 0x258021f },
++ { CCI_REG32(0xe1c4), 0x20e01db },
++ { CCI_REG32(0xe1c8), 0x1c7019f },
++ { CCI_REG32(0xe1cc), 0x1840169 },
++ { CCI_REG32(0xe1d0), 0x14d013e },
++ { CCI_REG32(0xe1d4), 0x1290120 },
++ { CCI_REG32(0xe1d8), 0x1180114 },
++ { CCI_REG32(0xe1dc), 0x1050103 },
++ { CCI_REG32(0xe1e0), 0x1050103 },
++ { CCI_REG32(0xe1e4), 0x11e0117 },
++ { CCI_REG32(0xe1e8), 0x12c0123 },
++ { CCI_REG32(0xe1ec), 0x1530142 },
++ { CCI_REG32(0xe1f0), 0x18d016f },
++ { CCI_REG32(0xe1f4), 0x1d201a6 },
++ { CCI_REG32(0xe1f8), 0x21a01e2 },
++ { CCI_REG32(0xe1fc), 0x2640225 },
++ { CCI_REG32(0xe200), 0x269022d },
++ { CCI_REG32(0xe204), 0x22601f1 },
++ { CCI_REG32(0xe208), 0x1e101b4 },
++ { CCI_REG32(0xe20c), 0x1a10181 },
++ { CCI_REG32(0xe210), 0x16c0156 },
++ { CCI_REG32(0xe214), 0x14d013e },
++ { CCI_REG32(0xe218), 0x1290120 },
++ { CCI_REG32(0xe21c), 0x11f0118 },
++ { CCI_REG32(0xe220), 0x11f0118 },
++ { CCI_REG32(0xe224), 0x12b0123 },
++ { CCI_REG32(0xe228), 0x1530142 },
++ { CCI_REG32(0xe22c), 0x172015a },
++ { CCI_REG32(0xe230), 0x1aa0187 },
++ { CCI_REG32(0xe234), 0x1ec01bb },
++ { CCI_REG32(0xe238), 0x23301f8 },
++ { CCI_REG32(0xe23c), 0x2750233 },
++ { CCI_REG32(0xe240), 0x28b024c },
++ { CCI_REG32(0xe244), 0x24f0216 },
++ { CCI_REG32(0xe248), 0x20701d4 },
++ { CCI_REG32(0xe24c), 0x1ce01a4 },
++ { CCI_REG32(0xe250), 0x1a10181 },
++ { CCI_REG32(0xe254), 0x16c0156 },
++ { CCI_REG32(0xe258), 0x1520141 },
++ { CCI_REG32(0xe25c), 0x1480138 },
++ { CCI_REG32(0xe260), 0x1480138 },
++ { CCI_REG32(0xe264), 0x1550143 },
++ { CCI_REG32(0xe268), 0x172015a },
++ { CCI_REG32(0xe26c), 0x1aa0187 },
++ { CCI_REG32(0xe270), 0x1d701a9 },
++ { CCI_REG32(0xe274), 0x21201db },
++ { CCI_REG32(0xe278), 0x25d021d },
++ { CCI_REG32(0xe27c), 0x2990254 },
++ { CCI_REG32(0xe280), 0x2d70291 },
++ { CCI_REG32(0xe284), 0x28c024c },
++ { CCI_REG32(0xe288), 0x2390201 },
++ { CCI_REG32(0xe28c), 0x20701d4 },
++ { CCI_REG32(0xe290), 0x1ce01a4 },
++ { CCI_REG32(0xe294), 0x1a70184 },
++ { CCI_REG32(0xe298), 0x18c016e },
++ { CCI_REG32(0xe29c), 0x1810164 },
++ { CCI_REG32(0xe2a0), 0x1810164 },
++ { CCI_REG32(0xe2a4), 0x1900170 },
++ { CCI_REG32(0xe2a8), 0x1ad0188 },
++ { CCI_REG32(0xe2ac), 0x1d601a9 },
++ { CCI_REG32(0xe2b0), 0x21201da },
++ { CCI_REG32(0xe2b4), 0x2450207 },
++ { CCI_REG32(0xe2b8), 0x29a0254 },
++ { CCI_REG32(0xe2bc), 0x2ea029d },
++ { CCI_REG32(0xe2c0), 0x2f602ae },
++ { CCI_REG32(0xe2c4), 0x2d80291 },
++ { CCI_REG32(0xe2c8), 0x280023f },
++ { CCI_REG32(0xe2cc), 0x2390200 },
++ { CCI_REG32(0xe2d0), 0x1fe01cc },
++ { CCI_REG32(0xe2d4), 0x1d201a4 },
++ { CCI_REG32(0xe2d8), 0x1c6019b },
++ { CCI_REG32(0xe2dc), 0x1c6019b },
++ { CCI_REG32(0xe2e0), 0x1c6019b },
++ { CCI_REG32(0xe2e4), 0x1c8019b },
++ { CCI_REG32(0xe2e8), 0x1d701a9 },
++ { CCI_REG32(0xe2ec), 0x20801d1 },
++ { CCI_REG32(0xe2f0), 0x2450206 },
++ { CCI_REG32(0xe2f4), 0x28e0248 },
++ { CCI_REG32(0xe2f8), 0x2ec029d },
++ { CCI_REG32(0xe2fc), 0x30902b9 },
++ { CCI_REG32(0xe300), 0x2a002a4 },
++ { CCI_REG32(0xe304), 0x2830286 },
++ { CCI_REG32(0xe308), 0x2330234 },
++ { CCI_REG32(0xe30c), 0x1f601f7 },
++ { CCI_REG32(0xe310), 0x1c301c4 },
++ { CCI_REG32(0xe314), 0x19c019c },
++ { CCI_REG32(0xe318), 0x1920193 },
++ { CCI_REG32(0xe31c), 0x1920193 },
++ { CCI_REG32(0xe320), 0x1920192 },
++ { CCI_REG32(0xe324), 0x1920193 },
++ { CCI_REG32(0xe328), 0x19f01a1 },
++ { CCI_REG32(0xe32c), 0x1c801ca },
++ { CCI_REG32(0xe330), 0x1fb01fe },
++ { CCI_REG32(0xe334), 0x239023e },
++ { CCI_REG32(0xe338), 0x28a0292 },
++ { CCI_REG32(0xe33c), 0x2a802b0 },
++ { CCI_REG32(0xe340), 0x2830287 },
++ { CCI_REG32(0xe344), 0x2400242 },
++ { CCI_REG32(0xe348), 0x1f601f8 },
++ { CCI_REG32(0xe34c), 0x1cd01ce },
++ { CCI_REG32(0xe350), 0x19c019d },
++ { CCI_REG32(0xe354), 0x17b017d },
++ { CCI_REG32(0xe358), 0x1650166 },
++ { CCI_REG32(0xe35c), 0x15c015d },
++ { CCI_REG32(0xe360), 0x15c015d },
++ { CCI_REG32(0xe364), 0x1670168 },
++ { CCI_REG32(0xe368), 0x17e0180 },
++ { CCI_REG32(0xe36c), 0x1a101a3 },
++ { CCI_REG32(0xe370), 0x1d101d3 },
++ { CCI_REG32(0xe374), 0x1fc0200 },
++ { CCI_REG32(0xe378), 0x246024c },
++ { CCI_REG32(0xe37c), 0x28a0291 },
++ { CCI_REG32(0xe380), 0x2430245 },
++ { CCI_REG32(0xe384), 0x20e0211 },
++ { CCI_REG32(0xe388), 0x1ce01d0 },
++ { CCI_REG32(0xe38c), 0x19c019e },
++ { CCI_REG32(0xe390), 0x17b017c },
++ { CCI_REG32(0xe394), 0x1500152 },
++ { CCI_REG32(0xe398), 0x13a013c },
++ { CCI_REG32(0xe39c), 0x1310134 },
++ { CCI_REG32(0xe3a0), 0x1310134 },
++ { CCI_REG32(0xe3a4), 0x13c013f },
++ { CCI_REG32(0xe3a8), 0x1540156 },
++ { CCI_REG32(0xe3ac), 0x17e0180 },
++ { CCI_REG32(0xe3b0), 0x1a101a4 },
++ { CCI_REG32(0xe3b4), 0x1d301d8 },
++ { CCI_REG32(0xe3b8), 0x2140219 },
++ { CCI_REG32(0xe3bc), 0x249024e },
++ { CCI_REG32(0xe3c0), 0x229022b },
++ { CCI_REG32(0xe3c4), 0x1ed01ef },
++ { CCI_REG32(0xe3c8), 0x1b001b2 },
++ { CCI_REG32(0xe3cc), 0x17c017e },
++ { CCI_REG32(0xe3d0), 0x1500151 },
++ { CCI_REG32(0xe3d4), 0x13a013c },
++ { CCI_REG32(0xe3d8), 0x11c011f },
++ { CCI_REG32(0xe3dc), 0x1130117 },
++ { CCI_REG32(0xe3e0), 0x1130117 },
++ { CCI_REG32(0xe3e4), 0x1200123 },
++ { CCI_REG32(0xe3e8), 0x13c013f },
++ { CCI_REG32(0xe3ec), 0x1540156 },
++ { CCI_REG32(0xe3f0), 0x1810183 },
++ { CCI_REG32(0xe3f4), 0x1b601ba },
++ { CCI_REG32(0xe3f8), 0x1f301f6 },
++ { CCI_REG32(0xe3fc), 0x2300234 },
++ { CCI_REG32(0xe400), 0x21d0221 },
++ { CCI_REG32(0xe404), 0x1d801db },
++ { CCI_REG32(0xe408), 0x19d019f },
++ { CCI_REG32(0xe40c), 0x1670169 },
++ { CCI_REG32(0xe410), 0x13b013d },
++ { CCI_REG32(0xe414), 0x11c011f },
++ { CCI_REG32(0xe418), 0x1130117 },
++ { CCI_REG32(0xe41c), 0x1010106 },
++ { CCI_REG32(0xe420), 0x1020108 },
++ { CCI_REG32(0xe424), 0x1130117 },
++ { CCI_REG32(0xe428), 0x1200123 },
++ { CCI_REG32(0xe42c), 0x13f0142 },
++ { CCI_REG32(0xe430), 0x16c016f },
++ { CCI_REG32(0xe434), 0x1a301a6 },
++ { CCI_REG32(0xe438), 0x1df01e2 },
++ { CCI_REG32(0xe43c), 0x2240228 },
++ { CCI_REG32(0xe440), 0x21d0220 },
++ { CCI_REG32(0xe444), 0x1d001d3 },
++ { CCI_REG32(0xe448), 0x1940196 },
++ { CCI_REG32(0xe44c), 0x15d0160 },
++ { CCI_REG32(0xe450), 0x1320135 },
++ { CCI_REG32(0xe454), 0x1140118 },
++ { CCI_REG32(0xe458), 0x1010106 },
++ { CCI_REG32(0xe45c), 0x1000106 },
++ { CCI_REG32(0xe460), 0x1000106 },
++ { CCI_REG32(0xe464), 0x1030109 },
++ { CCI_REG32(0xe468), 0x118011b },
++ { CCI_REG32(0xe46c), 0x136013a },
++ { CCI_REG32(0xe470), 0x1630165 },
++ { CCI_REG32(0xe474), 0x19a019c },
++ { CCI_REG32(0xe478), 0x1d601d9 },
++ { CCI_REG32(0xe47c), 0x2240227 },
++ { CCI_REG32(0xe480), 0x21d0220 },
++ { CCI_REG32(0xe484), 0x1d001d3 },
++ { CCI_REG32(0xe488), 0x1940196 },
++ { CCI_REG32(0xe48c), 0x15d0160 },
++ { CCI_REG32(0xe490), 0x1320135 },
++ { CCI_REG32(0xe494), 0x1140118 },
++ { CCI_REG32(0xe498), 0x1030109 },
++ { CCI_REG32(0xe49c), 0x1000106 },
++ { CCI_REG32(0xe4a0), 0x1020108 },
++ { CCI_REG32(0xe4a4), 0x1030109 },
++ { CCI_REG32(0xe4a8), 0x118011b },
++ { CCI_REG32(0xe4ac), 0x1360139 },
++ { CCI_REG32(0xe4b0), 0x1630165 },
++ { CCI_REG32(0xe4b4), 0x19a019c },
++ { CCI_REG32(0xe4b8), 0x1d601d9 },
++ { CCI_REG32(0xe4bc), 0x2240227 },
++ { CCI_REG32(0xe4c0), 0x21f0221 },
++ { CCI_REG32(0xe4c4), 0x1db01de },
++ { CCI_REG32(0xe4c8), 0x19f01a2 },
++ { CCI_REG32(0xe4cc), 0x169016c },
++ { CCI_REG32(0xe4d0), 0x13e0141 },
++ { CCI_REG32(0xe4d4), 0x1200124 },
++ { CCI_REG32(0xe4d8), 0x1140119 },
++ { CCI_REG32(0xe4dc), 0x1030109 },
++ { CCI_REG32(0xe4e0), 0x1030109 },
++ { CCI_REG32(0xe4e4), 0x117011c },
++ { CCI_REG32(0xe4e8), 0x1230126 },
++ { CCI_REG32(0xe4ec), 0x1420145 },
++ { CCI_REG32(0xe4f0), 0x16f0171 },
++ { CCI_REG32(0xe4f4), 0x1a601a8 },
++ { CCI_REG32(0xe4f8), 0x1e201e4 },
++ { CCI_REG32(0xe4fc), 0x2250227 },
++ { CCI_REG32(0xe500), 0x22d0231 },
++ { CCI_REG32(0xe504), 0x1f101f4 },
++ { CCI_REG32(0xe508), 0x1b401b7 },
++ { CCI_REG32(0xe50c), 0x1810183 },
++ { CCI_REG32(0xe510), 0x1560159 },
++ { CCI_REG32(0xe514), 0x13e0141 },
++ { CCI_REG32(0xe518), 0x1200124 },
++ { CCI_REG32(0xe51c), 0x118011c },
++ { CCI_REG32(0xe520), 0x118011c },
++ { CCI_REG32(0xe524), 0x1230126 },
++ { CCI_REG32(0xe528), 0x1420145 },
++ { CCI_REG32(0xe52c), 0x15a015c },
++ { CCI_REG32(0xe530), 0x1870188 },
++ { CCI_REG32(0xe534), 0x1bb01bd },
++ { CCI_REG32(0xe538), 0x1f801fb },
++ { CCI_REG32(0xe53c), 0x2330236 },
++ { CCI_REG32(0xe540), 0x24c0250 },
++ { CCI_REG32(0xe544), 0x2160219 },
++ { CCI_REG32(0xe548), 0x1d401d7 },
++ { CCI_REG32(0xe54c), 0x1a401a6 },
++ { CCI_REG32(0xe550), 0x1810183 },
++ { CCI_REG32(0xe554), 0x1560158 },
++ { CCI_REG32(0xe558), 0x1410144 },
++ { CCI_REG32(0xe55c), 0x138013b },
++ { CCI_REG32(0xe560), 0x138013b },
++ { CCI_REG32(0xe564), 0x1430146 },
++ { CCI_REG32(0xe568), 0x15a015c },
++ { CCI_REG32(0xe56c), 0x1870188 },
++ { CCI_REG32(0xe570), 0x1a901ab },
++ { CCI_REG32(0xe574), 0x1db01dd },
++ { CCI_REG32(0xe578), 0x21d0221 },
++ { CCI_REG32(0xe57c), 0x2540259 },
++ { CCI_REG32(0xe580), 0x2910296 },
++ { CCI_REG32(0xe584), 0x24c0251 },
++ { CCI_REG32(0xe588), 0x2010204 },
++ { CCI_REG32(0xe58c), 0x1d401d6 },
++ { CCI_REG32(0xe590), 0x1a401a5 },
++ { CCI_REG32(0xe594), 0x1840186 },
++ { CCI_REG32(0xe598), 0x16e0170 },
++ { CCI_REG32(0xe59c), 0x1640167 },
++ { CCI_REG32(0xe5a0), 0x1640167 },
++ { CCI_REG32(0xe5a4), 0x1700173 },
++ { CCI_REG32(0xe5a8), 0x188018a },
++ { CCI_REG32(0xe5ac), 0x1a901ab },
++ { CCI_REG32(0xe5b0), 0x1da01dd },
++ { CCI_REG32(0xe5b4), 0x207020a },
++ { CCI_REG32(0xe5b8), 0x2540259 },
++ { CCI_REG32(0xe5bc), 0x29d02a3 },
++ { CCI_REG32(0xe5c0), 0x2ae02b4 },
++ { CCI_REG32(0xe5c4), 0x2910297 },
++ { CCI_REG32(0xe5c8), 0x23f0243 },
++ { CCI_REG32(0xe5cc), 0x2000201 },
++ { CCI_REG32(0xe5d0), 0x1cc01cd },
++ { CCI_REG32(0xe5d4), 0x1a401a6 },
++ { CCI_REG32(0xe5d8), 0x19b019d },
++ { CCI_REG32(0xe5dc), 0x19b019d },
++ { CCI_REG32(0xe5e0), 0x19b019d },
++ { CCI_REG32(0xe5e4), 0x19b019e },
++ { CCI_REG32(0xe5e8), 0x1a901ab },
++ { CCI_REG32(0xe5ec), 0x1d101d3 },
++ { CCI_REG32(0xe5f0), 0x2060209 },
++ { CCI_REG32(0xe5f4), 0x248024b },
++ { CCI_REG32(0xe5f8), 0x29d02a3 },
++ { CCI_REG32(0xe5fc), 0x2b902c0 },
++ { CCI_REG8(0xd822), 0x01 },
++ { CCI_REG8(0xd823), 0x0f },
+ };
+
+ /* Mode configs */
--- /dev/null
+From 0985b32279d47e7ea01152233d26eedcdf721d09 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 11 Oct 2024 14:24:51 +0100
+Subject: [PATCH 1319/1350] dts: bcm2712-rpi-500: Add USER_LED GPIO name
+
+The USER_LED signal was missing from the initial DTS.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
+@@ -35,7 +35,7 @@
+ "PWR_GPIO", // GPIO_020
+ "KEYB_RUN", // GPIO_021
+ "-", // GPIO_022
+- "-", // GPIO_023
++ "USER_LED", // GPIO_023
+ "BT_RTS", // GPIO_024
+ "BT_CTS", // GPIO_025
+ "BT_TXD", // GPIO_026
--- /dev/null
+From 239df148741e35d5b54749a624f96dfcacc7c57e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 14 Oct 2024 15:37:57 +0100
+Subject: [PATCH 1321/1350] dtoverlays: Fix up imx500 overlays to have unique
+ clock nodes
+
+The overlay was creating DT nodes /clocks/clk-aicam and
+/clocks/clk-aicam-gated for both cam0 and cam1, which resulted
+in one failing.
+
+The clock infrastructure creates the clock name from the node name
+without any @N reg extension, so we can't just use that. The nodes
+therefore have to be renamed.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx500-overlay.dts | 10 ++++++----
+ arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts | 10 ++++++----
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/imx500-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx500-overlay.dts
+@@ -72,16 +72,16 @@
+ };
+ };
+
+- clocks_frag: fragment@104 {
++ fragment@104 {
+ target-path = "/clocks";
+ __overlay__ {
+- clk_aicam: clk-aicam {
++ clk_aicam: clk-aicam1 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
+
+- clk_aicam_gated: clk-aicam-gated {
++ clk_aicam_gated: clk-aicam-gated1 {
+ compatible = "gpio-gate-clock";
+ clocks = <&clk_aicam>;
+ #clock-cells = <0>;
+@@ -98,7 +98,9 @@
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&spi_bridge>, "power-supply:0=",<&cam0_reg>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+- <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++ <&cam_node>, "VANA-supply:0=",<&cam0_reg>,
++ <&clk_aicam>,"name=clk-aicam0",
++ <&clk_aicam_gated>,"name=clk-aicam-gated0";
+ bypass-cache = <&spi_bridge>,"bypass-cache?";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
+@@ -75,16 +75,16 @@
+ };
+ };
+
+- clocks_frag: fragment@104 {
++ fragment@104 {
+ target-path = "/clocks";
+ __overlay__ {
+- clk_aicam: clk-aicam {
++ clk_aicam: clk-aicam1 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
+
+- clk_aicam_gated: clk-aicam-gated {
++ clk_aicam_gated: clk-aicam-gated1 {
+ compatible = "gpio-gate-clock";
+ clocks = <&clk_aicam>;
+ #clock-cells = <0>;
+@@ -103,7 +103,9 @@
+ <&spi_frag_overlay>, "fast_xfer-gpios:16=35", // CD0_IO1_MICDAT0 (clock)
+ <&spi_bridge>, "power-supply:0=",<&cam0_reg>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+- <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++ <&cam_node>, "VANA-supply:0=",<&cam0_reg>,
++ <&clk_aicam>,"name=clk-aicam0",
++ <&clk_aicam_gated>,"name=clk-aicam-gated0";
+ bypass-cache = <&spi_bridge>,"bypass-cache?";
+ };
+ };
--- /dev/null
+From 6f2d1b4c3132206de40247b604638a51277131fb Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 14 Oct 2024 18:53:43 +0100
+Subject: [PATCH 1322/1350] dtoverlays: Add an overlay for Waveshare's 800x480
+ 4.3" DSI screen
+
+It tried to be a clone of the Pi 7" display, but isn't, and gives
+corrupt images with the current timings.
+
+Add a new overlay for it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 17 +++
+ .../vc4-kms-dsi-waveshare-800x480-overlay.dts | 119 ++++++++++++++++++
+ 3 files changed, 137 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -319,6 +319,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ vc4-kms-dsi-ili9881-7inch.dtbo \
+ vc4-kms-dsi-lt070me05000.dtbo \
+ vc4-kms-dsi-lt070me05000-v2.dtbo \
++ vc4-kms-dsi-waveshare-800x480.dtbo \
+ vc4-kms-dsi-waveshare-panel.dtbo \
+ vc4-kms-kippah-7inch.dtbo \
+ vc4-kms-v3d.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5248,6 +5248,23 @@ Load: dtoverlay=vc4-kms-dsi-lt070me050
+ Params: <None>
+
+
++Name: vc4-kms-dsi-waveshare-800x480
++Info: Enable the Waveshare 4.3" 800x480 DSI screen.
++ It tries to look like the Pi 7" display, but won't accept some of the
++ timings.
++ Includes the edt-ft5406 for the touchscreen element.
++ Requires vc4-kms-v3d to be loaded.
++Load: dtoverlay=vc4-kms-dsi-waveshare-800x480,<param>=<val>
++Params: sizex Touchscreen size x (default 800)
++ sizey Touchscreen size y (default 480)
++ invx Touchscreen inverted x axis
++ invy Touchscreen inverted y axis
++ swapxy Touchscreen swapped x y axis
++ disable_touch Disables the touch screen overlay driver
++ dsi0 Use DSI0 and i2c_csi_dsi0 (rather than
++ the default DSI1 and i2c_csi_dsi).
++
++
+ Name: vc4-kms-dsi-waveshare-panel
+ Info: Enable a Waveshare DSI touchscreen
+ Includes the Goodix driver for the touchscreen element.
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts
+@@ -0,0 +1,119 @@
++/*
++ * Device Tree overlay for Waveshare 4.3" 800x480 panel.
++ * It tries to look like a Pi 7" panel, but fails with some of the timing
++ * options.
++ */
++
++/dts-v1/;
++/plugin/;
++
++#include "edt-ft5406.dtsi"
++
++/ {
++ /* No compatible as it will have come from edt-ft5406.dtsi */
++
++ dsi_frag: fragment@0 {
++ target = <&dsi1>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++ port {
++ dsi_out: endpoint {
++ remote-endpoint = <&panel_dsi_port>;
++ };
++ };
++
++ panel: panel-dsi-generic@0 {
++ // See panel-dsi.yaml binding
++ compatible = "waveshare,4-3-inch-dsi","panel-dsi";
++ reg = <0>;
++ power-supply = <®_display>;
++ backlight = <®_display>;
++ dsi-color-format = "RGB888";
++ mode = "MODE_VIDEO";
++ width-mm = <0>;
++ height-mm = <0>;
++
++ port {
++ panel_dsi_port: endpoint {
++ data-lanes = <1>;
++ remote-endpoint = <&dsi_out>;
++ };
++ };
++
++ timing: panel-timing {
++ clock-frequency = <27777000>;
++ hactive = <800>;
++ vactive = <480>;
++ hfront-porch = <59>;
++ hsync-len = <2>;
++ hback-porch = <45>;
++ vfront-porch = <7>;
++ vsync-len = <2>;
++ vback-porch = <22>;
++ };
++ };
++ };
++ };
++
++ fragment@1 {
++ target-path = "/";
++ __overlay__ {
++ reg_bridge: reg_bridge@1 {
++ reg = <1>;
++ compatible = "regulator-fixed";
++ regulator-name = "bridge_reg";
++ gpio = <®_display 0 0>;
++ vin-supply = <®_display>;
++ enable-active-high;
++ };
++ };
++ };
++
++ i2c_frag: fragment@2 {
++ target = <&i2c_csi_dsi>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ reg_display: reg_display@45 {
++ compatible = "raspberrypi,7inch-touchscreen-panel-regulator";
++ reg = <0x45>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ };
++ };
++ };
++
++ fragment@3 {
++ target = <&i2c0if>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@4 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++ fragment@5 {
++ target = <&ft5406>;
++ __overlay__ {
++ vcc-supply = <®_display>;
++ reset-gpio = <®_display 1 1>;
++ };
++ };
++
++ __overrides__ {
++ dsi0 = <&dsi_frag>, "target:0=",<&dsi0>,
++ <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&ts_i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <®_bridge>, "reg:0=0",
++ <®_bridge>, "regulator-name=bridge_reg_0";
++ disable_touch = <&ft5406>, "status=disabled";
++ };
++};
--- /dev/null
+From 18d185166ca00c9280505ad41fbe036efbb52e67 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 14 Oct 2024 18:55:00 +0100
+Subject: [PATCH 1323/1350] drm/vc4: Remove request for min clocks when hdmi
+ output is disabled
+
+Currently, booting with no hdmi connected has:
+pi@pi4:~ $ vcgencmd measure_clock hdmi pixel
+frequency(9)=120010256
+frequency(29)=74988280
+
+After connecting hdmi we get:
+pi@pi4:~ $ vcgencmd measure_clock hdmi pixel
+frequency(9)=300005856
+frequency(29)=149989744
+
+and that persists after disconnecting hdmi
+
+I can measure this on a power supply as 10mA@5.2V (52mW).
+
+We should always remove clk_set_min_rate requests
+when we no longer need them.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 +++
+ drivers/gpu/drm/vc4/vc4_v3d.c | 2 ++
+ 3 files changed, 9 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1224,6 +1224,8 @@ static void vc4_hdmi_encoder_post_crtc_p
+ if (vc4_hdmi->variant->phy_disable)
+ vc4_hdmi->variant->phy_disable(vc4_hdmi);
+
++ /* we no longer require a minimum clock rate */
++ clk_set_min_rate(vc4_hdmi->pixel_bvb_clock, 0);
+ clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
+ clk_disable_unprepare(vc4_hdmi->pixel_clock);
+
+@@ -3724,6 +3726,8 @@ static int vc4_hdmi_runtime_suspend(stru
+ struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(vc4_hdmi->audio_clock);
++ /* we no longer require a minimum clock rate */
++ clk_set_min_rate(vc4_hdmi->hsm_clock, 0);
+ clk_disable_unprepare(vc4_hdmi->hsm_clock);
+
+ return 0;
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -2308,7 +2308,10 @@ static void vc4_hvs_unbind(struct device
+ drm_mm_remove_node(node);
+ drm_mm_takedown(&vc4->hvs->lbm_mm);
+
++ /* we no longer require a minimum clock rate */
++ clk_set_min_rate(hvs->disp_clk, 0);
+ clk_disable_unprepare(hvs->disp_clk);
++ clk_set_min_rate(hvs->core_clk, 0);
+ clk_disable_unprepare(hvs->core_clk);
+
+ vc4->hvs = NULL;
+--- a/drivers/gpu/drm/vc4/vc4_v3d.c
++++ b/drivers/gpu/drm/vc4/vc4_v3d.c
+@@ -376,6 +376,8 @@ static int vc4_v3d_runtime_suspend(struc
+
+ vc4_irq_disable(&vc4->base);
+
++ /* we no longer require a minimum clock rate */
++ clk_set_min_rate(v3d->clk, 0);
+ clk_disable_unprepare(v3d->clk);
+
+ return 0;
--- /dev/null
+From 36faab69e8eebfb7f587bddef96040c59d3daa7c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 16 Oct 2024 11:31:04 +0100
+Subject: [PATCH 1324/1350] dts: bcm2712-rpi: Add aliases for the CSI/DSI I2Cs
+
+Older Pis arrange that the camera I2C ports appear as /dev/i2c-10. Add
+aliases so that on the Pi 5 family, i2c_csi_dsi0 becomes i2c-10 and
+i2c_csi_dsi1 becomes i2c-11. Only the I2C buses that appear on the
+40-pin header, i.e. I2C0 to I2C3, get a low bus number.
+
+Also add hints for our udev rules about which symlinks to create for
+backwards-compatibility with the previous bus numbers. Note that
+lower numbers have priority, so i2c-0 on CM5 masks i2c-11, forcing
+i2c-11 to be a symlink to i2c-0, not vice versa.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 ++
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 7 +++----
+ 3 files changed, 7 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
+@@ -255,12 +255,14 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f
+ pinctrl-0 = <&rp1_i2c6_38_39>;
+ pinctrl-names = "default";
+ clock-frequency = <100000>;
++ symlink = "i2c-6";
+ };
+
+ i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only
+ pinctrl-0 = <&rp1_i2c4_40_41>;
+ pinctrl-names = "default";
+ clock-frequency = <100000>;
++ symlink = "i2c-4";
+ };
+
+ i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -238,9 +238,11 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f
+ pinctrl-0 = <&rp1_i2c6_38_39>;
+ pinctrl-names = "default";
+ clock-frequency = <100000>;
++ symlink = "i2c-6";
+ };
+
+ i2c_csi_dsi1: &i2c0 { // Note: This is for MIPI1 connector
++ symlink = "i2c-11";
+ };
+
+ i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -117,12 +117,11 @@
+ i2c = &i2c_arm;
+ i2c0 = &i2c0;
+ i2c1 = &i2c1;
+- i2c10 = &i2c_rp1boot;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+- i2c4 = &i2c4;
+- i2c5 = &i2c5;
+- i2c6 = &i2c6;
++ i2c10 = &i2c_csi_dsi0;
++ i2c11 = &i2c_csi_dsi1;
++ i2c12 = &i2c_rp1boot;
+ mailbox = &mailbox;
+ mmc0 = &sdio1;
+ serial0 = &uart0;
--- /dev/null
+From e03a63b8d4a60ee49e3277ac7f7ea4c2998e7938 Mon Sep 17 00:00:00 2001
+From: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Date: Thu, 10 Oct 2024 14:52:52 +0100
+Subject: [PATCH 1325/1350] NotForUpstream: media: video-mux: Propagate
+ controls to source
+
+The i.MX8MP makes calls on it's source device to determine
+the link-frequency that should be configured on the CSI2 receiver.
+
+When the source is behind a video mux, we need to pass this call through
+to the connected device.
+
+Map the control handler of the source device to the video-mux,
+essentially proxying all controls on the mux to the device which has
+it's link enabled.
+
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+---
+ drivers/media/platform/video-mux.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/media/platform/video-mux.c
++++ b/drivers/media/platform/video-mux.c
+@@ -69,6 +69,7 @@ static int video_mux_link_setup(struct m
+ const struct media_pad *remote, u32 flags)
+ {
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
++ struct v4l2_subdev *source_sd;
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ u16 source_pad = entity->num_pads - 1;
+ int ret = 0;
+@@ -111,6 +112,10 @@ static int video_mux_link_setup(struct m
+ *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state,
+ vmux->active);
+ v4l2_subdev_unlock_state(sd_state);
++
++ source_sd = media_entity_to_v4l2_subdev(remote->entity);
++ vmux->subdev.ctrl_handler = source_sd->ctrl_handler;
++
+ } else {
+ if (vmux->active != local->index)
+ goto out;
+@@ -118,6 +123,8 @@ static int video_mux_link_setup(struct m
+ dev_dbg(sd->dev, "going inactive\n");
+ mux_control_deselect(vmux->mux);
+ vmux->active = -1;
++
++ vmux->subdev.ctrl_handler = NULL;
+ }
+
+ out:
--- /dev/null
+From cc0c868b51941f0ff2676970b70d388f1722e7fe Mon Sep 17 00:00:00 2001
+From: Paul Elder <paul.elder@ideasonboard.com>
+Date: Thu, 10 Oct 2024 14:52:53 +0100
+Subject: [PATCH 1326/1350] media: platform: video-mux: Fix mutex locking
+
+The current order of locking between the driver mutex and the v4l2
+subdev state lock causes a circuluar locking dependency when trying to
+set up a link. Fix this.
+
+Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+---
+ drivers/media/platform/video-mux.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/video-mux.c
++++ b/drivers/media/platform/video-mux.c
+@@ -70,6 +70,7 @@ static int video_mux_link_setup(struct m
+ {
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct v4l2_subdev *source_sd;
++ struct v4l2_subdev_state *sd_state;
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ u16 source_pad = entity->num_pads - 1;
+ int ret = 0;
+@@ -85,10 +86,10 @@ static int video_mux_link_setup(struct m
+ remote->entity->name, remote->index, local->entity->name,
+ local->index, flags & MEDIA_LNK_FL_ENABLED);
+
++ sd_state = v4l2_subdev_lock_and_get_active_state(sd);
+ mutex_lock(&vmux->lock);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+- struct v4l2_subdev_state *sd_state;
+ struct v4l2_mbus_framefmt *source_mbusformat;
+
+ if (vmux->active == local->index)
+@@ -106,12 +107,10 @@ static int video_mux_link_setup(struct m
+ vmux->active = local->index;
+
+ /* Propagate the active format to the source */
+- sd_state = v4l2_subdev_lock_and_get_active_state(sd);
+ source_mbusformat = v4l2_subdev_get_pad_format(sd, sd_state,
+ source_pad);
+ *source_mbusformat = *v4l2_subdev_get_pad_format(sd, sd_state,
+ vmux->active);
+- v4l2_subdev_unlock_state(sd_state);
+
+ source_sd = media_entity_to_v4l2_subdev(remote->entity);
+ vmux->subdev.ctrl_handler = source_sd->ctrl_handler;
+@@ -129,6 +128,7 @@ static int video_mux_link_setup(struct m
+
+ out:
+ mutex_unlock(&vmux->lock);
++ v4l2_subdev_unlock_state(sd_state);
+ return ret;
+ }
+
--- /dev/null
+From 664e835fd4886332ec3b3813de47102fe45e4fc3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 15 Oct 2024 16:05:57 +0100
+Subject: [PATCH 1327/1350] media: i2c: ov5647: Tidy up mode registers to make
+ the order common
+
+To make comparisons of the mode registers easier, put the registers
+for the binned and VGA modes in the same order as the others.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 33 ++++++++++++++-------------------
+ 1 file changed, 14 insertions(+), 19 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -344,6 +344,8 @@ static struct regval_list ov5647_2x2binn
+ {0x3036, 0x62},
+ {0x303c, 0x11},
+ {0x3106, 0xf5},
++ {0x3821, 0x01},
++ {0x3820, 0x41},
+ {0x3827, 0xec},
+ {0x370c, 0x03},
+ {0x3612, 0x59},
+@@ -416,8 +418,6 @@ static struct regval_list ov5647_2x2binn
+ {0x4837, 0x16},
+ {0x4800, 0x24},
+ {0x3503, 0x03},
+- {0x3820, 0x41},
+- {0x3821, 0x01},
+ {0x350a, 0x00},
+ {0x350b, 0x10},
+ {0x3500, 0x00},
+@@ -430,20 +430,27 @@ static struct regval_list ov5647_2x2binn
+ static struct regval_list ov5647_640x480_10bpp[] = {
+ {0x0100, 0x00},
+ {0x0103, 0x01},
+- {0x3035, 0x11},
++ {0x3034, 0x1a},
++ {0x3035, 0x21},
+ {0x3036, 0x46},
+ {0x303c, 0x11},
++ {0x3106, 0xf5},
+ {0x3821, 0x01},
+ {0x3820, 0x41},
++ {0x3827, 0xec},
+ {0x370c, 0x03},
+ {0x3612, 0x59},
+ {0x3618, 0x00},
+ {0x5000, 0x06},
+ {0x5003, 0x08},
+ {0x5a00, 0x08},
+- {0x3000, 0xff},
+- {0x3001, 0xff},
+- {0x3002, 0xff},
++ {0x3000, 0x00},
++ {0x3001, 0x00},
++ {0x3002, 0x00},
++ {0x3016, 0x08},
++ {0x3017, 0xe0},
++ {0x3018, 0x44},
++ {0x301c, 0xf8},
+ {0x301d, 0xf0},
+ {0x3a18, 0x00},
+ {0x3a19, 0xf8},
+@@ -469,6 +476,7 @@ static struct regval_list ov5647_640x480
+ {0x3632, 0xe2},
+ {0x3633, 0x23},
+ {0x3634, 0x44},
++ {0x3636, 0x06},
+ {0x3620, 0x64},
+ {0x3621, 0xe0},
+ {0x3600, 0x37},
+@@ -497,19 +505,6 @@ static struct regval_list ov5647_640x480
+ {0x4001, 0x02},
+ {0x4004, 0x02},
+ {0x4000, 0x09},
+- {0x3000, 0x00},
+- {0x3001, 0x00},
+- {0x3002, 0x00},
+- {0x3017, 0xe0},
+- {0x301c, 0xfc},
+- {0x3636, 0x06},
+- {0x3016, 0x08},
+- {0x3827, 0xec},
+- {0x3018, 0x44},
+- {0x3035, 0x21},
+- {0x3106, 0xf5},
+- {0x3034, 0x1a},
+- {0x301c, 0xf8},
+ {0x4800, 0x34},
+ {0x3503, 0x03},
+ {0x0100, 0x01},
--- /dev/null
+From aef1e51838641a602e0ab60523283f8a6e36a725 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 15 Oct 2024 17:02:39 +0100
+Subject: [PATCH 1328/1350] media: i2c: ov5647: Separate out the common
+ registers.
+
+There are many registers in common between all the modes.
+Pull those out into one common table.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 243 ++++++++-----------------------------
+ 1 file changed, 50 insertions(+), 193 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -162,22 +162,16 @@ static const struct regval_list sensor_o
+ {0x3002, 0xe4},
+ };
+
+-static struct regval_list ov5647_2592x1944_10bpp[] = {
++static struct regval_list ov5647_common_regs[] = {
+ {0x0100, 0x00},
+ {0x0103, 0x01},
+ {0x3034, 0x1a},
+ {0x3035, 0x21},
+- {0x3036, 0x69},
+ {0x303c, 0x11},
+ {0x3106, 0xf5},
+- {0x3821, 0x00},
+- {0x3820, 0x00},
+ {0x3827, 0xec},
+ {0x370c, 0x03},
+- {0x3612, 0x5b},
+- {0x3618, 0x04},
+ {0x5000, 0x06},
+- {0x5002, 0x41},
+ {0x5003, 0x08},
+ {0x5a00, 0x08},
+ {0x3000, 0x00},
+@@ -192,24 +186,6 @@ static struct regval_list ov5647_2592x19
+ {0x3a19, 0xf8},
+ {0x3c01, 0x80},
+ {0x3b07, 0x0c},
+- {0x3814, 0x11},
+- {0x3815, 0x11},
+- {0x3708, 0x64},
+- {0x3709, 0x12},
+- {0x3808, 0x0a},
+- {0x3809, 0x20},
+- {0x380a, 0x07},
+- {0x380b, 0x98},
+- {0x3800, 0x00},
+- {0x3801, 0x00},
+- {0x3802, 0x00},
+- {0x3803, 0x00},
+- {0x3804, 0x0a},
+- {0x3805, 0x3f},
+- {0x3806, 0x07},
+- {0x3807, 0xa3},
+- {0x3811, 0x10},
+- {0x3813, 0x06},
+ {0x3630, 0x2e},
+ {0x3632, 0xe2},
+ {0x3633, 0x23},
+@@ -229,11 +205,6 @@ static struct regval_list ov5647_2592x19
+ {0x3f06, 0x10},
+ {0x3f01, 0x0a},
+ {0x3a08, 0x01},
+- {0x3a09, 0x28},
+- {0x3a0a, 0x00},
+- {0x3a0b, 0xf6},
+- {0x3a0d, 0x08},
+- {0x3a0e, 0x06},
+ {0x3a0f, 0x58},
+ {0x3a10, 0x50},
+ {0x3a1b, 0x58},
+@@ -241,52 +212,57 @@ static struct regval_list ov5647_2592x19
+ {0x3a11, 0x60},
+ {0x3a1f, 0x28},
+ {0x4001, 0x02},
+- {0x4004, 0x04},
+ {0x4000, 0x09},
++ {0x3503, 0x03},
++};
++
++static struct regval_list ov5647_2592x1944_10bpp[] = {
++ {0x3036, 0x69},
++ {0x3821, 0x00},
++ {0x3820, 0x00},
++ {0x3612, 0x5b},
++ {0x3618, 0x04},
++ {0x5002, 0x41},
++ {0x3814, 0x11},
++ {0x3815, 0x11},
++ {0x3708, 0x64},
++ {0x3709, 0x12},
++ {0x3800, 0x00},
++ {0x3801, 0x00},
++ {0x3802, 0x00},
++ {0x3803, 0x00},
++ {0x3804, 0x0a},
++ {0x3805, 0x3f},
++ {0x3806, 0x07},
++ {0x3807, 0xa3},
++ {0x3808, 0x0a},
++ {0x3809, 0x20},
++ {0x380a, 0x07},
++ {0x380b, 0x98},
++ {0x3811, 0x10},
++ {0x3813, 0x06},
++ {0x3a09, 0x28},
++ {0x3a0a, 0x00},
++ {0x3a0b, 0xf6},
++ {0x3a0d, 0x08},
++ {0x3a0e, 0x06},
++ {0x4004, 0x04},
+ {0x4837, 0x19},
+ {0x4800, 0x24},
+- {0x3503, 0x03},
+ {0x0100, 0x01},
+ };
+
+ static struct regval_list ov5647_1080p30_10bpp[] = {
+- {0x0100, 0x00},
+- {0x0103, 0x01},
+- {0x3034, 0x1a},
+- {0x3035, 0x21},
+ {0x3036, 0x62},
+- {0x303c, 0x11},
+- {0x3106, 0xf5},
+ {0x3821, 0x00},
+ {0x3820, 0x00},
+- {0x3827, 0xec},
+- {0x370c, 0x03},
+ {0x3612, 0x5b},
+ {0x3618, 0x04},
+- {0x5000, 0x06},
+ {0x5002, 0x41},
+- {0x5003, 0x08},
+- {0x5a00, 0x08},
+- {0x3000, 0x00},
+- {0x3001, 0x00},
+- {0x3002, 0x00},
+- {0x3016, 0x08},
+- {0x3017, 0xe0},
+- {0x3018, 0x44},
+- {0x301c, 0xf8},
+- {0x301d, 0xf0},
+- {0x3a18, 0x00},
+- {0x3a19, 0xf8},
+- {0x3c01, 0x80},
+- {0x3b07, 0x0c},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3708, 0x64},
+ {0x3709, 0x12},
+- {0x3808, 0x07},
+- {0x3809, 0x80},
+- {0x380a, 0x04},
+- {0x380b, 0x38},
+ {0x3800, 0x01},
+ {0x3801, 0x5c},
+ {0x3802, 0x01},
+@@ -295,77 +271,30 @@ static struct regval_list ov5647_1080p30
+ {0x3805, 0xe3},
+ {0x3806, 0x05},
+ {0x3807, 0xf1},
++ {0x3808, 0x07},
++ {0x3809, 0x80},
++ {0x380a, 0x04},
++ {0x380b, 0x38},
+ {0x3811, 0x04},
+ {0x3813, 0x02},
+- {0x3630, 0x2e},
+- {0x3632, 0xe2},
+- {0x3633, 0x23},
+- {0x3634, 0x44},
+- {0x3636, 0x06},
+- {0x3620, 0x64},
+- {0x3621, 0xe0},
+- {0x3600, 0x37},
+- {0x3704, 0xa0},
+- {0x3703, 0x5a},
+- {0x3715, 0x78},
+- {0x3717, 0x01},
+- {0x3731, 0x02},
+- {0x370b, 0x60},
+- {0x3705, 0x1a},
+- {0x3f05, 0x02},
+- {0x3f06, 0x10},
+- {0x3f01, 0x0a},
+- {0x3a08, 0x01},
+ {0x3a09, 0x4b},
+ {0x3a0a, 0x01},
+ {0x3a0b, 0x13},
+ {0x3a0d, 0x04},
+ {0x3a0e, 0x03},
+- {0x3a0f, 0x58},
+- {0x3a10, 0x50},
+- {0x3a1b, 0x58},
+- {0x3a1e, 0x50},
+- {0x3a11, 0x60},
+- {0x3a1f, 0x28},
+- {0x4001, 0x02},
+ {0x4004, 0x04},
+- {0x4000, 0x09},
+ {0x4837, 0x19},
+ {0x4800, 0x34},
+- {0x3503, 0x03},
+ {0x0100, 0x01},
+ };
+
+ static struct regval_list ov5647_2x2binned_10bpp[] = {
+- {0x0100, 0x00},
+- {0x0103, 0x01},
+- {0x3034, 0x1a},
+- {0x3035, 0x21},
+ {0x3036, 0x62},
+- {0x303c, 0x11},
+- {0x3106, 0xf5},
+ {0x3821, 0x01},
+ {0x3820, 0x41},
+- {0x3827, 0xec},
+- {0x370c, 0x03},
+ {0x3612, 0x59},
+ {0x3618, 0x00},
+- {0x5000, 0x06},
+ {0x5002, 0x41},
+- {0x5003, 0x08},
+- {0x5a00, 0x08},
+- {0x3000, 0x00},
+- {0x3001, 0x00},
+- {0x3002, 0x00},
+- {0x3016, 0x08},
+- {0x3017, 0xe0},
+- {0x3018, 0x44},
+- {0x301c, 0xf8},
+- {0x301d, 0xf0},
+- {0x3a18, 0x00},
+- {0x3a19, 0xf8},
+- {0x3c01, 0x80},
+- {0x3b07, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+@@ -382,42 +311,14 @@ static struct regval_list ov5647_2x2binn
+ {0x3813, 0x06},
+ {0x3814, 0x31},
+ {0x3815, 0x31},
+- {0x3630, 0x2e},
+- {0x3632, 0xe2},
+- {0x3633, 0x23},
+- {0x3634, 0x44},
+- {0x3636, 0x06},
+- {0x3620, 0x64},
+- {0x3621, 0xe0},
+- {0x3600, 0x37},
+- {0x3704, 0xa0},
+- {0x3703, 0x5a},
+- {0x3715, 0x78},
+- {0x3717, 0x01},
+- {0x3731, 0x02},
+- {0x370b, 0x60},
+- {0x3705, 0x1a},
+- {0x3f05, 0x02},
+- {0x3f06, 0x10},
+- {0x3f01, 0x0a},
+- {0x3a08, 0x01},
+ {0x3a09, 0x28},
+ {0x3a0a, 0x00},
+ {0x3a0b, 0xf6},
+ {0x3a0d, 0x08},
+ {0x3a0e, 0x06},
+- {0x3a0f, 0x58},
+- {0x3a10, 0x50},
+- {0x3a1b, 0x58},
+- {0x3a1e, 0x50},
+- {0x3a11, 0x60},
+- {0x3a1f, 0x28},
+- {0x4001, 0x02},
+ {0x4004, 0x04},
+- {0x4000, 0x09},
+ {0x4837, 0x16},
+ {0x4800, 0x24},
+- {0x3503, 0x03},
+ {0x350a, 0x00},
+ {0x350b, 0x10},
+ {0x3500, 0x00},
+@@ -428,42 +329,15 @@ static struct regval_list ov5647_2x2binn
+ };
+
+ static struct regval_list ov5647_640x480_10bpp[] = {
+- {0x0100, 0x00},
+- {0x0103, 0x01},
+- {0x3034, 0x1a},
+- {0x3035, 0x21},
+ {0x3036, 0x46},
+- {0x303c, 0x11},
+- {0x3106, 0xf5},
+ {0x3821, 0x01},
+ {0x3820, 0x41},
+- {0x3827, 0xec},
+- {0x370c, 0x03},
+ {0x3612, 0x59},
+ {0x3618, 0x00},
+- {0x5000, 0x06},
+- {0x5003, 0x08},
+- {0x5a00, 0x08},
+- {0x3000, 0x00},
+- {0x3001, 0x00},
+- {0x3002, 0x00},
+- {0x3016, 0x08},
+- {0x3017, 0xe0},
+- {0x3018, 0x44},
+- {0x301c, 0xf8},
+- {0x301d, 0xf0},
+- {0x3a18, 0x00},
+- {0x3a19, 0xf8},
+- {0x3c01, 0x80},
+- {0x3b07, 0x0c},
+ {0x3814, 0x35},
+ {0x3815, 0x35},
+ {0x3708, 0x64},
+ {0x3709, 0x52},
+- {0x3808, 0x02},
+- {0x3809, 0x80},
+- {0x380a, 0x01},
+- {0x380b, 0xe0},
+ {0x3800, 0x00},
+ {0x3801, 0x10},
+ {0x3802, 0x00},
+@@ -472,41 +346,17 @@ static struct regval_list ov5647_640x480
+ {0x3805, 0x2f},
+ {0x3806, 0x07},
+ {0x3807, 0x9f},
+- {0x3630, 0x2e},
+- {0x3632, 0xe2},
+- {0x3633, 0x23},
+- {0x3634, 0x44},
+- {0x3636, 0x06},
+- {0x3620, 0x64},
+- {0x3621, 0xe0},
+- {0x3600, 0x37},
+- {0x3704, 0xa0},
+- {0x3703, 0x5a},
+- {0x3715, 0x78},
+- {0x3717, 0x01},
+- {0x3731, 0x02},
+- {0x370b, 0x60},
+- {0x3705, 0x1a},
+- {0x3f05, 0x02},
+- {0x3f06, 0x10},
+- {0x3f01, 0x0a},
+- {0x3a08, 0x01},
++ {0x3808, 0x02},
++ {0x3809, 0x80},
++ {0x380a, 0x01},
++ {0x380b, 0xe0},
+ {0x3a09, 0x2e},
+ {0x3a0a, 0x00},
+ {0x3a0b, 0xfb},
+ {0x3a0d, 0x02},
+ {0x3a0e, 0x01},
+- {0x3a0f, 0x58},
+- {0x3a10, 0x50},
+- {0x3a1b, 0x58},
+- {0x3a1e, 0x50},
+- {0x3a11, 0x60},
+- {0x3a1f, 0x28},
+- {0x4001, 0x02},
+ {0x4004, 0x02},
+- {0x4000, 0x09},
+ {0x4800, 0x34},
+- {0x3503, 0x03},
+ {0x0100, 0x01},
+ };
+
+@@ -715,6 +565,13 @@ static int ov5647_set_mode(struct v4l2_s
+ if (ret < 0)
+ return ret;
+
++ ret = ov5647_write_array(sd, ov5647_common_regs,
++ ARRAY_SIZE(ov5647_common_regs));
++ if (ret < 0) {
++ dev_err(&client->dev, "write sensor common regs error\n");
++ return ret;
++ }
++
+ ret = ov5647_write_array(sd, sensor->mode->reg_list,
+ sensor->mode->num_regs);
+ if (ret < 0) {
--- /dev/null
+From 35e5c3d706cbe686e3468d9f24ae999212553893 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 15 Oct 2024 19:36:13 +0100
+Subject: [PATCH 1329/1350] media: i2c: ov5647: Use the same PLL config for
+ full, 1080p, and binned modes
+
+In order to simplify the driver slightly, use the same PLL
+configuration, and hence pixel rate and link frequency (to be
+added) for the full, 1080p, and binned modes.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -253,7 +253,7 @@ static struct regval_list ov5647_2592x19
+ };
+
+ static struct regval_list ov5647_1080p30_10bpp[] = {
+- {0x3036, 0x62},
++ {0x3036, 0x69},
+ {0x3821, 0x00},
+ {0x3820, 0x00},
+ {0x3612, 0x5b},
+@@ -289,7 +289,7 @@ static struct regval_list ov5647_1080p30
+ };
+
+ static struct regval_list ov5647_2x2binned_10bpp[] = {
+- {0x3036, 0x62},
++ {0x3036, 0x69},
+ {0x3821, 0x01},
+ {0x3820, 0x41},
+ {0x3612, 0x59},
+@@ -397,7 +397,7 @@ static const struct ov5647_mode ov5647_m
+ .width = 1928,
+ .height = 1080,
+ },
+- .pixel_rate = 81666700,
++ .pixel_rate = 87500000,
+ .hts = 2416,
+ .vts = 0x450,
+ .reg_list = ov5647_1080p30_10bpp,
+@@ -418,7 +418,7 @@ static const struct ov5647_mode ov5647_m
+ .width = 2592,
+ .height = 1944,
+ },
+- .pixel_rate = 81666700,
++ .pixel_rate = 87500000,
+ .hts = 1896,
+ .vts = 0x59b,
+ .reg_list = ov5647_2x2binned_10bpp,
--- /dev/null
+From 84ab77459e61c648299d32464127b89ca65de40a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 15 Oct 2024 19:46:01 +0100
+Subject: [PATCH 1330/1350] media: i2c: ov5647: Add V4L2_CID_LINK_FREQUENCY
+ control
+
+The link frequency can vary between modes, so add it as a
+control.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 25 ++++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -97,6 +97,13 @@ static const char * const ov5647_supply_
+
+ #define OV5647_NUM_SUPPLIES ARRAY_SIZE(ov5647_supply_names)
+
++#define FREQ_INDEX_FULL 0
++#define FREQ_INDEX_VGA 1
++static const s64 ov5647_link_freqs[] = {
++ [FREQ_INDEX_FULL] = 218500000,
++ [FREQ_INDEX_VGA] = 208333000,
++};
++
+ struct regval_list {
+ u16 addr;
+ u8 data;
+@@ -106,6 +113,7 @@ struct ov5647_mode {
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+ u64 pixel_rate;
++ unsigned int link_freq_index;
+ int hts;
+ int vts;
+ const struct regval_list *reg_list;
+@@ -128,6 +136,7 @@ struct ov5647 {
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *link_freq;
+ bool streaming;
+ };
+
+@@ -377,6 +386,7 @@ static const struct ov5647_mode ov5647_m
+ .height = 1944
+ },
+ .pixel_rate = 87500000,
++ .link_freq_index = FREQ_INDEX_FULL,
+ .hts = 2844,
+ .vts = 0x7b0,
+ .reg_list = ov5647_2592x1944_10bpp,
+@@ -398,6 +408,7 @@ static const struct ov5647_mode ov5647_m
+ .height = 1080,
+ },
+ .pixel_rate = 87500000,
++ .link_freq_index = FREQ_INDEX_FULL,
+ .hts = 2416,
+ .vts = 0x450,
+ .reg_list = ov5647_1080p30_10bpp,
+@@ -419,6 +430,7 @@ static const struct ov5647_mode ov5647_m
+ .height = 1944,
+ },
+ .pixel_rate = 87500000,
++ .link_freq_index = FREQ_INDEX_FULL,
+ .hts = 1896,
+ .vts = 0x59b,
+ .reg_list = ov5647_2x2binned_10bpp,
+@@ -440,6 +452,7 @@ static const struct ov5647_mode ov5647_m
+ .height = 1920,
+ },
+ .pixel_rate = 55000000,
++ .link_freq_index = FREQ_INDEX_VGA,
+ .hts = 1852,
+ .vts = 0x1f8,
+ .reg_list = ov5647_640x480_10bpp,
+@@ -943,6 +956,8 @@ static int ov5647_set_pad_fmt(struct v4l
+ sensor->exposure->minimum,
+ exposure_max, sensor->exposure->step,
+ exposure_def);
++
++ __v4l2_ctrl_s_ctrl(sensor->link_freq, mode->link_freq_index);
+ }
+ *fmt = mode->format;
+ /* The code we pass back must reflect the current h/vflips. */
+@@ -1248,7 +1263,7 @@ static int ov5647_init_controls(struct o
+ int hblank, exposure_max, exposure_def;
+ struct v4l2_fwnode_device_properties props;
+
+- v4l2_ctrl_handler_init(&sensor->ctrls, 9);
++ v4l2_ctrl_handler_init(&sensor->ctrls, 10);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
+@@ -1308,6 +1323,14 @@ static int ov5647_init_controls(struct o
+ if (sensor->vflip)
+ sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
++ sensor->link_freq =
++ v4l2_ctrl_new_int_menu(&sensor->ctrls, &ov5647_ctrl_ops,
++ V4L2_CID_LINK_FREQ,
++ ARRAY_SIZE(ov5647_link_freqs) - 1, 0,
++ ov5647_link_freqs);
++ if (sensor->link_freq)
++ sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+ v4l2_fwnode_device_parse(dev, &props);
+
+ v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &ov5647_ctrl_ops,
--- /dev/null
+From b80d5d1d5148a89121e3759bb4caa27e7ee1cf05 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 15 Oct 2024 11:22:53 +0100
+Subject: [PATCH 1331/1350] mmc: don't reference requests after finishing them
+
+Posted write tracking introduced in the commit below raced with re-use
+of the requests between completion and submission, potentially causing
+underflow of the pending write count.
+
+Fixes: e6c1e862b2b8 ("mmc: restrict posted write counts for SD cards in CQ mode")
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/block.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -1524,6 +1524,7 @@ static void mmc_blk_cqe_complete_rq(stru
+ struct request_queue *q = req->q;
+ struct mmc_host *host = mq->card->host;
+ enum mmc_issue_type issue_type = mmc_issue_type(mq, req);
++ bool write = req_op(req) == REQ_OP_WRITE;
+ unsigned long flags;
+ bool put_card;
+ int err;
+@@ -1555,7 +1556,7 @@ static void mmc_blk_cqe_complete_rq(stru
+
+ spin_lock_irqsave(&mq->lock, flags);
+
+- if (req_op(req) == REQ_OP_WRITE)
++ if (write)
+ mq->pending_writes--;
+ mq->in_flight[issue_type] -= 1;
+
+@@ -2170,15 +2171,16 @@ static void mmc_blk_mq_poll_completion(s
+ }
+
+ static void mmc_blk_mq_dec_in_flight(struct mmc_queue *mq, enum mmc_issue_type issue_type,
+- struct request *req)
++ bool write)
+ {
+ unsigned long flags;
+ bool put_card;
+
+ spin_lock_irqsave(&mq->lock, flags);
+
+- if (req_op(req) == REQ_OP_WRITE)
++ if (write)
+ mq->pending_writes--;
++
+ mq->in_flight[issue_type] -= 1;
+
+ put_card = (mmc_tot_in_flight(mq) == 0);
+@@ -2193,6 +2195,7 @@ static void mmc_blk_mq_post_req(struct m
+ bool can_sleep)
+ {
+ enum mmc_issue_type issue_type = mmc_issue_type(mq, req);
++ bool write = req_op(req) == REQ_OP_WRITE;
+ struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
+ struct mmc_request *mrq = &mqrq->brq.mrq;
+ struct mmc_host *host = mq->card->host;
+@@ -2212,7 +2215,7 @@ static void mmc_blk_mq_post_req(struct m
+ blk_mq_complete_request(req);
+ }
+
+- mmc_blk_mq_dec_in_flight(mq, issue_type, req);
++ mmc_blk_mq_dec_in_flight(mq, issue_type, write);
+ }
+
+ void mmc_blk_mq_recovery(struct mmc_queue *mq)
--- /dev/null
+From 4d702b5c68d6449d81281677f71de557f64cdce5 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 15 Oct 2024 14:35:42 +0100
+Subject: [PATCH 1333/1350] mmc: quirks: disable cache on more known-bad
+ Sandisk card date ranges
+
+Cards with manufacture dates in 2019 and 2020 have been seen in the wild
+that hang indefinitely if issued a cache flush command in CQ mode.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/quirks.h | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/mmc/core/quirks.h
++++ b/drivers/mmc/core/quirks.h
+@@ -33,6 +33,18 @@ static const struct mmc_fixup __maybe_un
+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
+ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
+
++ /*
++ * Early Sandisk Extreme and Extreme Pro A2 cards never finish SD cache
++ * flush in CQ mode. Latest card date this was seen on is 10/2020.
++ */
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_SANDISK_SD, 0x5344, 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_SANDISK_SD, 0x5344, 2020, CID_MONTH_ANY,
++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
+ END_FIXUP
+ };
+
--- /dev/null
+From 73ddacb555c5ef1f063f44b4ec3268da899ff8b5 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 18 Oct 2024 13:11:11 +0100
+Subject: [PATCH 1334/1350] mmc: block: disable CQ on SD cards when doing
+ non-Discard erase
+
+Only CMD38 with Arg=0x1 (Discard) is supported when in CQ mode, so
+turn it off before issuing a non-discard erase op.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/block.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -1177,12 +1177,26 @@ static void mmc_blk_issue_erase_rq(struc
+ unsigned int from, nr;
+ int err = 0;
+ blk_status_t status = BLK_STS_OK;
++ bool restart_cmdq = false;
+
+ if (!mmc_can_erase(card)) {
+ status = BLK_STS_NOTSUPP;
+ goto fail;
+ }
+
++ /*
++ * Only Discard ops are supported with SD cards in CQ mode
++ * (SD Physical Spec v9.00 4.19.2)
++ */
++ if (mmc_card_sd(card) && card->ext_csd.cmdq_en && erase_arg != SD_DISCARD_ARG) {
++ restart_cmdq = true;
++ err = mmc_sd_cmdq_disable(card);
++ if (err) {
++ status = BLK_STS_IOERR;
++ goto fail;
++ }
++ }
++
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+@@ -1203,6 +1217,11 @@ static void mmc_blk_issue_erase_rq(struc
+ status = BLK_STS_IOERR;
+ else
+ mmc_blk_reset_success(md, type);
++
++ if (restart_cmdq)
++ err = mmc_sd_cmdq_enable(card);
++ if (err)
++ status = BLK_STS_IOERR;
+ fail:
+ blk_mq_end_request(req, status);
+ }
--- /dev/null
+From 48a15bc46004025776880a091d47a22e03449acc Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 18 Oct 2024 14:06:01 +0100
+Subject: [PATCH 1335/1350] DTS: bcm2712; re-enable SD slot CQE by default on
+ Pi 5
+
+This reverts commit 1b92c9369569137d8f2b8bf82884a05999e1f73b.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 6 +++---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 1 +
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -378,9 +378,9 @@ Params:
+ non-lite SKU of CM4).
+ (default "on")
+
+- sd_cqe Use to enable Command Queueing on the SD
+- interface for faster Class A2 card performance
+- (Pi 5 only, default "off")
++ sd_cqe Set to "off" to disable Command Queueing if you
++ have an incompatible Class A2 SD card
++ (Pi 5 only, default "on")
+
+ sd_overclock Clock (in MHz) to use when the MMC framework
+ requests 50MHz
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -365,6 +365,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g
+ sd-uhs-sdr50;
+ sd-uhs-ddr50;
+ sd-uhs-sdr104;
++ supports-cqe;
+ cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>;
+ //no-1-8-v;
+ status = "okay";
--- /dev/null
+From 527d6f5a7861e24c60d41e9706ec4589165321d1 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 18 Oct 2024 16:01:28 +0100
+Subject: [PATCH 1336/1350] mmc: quirks: add MMC_QUIRK_BROKEN_ERASE for
+ Phison/Integral cards
+
+Recent Integral cards end up with corrupt sectors after a flash erase.
+This covers sizes for the A2 range, which can't be differentiated from
+the A1 range which might not have the same issue.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/quirks.h | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/mmc/core/quirks.h
++++ b/drivers/mmc/core/quirks.h
+@@ -162,6 +162,15 @@ static const struct mmc_fixup __maybe_un
+ MMC_FIXUP("SD32G", 0x41, 0x3432, add_quirk, MMC_QUIRK_ERASE_BROKEN),
+ MMC_FIXUP("SD64G", 0x41, 0x3432, add_quirk, MMC_QUIRK_ERASE_BROKEN),
+
++ /*
++ * Larger Integral SD cards using rebranded Phison controllers trash
++ * nearby flash blocks after erases.
++ */
++ MMC_FIXUP("SD64G", 0x27, 0x5048, add_quirk, MMC_QUIRK_ERASE_BROKEN),
++ MMC_FIXUP("SD128", 0x27, 0x5048, add_quirk, MMC_QUIRK_ERASE_BROKEN),
++ MMC_FIXUP("SD256", 0x27, 0x5048, add_quirk, MMC_QUIRK_ERASE_BROKEN),
++ MMC_FIXUP("SD512", 0x27, 0x5048, add_quirk, MMC_QUIRK_ERASE_BROKEN),
++
+ END_FIXUP
+ };
+
--- /dev/null
+From 25b2b36dea57fd86b63df2e253b376532d314a30 Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Sat, 20 Jan 2024 21:32:28 -0800
+Subject: [PATCH 1337/1350] Input: matrix_keypad - avoid repeatedly converting
+ GPIO to IRQ
+
+commit a96fb711c6be76bcfbcf594a865002fa7c0eb525 upstream.
+
+There is no need to do conversion from GPIOs to interrupt numbers.
+Convert row GPIOs to interrupt numbers once in probe() and use
+this information when the driver needs to enable or disable given
+interrupt line.
+
+Link: https://lore.kernel.org/r/20240121053232.276968-1-dmitry.torokhov@gmail.com
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+---
+ drivers/input/keyboard/matrix_keypad.c | 48 ++++++++++++++------------
+ 1 file changed, 25 insertions(+), 23 deletions(-)
+
+--- a/drivers/input/keyboard/matrix_keypad.c
++++ b/drivers/input/keyboard/matrix_keypad.c
+@@ -27,6 +27,7 @@ struct matrix_keypad {
+ const struct matrix_keypad_platform_data *pdata;
+ struct input_dev *input_dev;
+ unsigned int row_shift;
++ unsigned int row_irqs[MATRIX_MAX_ROWS];
+
+ DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
+
+@@ -92,7 +93,7 @@ static void enable_row_irqs(struct matri
+ enable_irq(pdata->clustered_irq);
+ else {
+ for (i = 0; i < pdata->num_row_gpios; i++)
+- enable_irq(gpio_to_irq(pdata->row_gpios[i]));
++ enable_irq(keypad->row_irqs[i]);
+ }
+ }
+
+@@ -105,7 +106,7 @@ static void disable_row_irqs(struct matr
+ disable_irq_nosync(pdata->clustered_irq);
+ else {
+ for (i = 0; i < pdata->num_row_gpios; i++)
+- disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
++ disable_irq_nosync(keypad->row_irqs[i]);
+ }
+ }
+
+@@ -233,7 +234,6 @@ static void matrix_keypad_stop(struct in
+ static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
+ {
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+- unsigned int gpio;
+ int i;
+
+ if (pdata->clustered_irq > 0) {
+@@ -241,21 +241,16 @@ static void matrix_keypad_enable_wakeup(
+ keypad->gpio_all_disabled = true;
+ } else {
+
+- for (i = 0; i < pdata->num_row_gpios; i++) {
+- if (!test_bit(i, keypad->disabled_gpios)) {
+- gpio = pdata->row_gpios[i];
+-
+- if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ if (!test_bit(i, keypad->disabled_gpios))
++ if (enable_irq_wake(keypad->row_irqs[i]) == 0)
+ __set_bit(i, keypad->disabled_gpios);
+- }
+- }
+ }
+ }
+
+ static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
+ {
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+- unsigned int gpio;
+ int i;
+
+ if (pdata->clustered_irq > 0) {
+@@ -264,12 +259,9 @@ static void matrix_keypad_disable_wakeup
+ keypad->gpio_all_disabled = false;
+ }
+ } else {
+- for (i = 0; i < pdata->num_row_gpios; i++) {
+- if (test_and_clear_bit(i, keypad->disabled_gpios)) {
+- gpio = pdata->row_gpios[i];
+- disable_irq_wake(gpio_to_irq(gpio));
+- }
+- }
++ for (i = 0; i < pdata->num_row_gpios; i++)
++ if (test_and_clear_bit(i, keypad->disabled_gpios))
++ disable_irq_wake(keypad->row_irqs[i]);
+ }
+ }
+
+@@ -306,7 +298,7 @@ static int matrix_keypad_init_gpio(struc
+ struct matrix_keypad *keypad)
+ {
+ const struct matrix_keypad_platform_data *pdata = keypad->pdata;
+- int i, err;
++ int i, irq, err;
+
+ /* initialized strobe lines as outputs, activated */
+ for (i = 0; i < pdata->num_col_gpios; i++) {
+@@ -345,11 +337,19 @@ static int matrix_keypad_init_gpio(struc
+ }
+ } else {
+ for (i = 0; i < pdata->num_row_gpios; i++) {
+- err = request_any_context_irq(
+- gpio_to_irq(pdata->row_gpios[i]),
++ irq = gpio_to_irq(pdata->row_gpios[i]);
++ if (irq < 0) {
++ err = irq;
++ dev_err(&pdev->dev,
++ "Unable to convert GPIO line %i to irq: %d\n",
++ pdata->row_gpios[i], err);
++ goto err_free_irqs;
++ }
++
++ err = request_any_context_irq(irq,
+ matrix_keypad_interrupt,
+ IRQF_TRIGGER_RISING |
+- IRQF_TRIGGER_FALLING,
++ IRQF_TRIGGER_FALLING,
+ "matrix-keypad", keypad);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+@@ -357,6 +357,8 @@ static int matrix_keypad_init_gpio(struc
+ pdata->row_gpios[i]);
+ goto err_free_irqs;
+ }
++
++ keypad->row_irqs[i] = irq;
+ }
+ }
+
+@@ -366,7 +368,7 @@ static int matrix_keypad_init_gpio(struc
+
+ err_free_irqs:
+ while (--i >= 0)
+- free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
++ free_irq(keypad->row_irqs[i], keypad);
+ i = pdata->num_row_gpios;
+ err_free_rows:
+ while (--i >= 0)
+@@ -388,7 +390,7 @@ static void matrix_keypad_free_gpio(stru
+ free_irq(pdata->clustered_irq, keypad);
+ } else {
+ for (i = 0; i < pdata->num_row_gpios; i++)
+- free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
++ free_irq(keypad->row_irqs[i], keypad);
+ }
+
+ for (i = 0; i < pdata->num_row_gpios; i++)
--- /dev/null
+From b1a0f9c705912ec0434645f2d2bcf914c635564d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 17 May 2024 11:40:23 -0300
+Subject: [PATCH 1338/1350] numa: Add simple generic NUMA emulation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add some common code for splitting the memory into N emulated NUMA memory
+nodes.
+
+Individual architecture can then enable selecting this option and use the
+existing numa=fake=<N> kernel argument to enable it.
+
+Memory is always split into equally sized chunks.
+
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+Co-developed-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: “Rafael J. Wysocki" <rafael@kernel.org>
+---
+ drivers/base/Kconfig | 7 ++++
+ drivers/base/Makefile | 1 +
+ drivers/base/arch_numa.c | 6 ++++
+ drivers/base/numa_emulation.c | 67 +++++++++++++++++++++++++++++++++++
+ drivers/base/numa_emulation.h | 21 +++++++++++
+ 5 files changed, 102 insertions(+)
+ create mode 100644 drivers/base/numa_emulation.c
+ create mode 100644 drivers/base/numa_emulation.h
+
+--- a/drivers/base/Kconfig
++++ b/drivers/base/Kconfig
+@@ -230,6 +230,13 @@ config GENERIC_ARCH_NUMA
+ Enable support for generic NUMA implementation. Currently, RISC-V
+ and ARM64 use it.
+
++config GENERIC_ARCH_NUMA_EMULATION
++ bool
++ depends on GENERIC_ARCH_NUMA
++ help
++ Enable NUMA emulation. Note that NUMA emulation will only be used if
++ the machine has no NUMA node.
++
+ config FW_DEVLINK_SYNC_STATE_TIMEOUT
+ bool "sync_state() behavior defaults to timeout instead of strict"
+ help
+--- a/drivers/base/Makefile
++++ b/drivers/base/Makefile
+@@ -25,6 +25,7 @@ obj-$(CONFIG_DEV_COREDUMP) += devcoredum
+ obj-$(CONFIG_GENERIC_MSI_IRQ) += platform-msi.o
+ obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
+ obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
++obj-$(CONFIG_GENERIC_ARCH_NUMA_EMULATION) += numa_emulation.o
+ obj-$(CONFIG_ACPI) += physical_location.o
+
+ obj-y += test/
+--- a/drivers/base/arch_numa.c
++++ b/drivers/base/arch_numa.c
+@@ -15,6 +15,8 @@
+
+ #include <asm/sections.h>
+
++#include "numa_emulation.h"
++
+ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
+ EXPORT_SYMBOL(node_data);
+ nodemask_t numa_nodes_parsed __initdata;
+@@ -30,6 +32,8 @@ static __init int numa_parse_early_param
+ return -EINVAL;
+ if (str_has_prefix(opt, "off"))
+ numa_off = true;
++ if (str_has_prefix(opt, "fake="))
++ return numa_emu_cmdline(opt + 5);
+
+ return 0;
+ }
+@@ -471,6 +475,8 @@ void __init arch_numa_init(void)
+ return;
+ if (acpi_disabled && !numa_init(of_numa_init))
+ return;
++ if (!numa_init(numa_emu_init))
++ return;
+ }
+
+ numa_init(dummy_numa_init);
+--- /dev/null
++++ b/drivers/base/numa_emulation.c
+@@ -0,0 +1,67 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Simple NUMA emulation.
++ *
++ * Copyright © 2024 Raspberry Pi Ltd
++ *
++ * Author: Maíra Canal <mcanal@igalia.com>
++ * Author: Tvrtko Ursulin <tursulin@igalia.com>
++ */
++#include <linux/memblock.h>
++
++#include "numa_emulation.h"
++
++static unsigned int emu_nodes;
++
++int __init numa_emu_cmdline(char *str)
++{
++ int ret;
++
++ ret = kstrtouint(str, 10, &emu_nodes);
++ if (ret)
++ return ret;
++
++ if (emu_nodes > MAX_NUMNODES) {
++ pr_notice("numa=fake=%u too large, reducing to %u\n",
++ emu_nodes, MAX_NUMNODES);
++ emu_nodes = MAX_NUMNODES;
++ }
++
++ return 0;
++}
++
++int __init numa_emu_init(void)
++{
++ phys_addr_t start, end;
++ unsigned long size;
++ unsigned int i;
++ int ret;
++
++ if (!emu_nodes)
++ return -EINVAL;
++
++ start = memblock_start_of_DRAM();
++ end = memblock_end_of_DRAM() - 1;
++
++ size = DIV_ROUND_DOWN_ULL(end - start + 1, emu_nodes);
++ size = PAGE_ALIGN_DOWN(size);
++
++ for (i = 0; i < emu_nodes; i++) {
++ u64 s, e;
++
++ s = start + i * size;
++ e = s + size - 1;
++
++ if (i == (emu_nodes - 1) && e != end)
++ e = end;
++
++ pr_info("Faking a node at [mem %pap-%pap]\n", &s, &e);
++ ret = numa_add_memblk(i, s, e + 1);
++ if (ret) {
++ pr_err("Failed to add fake NUMA node %d!\n", i);
++ break;
++ }
++ }
++
++ return ret;
++}
+--- /dev/null
++++ b/drivers/base/numa_emulation.h
+@@ -0,0 +1,21 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * NUMA emulation header
++ *
++ * Copyright © 2024 Raspberry Pi Ltd
++ */
++
++#ifdef CONFIG_GENERIC_ARCH_NUMA_EMULATION
++int numa_emu_cmdline(char *str);
++int __init numa_emu_init(void);
++#else
++static inline int numa_emu_cmdline(char *str)
++{
++ return -EINVAL;
++}
++
++static int __init numa_emu_init(void)
++{
++ return -EOPNOTSUPP;
++}
++#endif /* CONFIG_NUMA_EMU */
--- /dev/null
+From 462346142b26dd1fd8f55655959e38b385acb1c1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 17 May 2024 11:40:34 -0300
+Subject: [PATCH 1339/1350] arm64/numa: Add NUMA emulation for ARM64
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Allow selecting NUMA emulation on arm64.
+
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Cc: Catalin Marinas <catalin.marinas@arm.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: “Rafael J. Wysocki" <rafael@kernel.org>
+---
+ arch/arm64/Kconfig | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/arch/arm64/Kconfig
++++ b/arch/arm64/Kconfig
+@@ -1500,6 +1500,16 @@ config NODES_SHIFT
+ Specify the maximum number of NUMA Nodes available on the target
+ system. Increases memory reserved to accommodate various tables.
+
++config NUMA_EMULATION
++ bool "NUMA emulation"
++ depends on NUMA
++ select GENERIC_ARCH_NUMA_EMULATION
++ help
++ Enable NUMA emulation support. A flat machine will be split into
++ virtual nodes when booted with "numa=fake=N", where N is the number
++ of nodes, the system RAM will be split into N equal chunks, and
++ assigned to each node.
++
+ source "kernel/Kconfig.hz"
+
+ config ARCH_SPARSEMEM_ENABLE
--- /dev/null
+From 4bbdd9335a4784743a5ac30697f24972219559c2 Mon Sep 17 00:00:00 2001
+From: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Date: Wed, 22 May 2024 17:12:16 +0100
+Subject: [PATCH 1340/1350] mm/numa: Allow override of kernel's default NUMA
+ policy
+
+Add numa_policy kernel argument to allow overriding the kernel's default
+NUMA policy at boot time.
+
+Syntax identical to what tmpfs accepts as it's mpol argument is accepted.
+
+Some examples:
+
+ numa_policy=interleave
+ numa_policy=interleave=skip-interleave
+ numa_policy=bind:0-3,5,7,9-15
+ numa_policy=bind=static:1-2
+
+Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+---
+ mm/mempolicy.c | 49 ++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 42 insertions(+), 7 deletions(-)
+
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -2974,7 +2974,9 @@ void __init numa_policy_init(void)
+ /* Reset policy of current process to default */
+ void numa_default_policy(void)
+ {
+- do_set_mempolicy(MPOL_DEFAULT, 0, NULL);
++ struct mempolicy *pol = &default_policy;
++
++ do_set_mempolicy(pol->mode, pol->flags, &pol->nodes);
+ }
+
+ /*
+@@ -2992,7 +2994,6 @@ static const char * const policy_modes[]
+ };
+
+
+-#ifdef CONFIG_TMPFS
+ /**
+ * mpol_parse_str - parse string to mempolicy, for tmpfs mpol mount option.
+ * @str: string containing mempolicy to parse
+@@ -3005,13 +3006,18 @@ static const char * const policy_modes[]
+ */
+ int mpol_parse_str(char *str, struct mempolicy **mpol)
+ {
+- struct mempolicy *new = NULL;
++ struct mempolicy *new;
+ unsigned short mode_flags;
+ nodemask_t nodes;
+ char *nodelist = strchr(str, ':');
+ char *flags = strchr(str, '=');
+ int err = 1, mode;
+
++ if (*mpol)
++ new = *mpol;
++ else
++ new = NULL;
++
+ if (flags)
+ *flags++ = '\0'; /* terminate mode string */
+
+@@ -3090,9 +3096,16 @@ int mpol_parse_str(char *str, struct mem
+ goto out;
+ }
+
+- new = mpol_new(mode, mode_flags, &nodes);
+- if (IS_ERR(new))
+- goto out;
++ if (!new) {
++ new = mpol_new(mode, mode_flags, &nodes);
++ if (IS_ERR(new))
++ goto out;
++ } else {
++ atomic_set(&new->refcnt, 1);
++ new->mode = mode;
++ new->flags = mode_flags;
++ new->home_node = NUMA_NO_NODE;
++ }
+
+ /*
+ * Save nodes for mpol_to_str() to show the tmpfs mount options
+@@ -3125,7 +3138,29 @@ out:
+ *mpol = new;
+ return err;
+ }
+-#endif /* CONFIG_TMPFS */
++
++static int __init setup_numapolicy(char *str)
++{
++ struct mempolicy pol = { }, *ppol = &pol;
++ char buf[128];
++ int ret;
++
++ if (str)
++ ret = mpol_parse_str(str, &ppol);
++ else
++ ret = -EINVAL;
++
++ if (!ret) {
++ default_policy = pol;
++ mpol_to_str(buf, sizeof(buf), &pol);
++ pr_info("NUMA default policy overridden to '%s'\n", buf);
++ } else {
++ pr_warn("Unable to parse numa_policy=\n");
++ }
++
++ return ret == 0;
++}
++__setup("numa_policy=", setup_numapolicy);
+
+ /**
+ * mpol_to_str - format a mempolicy structure for printing
--- /dev/null
+From 56fda5adce0c9b4dc8ff2c7be9a704599e685001 Mon Sep 17 00:00:00 2001
+From: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Date: Wed, 17 Jul 2024 09:33:21 +0100
+Subject: [PATCH 1341/1350] dma-buf: system_heap: Allow specifying maximum
+ allocation order
+
+system_heap.max_order=<uint>
+
+Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+---
+ drivers/dma-buf/heaps/system_heap.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma-buf/heaps/system_heap.c
++++ b/drivers/dma-buf/heaps/system_heap.c
+@@ -54,6 +54,11 @@ static gfp_t order_flags[] = {HIGH_ORDER
+ static const unsigned int orders[] = {8, 4, 0};
+ #define NUM_ORDERS ARRAY_SIZE(orders)
+
++static unsigned int module_max_order = orders[0];
++
++module_param_named(max_order, module_max_order, uint, 0400);
++MODULE_PARM_DESC(max_order, "Maximum allocation order override.");
++
+ static struct sg_table *dup_sg_table(struct sg_table *table)
+ {
+ struct sg_table *new_table;
+@@ -339,7 +344,7 @@ static struct dma_buf *system_heap_alloc
+ struct system_heap_buffer *buffer;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ unsigned long size_remaining = len;
+- unsigned int max_order = orders[0];
++ unsigned int max_order = module_max_order;
+ struct dma_buf *dmabuf;
+ struct sg_table *table;
+ struct scatterlist *sg;
+@@ -433,6 +438,9 @@ static int system_heap_create(void)
+ if (IS_ERR(sys_heap))
+ return PTR_ERR(sys_heap);
+
++ if (module_max_order > orders[0])
++ module_max_order = orders[0];
++
+ return 0;
+ }
+ module_init(system_heap_create);
--- /dev/null
+From a27d76f4517d56a6471def2f76687e83fd2a7923 Mon Sep 17 00:00:00 2001
+From: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+Date: Mon, 29 Jul 2024 16:53:18 +0100
+Subject: [PATCH 1342/1350] numa/emulation: Check emulated zones around the CMA
+ window
+
+... Make sure CMA zones do not straddle the emulated NUMA nodes ...
+
+Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
+---
+ drivers/base/numa_emulation.c | 5 +++++
+ include/linux/cma.h | 10 ++++++++++
+ mm/cma.c | 36 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 51 insertions(+)
+
+--- a/drivers/base/numa_emulation.c
++++ b/drivers/base/numa_emulation.c
+@@ -7,6 +7,7 @@
+ * Author: Maíra Canal <mcanal@igalia.com>
+ * Author: Tvrtko Ursulin <tursulin@igalia.com>
+ */
++#include <linux/cma.h>
+ #include <linux/memblock.h>
+
+ #include "numa_emulation.h"
+@@ -55,6 +56,10 @@ int __init numa_emu_init(void)
+ if (i == (emu_nodes - 1) && e != end)
+ e = end;
+
++ ret = cma_check_range(&s, &e);
++ if (ret)
++ return ret;
++
+ pr_info("Faking a node at [mem %pap-%pap]\n", &s, &e);
+ ret = numa_add_memblk(i, s, e + 1);
+ if (ret) {
+--- a/include/linux/cma.h
++++ b/include/linux/cma.h
+@@ -56,4 +56,14 @@ extern bool cma_release(struct cma *cma,
+ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data);
+
+ extern void cma_reserve_pages_on_error(struct cma *cma);
++
++#ifdef CONFIG_CMA
++extern int cma_check_range(u64 *start, u64 *end);
++#else
++static inline int cma_check_range(u64 *start, u64 *end)
++{
++ return 0;
++}
++#endif
++
+ #endif
+--- a/mm/cma.c
++++ b/mm/cma.c
+@@ -587,3 +587,39 @@ int cma_for_each_area(int (*it)(struct c
+
+ return 0;
+ }
++
++struct cma_check_range_data {
++ u64 start, end;
++};
++
++static int check_range(struct cma *cma_, void *data)
++{
++ struct cma_check_range_data *range = data;
++ struct cma_check_range_data cma;
++ bool starts_in_range;
++ bool ends_in_range;
++
++ cma.start = cma_get_base(cma_);
++ cma.end = cma.start + cma_get_size(cma_) - 1;
++
++ starts_in_range = cma.start >= range->start && cma.start <= range->end;
++ ends_in_range = cma.end >= range->start && cma.end <= range->end;
++
++ if (starts_in_range == ends_in_range)
++ return 0;
++
++ pr_notice("CMA %s [%llx-%llx] straddles range [%llx-%llx]\n",
++ cma_->name, cma.start, cma.end, range->start, range->end);
++
++ return -EINVAL;
++}
++
++int cma_check_range(u64 *start, u64 *end)
++{
++ struct cma_check_range_data range = {
++ .start = *start,
++ .end = *end,
++ };
++
++ return cma_for_each_area(check_range, &range);
++}
--- /dev/null
+From aece59283a161dfad67c700c2ae97f9dd996eb2f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 23 Jul 2024 15:55:54 +0100
+Subject: [PATCH 1344/1350] dts: Move some common rpi settings into rpi files
+
+Most 2711 devices and all 2712 device share common bootargs (command
+lines). Make the common values shared defaults, overriding them were
+necessary.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 1 +
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 5 -----
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 5 -----
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 5 +++++
+ 6 files changed, 6 insertions(+), 18 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-4-b.dts
+@@ -265,10 +265,6 @@
+ #include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+ / {
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ /delete-node/ wifi-pwrseq;
+ };
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts
+@@ -274,10 +274,6 @@
+ #include "bcm283x-rpi-i2c0mux_0_44.dtsi"
+
+ / {
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ /delete-node/ wifi-pwrseq;
+ };
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
+@@ -3,6 +3,7 @@
+
+ / {
+ chosen: chosen {
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+ };
+
+ __overrides__ {
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -432,11 +432,6 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g
+ };
+
+ / {
+- chosen: chosen {
+- bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe";
+- stdout-path = "serial10:115200n8";
+- };
+-
+ fan: cooling_fan {
+ status = "disabled";
+ compatible = "pwm-fan";
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -422,11 +422,6 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g
+ };
+
+ / {
+- chosen: chosen {
+- bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe";
+- stdout-path = "serial10:115200n8";
+- };
+-
+ fan: cooling_fan {
+ status = "disabled";
+ compatible = "pwm-fan";
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -98,6 +98,11 @@
+ };
+
+ / {
++ chosen: chosen {
++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe";
++ stdout-path = "serial10:115200n8";
++ };
++
+ aliases: aliases {
+ blconfig = &blconfig;
+ blpubkey = &blpubkey;
--- /dev/null
+From dd44b93200c412c4c043392243c417f2d70082b3 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 18 Jul 2024 20:22:18 +0100
+Subject: [PATCH 1345/1350] dts: Set preferred numa options in bootargs
+
+The default cmdline adjustment is now numa_policy=interleave for 2711 and 2712,
+and additionally system_heap.max_order=0 iommu_dma_numa_policy=interleave for
+just 2712 (due to its better iommu support).
+
+The key setting numa=fake=<n> is not set here, so we will boot with a single
+numa region and behaviour should be pretty much unchanged from before this PR.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts | 2 +-
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 2 +-
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts
+@@ -148,7 +148,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0";
++ bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0 numa_policy=interleave";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
+@@ -3,7 +3,7 @@
+
+ / {
+ chosen: chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 numa_policy=interleave";
+ };
+
+ __overrides__ {
+--- 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";
++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0";
+ stdout-path = "serial10:115200n8";
+ };
+
--- /dev/null
+From a2c8327a1b534574d51f5ea42009876e0008efd2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Sep 2024 17:28:00 +0100
+Subject: [PATCH 1346/1350] drm/vc4: Do not include writeback conn load in load
+ tracker
+
+The transposer/writeback connector should be running with a
+lower priority, so shouldn't be factored into the load
+calculations.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -674,17 +674,26 @@ static int vc4_load_tracker_atomic_check
+ for_each_oldnew_plane_in_state(state, plane, old_plane_state,
+ new_plane_state, i) {
+ struct vc4_plane_state *vc4_plane_state;
++ struct vc4_crtc *vc4_crtc;
+
+ if (old_plane_state->fb && old_plane_state->crtc) {
+ vc4_plane_state = to_vc4_plane_state(old_plane_state);
+- load_state->membus_load -= vc4_plane_state->membus_load;
+- load_state->hvs_load -= vc4_plane_state->hvs_load;
++ vc4_crtc = to_vc4_crtc(old_plane_state->crtc);
++
++ if (!vc4_crtc->feeds_txp) {
++ load_state->membus_load -= vc4_plane_state->membus_load;
++ load_state->hvs_load -= vc4_plane_state->hvs_load;
++ }
+ }
+
+ if (new_plane_state->fb && new_plane_state->crtc) {
+ vc4_plane_state = to_vc4_plane_state(new_plane_state);
+- load_state->membus_load += vc4_plane_state->membus_load;
+- load_state->hvs_load += vc4_plane_state->hvs_load;
++ vc4_crtc = to_vc4_crtc(new_plane_state->crtc);
++
++ if (!vc4_crtc->feeds_txp) {
++ load_state->membus_load += vc4_plane_state->membus_load;
++ load_state->hvs_load += vc4_plane_state->hvs_load;
++ }
+ }
+ }
+
--- /dev/null
+From 2e85eb0e4950c5ad27df5a3ba54300e89694eba4 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Sep 2024 17:38:45 +0100
+Subject: [PATCH 1347/1350] drm/vc4: Drop panic priority for writeback
+ connector
+
+As the writeback connector doesn't have the same realtime
+constraints of a live display, drop the panic priority for it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 21 ++++++++++++++-------
+ 1 file changed, 14 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -224,7 +224,7 @@ static void vc4_hvs_pv_muxing_commit(str
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+ u32 dispctrl;
+- u32 dsp3_mux;
++ u32 dsp3_mux_pri;
+
+ if (!crtc_state->active)
+ continue;
+@@ -241,15 +241,22 @@ static void vc4_hvs_pv_muxing_commit(str
+ * enabled. In this case, FIFO 2 is directly accessed by the
+ * TXP IP, and we need to disable the FIFO2 -> pixelvalve1
+ * route.
++ *
++ * TXP can also run with a lower panic level than a live display,
++ * as it doesn't have the same real-time constraint.
+ */
+- if (vc4_crtc->feeds_txp)
+- dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
+- else
+- dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
++ if (vc4_crtc->feeds_txp) {
++ dsp3_mux_pri = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX);
++ dsp3_mux_pri |= VC4_SET_FIELD(0, SCALER_DISPCTRL_PANIC2);
++ } else {
++ dsp3_mux_pri = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX);
++ dsp3_mux_pri |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2);
++ }
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL) &
+- ~SCALER_DISPCTRL_DSP3_MUX_MASK;
+- HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux);
++ ~(SCALER_DISPCTRL_DSP3_MUX_MASK |
++ SCALER_DISPCTRL_PANIC2_MASK);
++ HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux_pri);
+ }
+ }
+
--- /dev/null
+From 8334494807ff5de7e500cb22e078c08754341c26 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 25 Oct 2024 10:36:20 +0100
+Subject: [PATCH 1348/1350] dts: Simplify bootargs
+
+Reduce the number of different places that bootargs is set in Pi dts
+files. The variants are all combinations of with/without Bluetooth
+and with/without NUMA support (2711).
+
+This is effectively a cosmetic change - the resulting dtbs are
+unchanged.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi | 4 ++++
+ arch/arm/boot/dts/broadcom/bcm2708-rpi-zero-w.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2710-rpi-zero-2-w.dts | 4 ----
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 2 +-
+ arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi | 4 ++++
+ 7 files changed, 9 insertions(+), 17 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi
+@@ -23,6 +23,10 @@
+ };
+
+ / {
++ chosen {
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
++ };
++
+ aliases {
+ bluetooth = &bt;
+ };
+--- a/arch/arm/boot/dts/broadcom/bcm2708-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2708-rpi-zero-w.dts
+@@ -11,10 +11,6 @@
+ compatible = "raspberrypi,model-zero-w", "brcm,bcm2835";
+ model = "Raspberry Pi Zero W";
+
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ aliases {
+ serial0 = &uart1;
+ serial1 = &uart0;
+--- a/arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
+@@ -12,10 +12,6 @@
+ compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
+ model = "Raspberry Pi 3 Model B+";
+
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ aliases {
+ serial0 = &uart1;
+ serial1 = &uart0;
+--- a/arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2710-rpi-3-b.dts
+@@ -12,10 +12,6 @@
+ compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
+ model = "Raspberry Pi 3 Model B";
+
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ aliases {
+ serial0 = &uart1;
+ serial1 = &uart0;
+--- a/arch/arm/boot/dts/broadcom/bcm2710-rpi-zero-2-w.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2710-rpi-zero-2-w.dts
+@@ -11,10 +11,6 @@
+ compatible = "raspberrypi,model-zero-2-w", "brcm,bcm2837";
+ model = "Raspberry Pi Zero 2 W";
+
+- chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
+- };
+-
+ aliases {
+ serial0 = &uart1;
+ serial1 = &uart0;
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
+@@ -2,7 +2,7 @@
+ #include "bcm270x-rpi.dtsi"
+
+ / {
+- chosen: chosen {
++ chosen {
+ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 numa_policy=interleave";
+ };
+
+--- a/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi
+@@ -23,6 +23,10 @@
+ };
+
+ / {
++ chosen {
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
++ };
++
+ aliases {
+ bluetooth = &bt;
+ };
--- /dev/null
+From 86099deff5abf5f63643eecaedb4c11ae77474ce Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Thu, 24 Oct 2024 08:03:19 -0300
+Subject: [PATCH 1349/1350] cgroup: Use kernel command line to disable memory
+ cgroup
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Commit 94a23e978235 ("cgroup: Disable cgroup "memory" by default")
+disabled the memory cgroup by default when initing the cgroups. However,
+it's possible to disable the memory cgroup by a kernel command line.
+Hard-coding such a feature can be problematic as some memory management
+features depend on the order that things are set.
+
+For example, it is possible to see a NULL pointer dereference caused by
+commit 94a23e978235cd35f38075072b34152b2b667e6e. The NULL pointer
+dereference is triggered by the memory shrinker and ends up in a kernel
+crash.
+
+[ 50.028629] ==================================================================
+[ 50.028645] BUG: KASAN: null-ptr-deref in do_shrink_slab+0x1fc/0x978
+[ 50.028663] Write of size 8 at addr 0000000000000000 by task gfxrecon-replay/1965
+
+[ 50.028676] CPU: 3 UID: 1000 PID: 1965 Comm: gfxrecon-replay Tainted: G C 6.12.0-rc4-v8-thp-kasan+ #85
+[ 50.028685] Tainted: [C]=CRAP
+[ 50.028689] Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT)
+[ 50.028694] Call trace:
+[ 50.028697] dump_backtrace+0xfc/0x120
+[ 50.028706] show_stack+0x24/0x38
+[ 50.028711] dump_stack_lvl+0x40/0x88
+[ 50.028720] print_report+0xe4/0x708
+[ 50.028728] kasan_report+0xcc/0x130
+[ 50.028733] kasan_check_range+0x254/0x298
+[ 50.028738] __kasan_check_write+0x20/0x30
+[ 50.028745] do_shrink_slab+0x1fc/0x978
+[ 50.028751] shrink_slab+0x318/0xc38
+[ 50.028756] shrink_one+0x254/0x6d8
+[ 50.028762] shrink_node+0x26b4/0x2848
+[ 50.028767] do_try_to_free_pages+0x3e4/0x1190
+[ 50.028773] try_to_free_pages+0x5a4/0xb40
+[ 50.028778] __alloc_pages_direct_reclaim+0x144/0x298
+[ 50.028787] __alloc_pages_slowpath+0x5c4/0xc70
+[ 50.028793] __alloc_pages_noprof+0x4a8/0x6a8
+[ 50.028800] __folio_alloc_noprof+0x24/0xa8
+[ 50.028806] shmem_alloc_and_add_folio+0x2ec/0xce0
+[ 50.028812] shmem_get_folio_gfp+0x380/0xc20
+[ 50.028818] shmem_read_folio_gfp+0xe0/0x160
+[ 50.028824] drm_gem_get_pages+0x238/0x620 [drm]
+[ 50.029039] drm_gem_shmem_get_pages_sgt+0xd8/0x4b8 [drm_shmem_helper]
+[ 50.029053] v3d_bo_create_finish+0x58/0x1e0 [v3d]
+[ 50.029083] v3d_create_bo_ioctl+0xac/0x210 [v3d]
+[ 50.029105] drm_ioctl_kernel+0x1d8/0x2b8 [drm]
+[ 50.029220] drm_ioctl+0x4b4/0x920 [drm]
+[ 50.029330] __arm64_sys_ioctl+0x11c/0x160
+[ 50.029337] invoke_syscall+0x88/0x268
+[ 50.029345] el0_svc_common+0x160/0x1d8
+[ 50.029351] do_el0_svc+0x50/0x68
+[ 50.029358] el0_svc+0x34/0x80
+[ 50.029364] el0t_64_sync_handler+0x84/0x100
+[ 50.029371] el0t_64_sync+0x190/0x198
+[ 50.029376] ==================================================================
+
+This happens because the memory shrinker is unaware that we are
+artificially disabling the memory cgroups and therefore it doesn't
+allocate `nr_deferred` (as it would if we used the kernel command line).
+
+To avoid such an issue, revert the artificial disablement and disable it
+through the command line. If a user wants to enable the feature, it can
+use the `cgroup_enable=` command line.
+
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi | 2 +-
+ arch/arm/boot/dts/broadcom/bcm270x.dtsi | 2 +-
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts | 2 +-
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi | 2 +-
+ arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi | 2 +-
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +-
+ kernel/cgroup/cgroup.c | 15 +--------------
+ 7 files changed, 7 insertions(+), 20 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2708-rpi-bt.dtsi
+@@ -24,7 +24,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/broadcom/bcm270x.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm270x.dtsi
+@@ -4,7 +4,7 @@
+ / {
+ chosen: chosen {
+ // Disable audio by default
+- bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0";
++ bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0 cgroup_disable=memory";
+ stdout-path = "serial0:115200n8";
+ };
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4s.dts
+@@ -148,7 +148,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0 numa_policy=interleave";
++ bootargs = "coherent_pool=1M snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave";
+ };
+
+ aliases {
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-ds.dtsi
+@@ -3,7 +3,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 numa_policy=interleave";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory numa_policy=interleave";
+ };
+
+ __overrides__ {
+--- a/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm271x-rpi-bt.dtsi
+@@ -24,7 +24,7 @@
+
+ / {
+ chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0";
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 snd_bcm2835.enable_headphones=0 cgroup_disable=memory";
+ };
+
+ aliases {
+--- 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 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 iommu_dma_numa_policy=interleave system_heap.max_order=0";
+ stdout-path = "serial10:115200n8";
+ };
+
+--- a/kernel/cgroup/cgroup.c
++++ b/kernel/cgroup/cgroup.c
+@@ -6060,9 +6060,6 @@ int __init cgroup_init_early(void)
+ return 0;
+ }
+
+-static u16 cgroup_enable_mask __initdata;
+-static int __init cgroup_disable(char *str);
+-
+ /**
+ * cgroup_init - cgroup initialization
+ *
+@@ -6096,12 +6093,6 @@ int __init cgroup_init(void)
+
+ cgroup_unlock();
+
+- /*
+- * Apply an implicit disable, knowing that an explicit enable will
+- * prevent if from doing anything.
+- */
+- cgroup_disable("memory");
+-
+ for_each_subsys(ss, ssid) {
+ if (ss->early_init) {
+ struct cgroup_subsys_state *css =
+@@ -6742,10 +6733,6 @@ static int __init cgroup_disable(char *s
+ strcmp(token, ss->legacy_name))
+ continue;
+
+- /* An explicit cgroup_enable overrides a disable */
+- if (cgroup_enable_mask & (1 << i))
+- continue;
+-
+ static_branch_disable(cgroup_subsys_enabled_key[i]);
+ pr_info("Disabling %s control group subsystem\n",
+ ss->name);
+@@ -6779,7 +6766,7 @@ static int __init cgroup_enable(char *st
+ strcmp(token, ss->legacy_name))
+ continue;
+
+- cgroup_enable_mask |= 1 << i;
++ cgroup_feature_disable_mask &= ~(1 << i);
+ static_branch_enable(cgroup_subsys_enabled_key[i]);
+ pr_info("Enabling %s control group subsystem\n",
+ ss->name);
--- /dev/null
+From 4622323f5c9e540f3356f2229c4d551b31cc234d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 24 Oct 2024 15:38:36 +0100
+Subject: [PATCH 1350/1350] arm64: dts: Sort out CM5 and I/O board I2C ports
+
+There is a difference in I2C usage between CM4IO and CM5IO. Present a
+simple, consistent view of the world by moving the assignment of the
+bus IDs into carrier-specific files.
+
+CM5 has reduced connectivity on CM4IO - the DPHYs are connected to
+CAM1 and DISP1. Keep i2c-10 for use with that pair, as is the case for
+CM4 on CM4IO.
+
+Fixes: 36faab69e8ee ("dts: bcm2712-rpi: Add aliases for the CSI/DSI I2Cs")
+See: https://github.com/raspberrypi/linux/pull/6421
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../boot/dts/broadcom/bcm2712-rpi-cm4io.dtsi | 28 +++++++++++++++++++
+ .../dts/broadcom/bcm2712-rpi-cm5-cm4io.dts | 17 +----------
+ .../dts/broadcom/bcm2712-rpi-cm5-cm5io.dts | 7 +----
+ .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 13 ---------
+ .../boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 14 ++++++++++
+ .../dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts | 17 +----------
+ .../dts/broadcom/bcm2712-rpi-cm5l-cm5io.dts | 7 +----
+ .../boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 4 ---
+ 8 files changed, 46 insertions(+), 61 deletions(-)
+ create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm4io.dtsi
+ create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
+
+--- /dev/null
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm4io.dtsi
+@@ -0,0 +1,28 @@
++// SPDX-License-Identifier: GPL-2.0
++
++i2c_csi_dsi0: &i2c0 { // Note: For CAM0 and DISP0 connectors
++};
++
++i2c_csi_dsi1: &i2c6 { // Note: For CAM1, DISP1, on-board RTC, and fan controller
++ pinctrl-0 = <&rp1_i2c6_38_39>;
++ pinctrl-names = "default";
++ clock-frequency = <100000>;
++ symlink = "i2c-6";
++};
++
++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable
++
++&aliases {
++ /delete-property/ i2c11;
++ i2c10 = &i2c_csi_dsi;
++};
++
++// The RP1 USB3 interfaces are not usable on CM4IO
++
++&rp1_usb0 {
++ status = "disabled";
++};
++
++&rp1_usb1 {
++ status = "disabled";
++};
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5-cm4io.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5-cm4io.dts
+@@ -2,19 +2,4 @@
+ /dts-v1/;
+
+ #include "bcm2712-rpi-cm5.dtsi"
+-
+-// The RP1 USB3 interfaces are not usable on CM4IO
+-
+-&rp1_usb0 {
+- status = "disabled";
+-};
+-
+-&rp1_usb1 {
+- status = "disabled";
+-};
+-
+-/ {
+- __overrides__ {
+- i2c_csi_dsi = <&i2c_csi_dsi>, "status";
+- };
+-};
++#include "bcm2712-rpi-cm4io.dtsi"
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5-cm5io.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5-cm5io.dts
+@@ -2,9 +2,4 @@
+ /dts-v1/;
+
+ #include "bcm2712-rpi-cm5.dtsi"
+-
+-/ {
+- __overrides__ {
+- i2c_csi_dsi = <&i2c_csi_dsi>, "status";
+- };
+-};
++#include "bcm2712-rpi-cm5io.dtsi"
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -234,19 +234,6 @@ aux: &dummy {};
+
+ #include "bcm2712-rpi.dtsi"
+
+-i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
+- pinctrl-0 = <&rp1_i2c6_38_39>;
+- pinctrl-names = "default";
+- clock-frequency = <100000>;
+- symlink = "i2c-6";
+-};
+-
+-i2c_csi_dsi1: &i2c0 { // Note: This is for MIPI1 connector
+- symlink = "i2c-11";
+-};
+-
+-i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
+-
+ cam1_reg: &cam0_reg { // Shares CAM_GPIO with cam0_reg
+ };
+
+--- /dev/null
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
+@@ -0,0 +1,14 @@
++// SPDX-License-Identifier: GPL-2.0
++
++i2c_csi_dsi1: &i2c0 { // Note: This is for CAM/DISP 1 connector
++ symlink = "i2c-11";
++};
++
++i2c_csi_dsi0: &i2c6 { // Note: This is for CAM/DISP 0 connector
++ pinctrl-0 = <&rp1_i2c6_38_39>;
++ pinctrl-names = "default";
++ clock-frequency = <100000>;
++ symlink = "i2c-6";
++};
++
++i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm4io.dts
+@@ -2,19 +2,4 @@
+ /dts-v1/;
+
+ #include "bcm2712-rpi-cm5l.dtsi"
+-
+-// The RP1 USB3 interfaces are not usable on CM4IO
+-
+-&rp1_usb0 {
+- status = "disabled";
+-};
+-
+-&rp1_usb1 {
+- status = "disabled";
+-};
+-
+-/ {
+- __overrides__ {
+- i2c_csi_dsi = <&i2c_csi_dsi>, "status";
+- };
+-};
++#include "bcm2712-rpi-cm4io.dtsi"
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm5io.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l-cm5io.dts
+@@ -2,9 +2,4 @@
+ /dts-v1/;
+
+ #include "bcm2712-rpi-cm5l.dtsi"
+-
+-/ {
+- __overrides__ {
+- i2c_csi_dsi = <&i2c_csi_dsi>, "status";
+- };
+-};
++#include "bcm2712-rpi-cm5io.dtsi"
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
+@@ -5,10 +5,6 @@
+
+ / {
+ model = "Raspberry Pi Compute Module 5 Lite";
+-
+- __overrides__ {
+- i2c_csi_dsi = <&i2c_csi_dsi>, "status";
+- };
+ };
+
+ &sd_io_1v8_reg {