diff --git a/target/linux/bcm27xx/bcm2708/config-6.6 b/target/linux/bcm27xx/bcm2708/config-6.6 index b587b5d71..a8a3cc9af 100644 --- a/target/linux/bcm27xx/bcm2708/config-6.6 +++ b/target/linux/bcm27xx/bcm2708/config-6.6 @@ -314,6 +314,7 @@ CONFIG_PRINTK_TIME=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_RANDSTRUCT_NONE=y CONFIG_RASPBERRYPI_FIRMWARE=y @@ -346,6 +347,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMSC_PHY=y CONFIG_SOFTIRQ_ON_OWN_STACK=y diff --git a/target/linux/bcm27xx/bcm2709/config-6.6 b/target/linux/bcm27xx/bcm2709/config-6.6 index 4a295d52c..edcc8f58c 100644 --- a/target/linux/bcm27xx/bcm2709/config-6.6 +++ b/target/linux/bcm27xx/bcm2709/config-6.6 @@ -398,6 +398,7 @@ CONFIG_PTP_1588_CLOCK=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_RANDSTRUCT_NONE=y CONFIG_RAS=y @@ -435,6 +436,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SMP_ON_UP=y diff --git a/target/linux/bcm27xx/bcm2710/config-6.6 b/target/linux/bcm27xx/bcm2710/config-6.6 index 97f2f110b..18a9c26b0 100644 --- a/target/linux/bcm27xx/bcm2710/config-6.6 +++ b/target/linux/bcm27xx/bcm2710/config-6.6 @@ -389,6 +389,7 @@ CONFIG_PRINTK_TIME=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y CONFIG_QUEUED_SPINLOCKS=y @@ -428,6 +429,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SMSC_PHY=y diff --git a/target/linux/bcm27xx/bcm2711/config-6.6 b/target/linux/bcm27xx/bcm2711/config-6.6 index f9b746960..a48771b8e 100644 --- a/target/linux/bcm27xx/bcm2711/config-6.6 +++ b/target/linux/bcm27xx/bcm2711/config-6.6 @@ -405,6 +405,7 @@ CONFIG_PTP_1588_CLOCK=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y +CONFIG_PWM_GPIO=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y CONFIG_QUEUED_SPINLOCKS=y @@ -445,6 +446,7 @@ CONFIG_SERIAL_DEV_BUS=y # CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_RPI_FW=y CONFIG_SG_POOL=y CONFIG_SMP=y CONFIG_SOCK_RX_QUEUE_MAPPING=y diff --git a/target/linux/bcm27xx/bcm2712/config-6.6 b/target/linux/bcm27xx/bcm2712/config-6.6 index 5abc41ff6..bd36c4063 100644 --- a/target/linux/bcm27xx/bcm2712/config-6.6 +++ b/target/linux/bcm27xx/bcm2712/config-6.6 @@ -128,7 +128,7 @@ CONFIG_CMA_SIZE_SEL_MBYTES=y # CONFIG_CMA_SYSFS is not set CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_RP1=y -# CONFIG_COMMON_CLK_RP1_SDIO is not set +CONFIG_COMMON_CLK_RP1_SDIO=y CONFIG_COMMON_CLK_XGENE=y CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 # CONFIG_COMPAT_32BIT_TIME is not set @@ -511,6 +511,7 @@ CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_PWM=y CONFIG_PWM_BCM2835=y CONFIG_PWM_BRCMSTB=y +CONFIG_PWM_GPIO=y CONFIG_PWM_RP1=y CONFIG_PWM_SYSFS=y CONFIG_QUEUED_RWLOCKS=y @@ -546,7 +547,6 @@ CONFIG_SCSI_COMMON=y # CONFIG_SCSI_LOWLEVEL is not set # CONFIG_SCSI_PROC_FS is not set CONFIG_SENSORS_RASPBERRYPI_HWMON=y -CONFIG_SENSORS_RP1_ADC=y CONFIG_SERIAL_8250_BCM2835AUX=y CONFIG_SERIAL_8250_BCM7271=y # CONFIG_SERIAL_8250_DMA is not set @@ -574,6 +574,7 @@ CONFIG_SPARSEMEM_VMEMMAP=y CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y CONFIG_SPARSE_IRQ=y CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +CONFIG_SRAM=y # CONFIG_STRIP_ASM_SYMS is not set CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y diff --git a/target/linux/bcm27xx/config-6.6 b/target/linux/bcm27xx/config-6.6 index b44aa9f8a..e69dbf5d7 100644 --- a/target/linux/bcm27xx/config-6.6 +++ b/target/linux/bcm27xx/config-6.6 @@ -1,6 +1,7 @@ # CONFIG_BACKLIGHT_RPI is not set # CONFIG_BCM2712_MIP is not set # CONFIG_COMMON_CLK_RP1 is not set +# CONFIG_COMMON_CLK_RP1_SDIO is not set # CONFIG_COMMON_CLK_RP1_SDIO is not set # CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set # CONFIG_DRM_PANEL_TPO_Y17P is not set @@ -9,8 +10,10 @@ # CONFIG_DRM_RP1_DSI is not set # CONFIG_DRM_RP1_VEC is not set # CONFIG_FB_RPISENSE is not set +# CONFIG_FIRMWARE_RP1 is not set # CONFIG_GPIO_PWM is not set # CONFIG_INPUT_RASPBERRYPI_BUTTON is not set +# CONFIG_MBOX_RP1 is not set # CONFIG_MEDIA_PCI_HAILO is not set # CONFIG_MFD_PM8921_CORE is not set # CONFIG_MFD_RASPBERRYPI_POE_HAT is not set @@ -19,10 +22,14 @@ # CONFIG_PHY_CADENCE_DP is not set # CONFIG_PINCTRL_BCM2712 is not set # CONFIG_PINCTRL_RP1 is not set +# CONFIG_PWM_PIO_RP1 is not set # CONFIG_PWM_RP1 is not set # CONFIG_RASPBERRYPI_GPIOMEM is not set # CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 is not set +# CONFIG_RP1_PIO is not set # CONFIG_SENSORS_RP1_ADC is not set +# CONFIG_SERIAL_RPI_FW is not set +# CONFIG_SND_PIMIDI is not set # CONFIG_SPI_RP2040_GPIO_BRIDGE is not set # CONFIG_VIDEO_AD5398 is not set # CONFIG_VIDEO_ARDUCAM_64MP is not set @@ -39,3 +46,4 @@ # CONFIG_VIDEO_OV64A40 is not set # CONFIG_VIDEO_RASPBERRYPI_PISP_BE is not set # CONFIG_VIDEO_RP1_CFE is not set +# CONFIG_WS2812_PIO_RP1 is not set diff --git a/target/linux/bcm27xx/modules/hwmon.mk b/target/linux/bcm27xx/modules/hwmon.mk index 0f1547cd4..320b11708 100644 --- a/target/linux/bcm27xx/modules/hwmon.mk +++ b/target/linux/bcm27xx/modules/hwmon.mk @@ -2,6 +2,23 @@ # # Copyright (C) 2019 OpenWrt.org +define KernelPackage/rp1-adc + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 ADC and temperature sensor driver + KCONFIG:=CONFIG_SENSORS_RP1_ADC + FILES:=$(LINUX_DIR)/drivers/hwmon/rp1-adc.ko + AUTOLOAD:=$(call AutoLoad,21,rp1-adc) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +endef + +define KernelPackage/rp1-adc/description + Kernel module for RP1 silicon providing ADC and + temperature monitoring. +endef + +$(eval $(call KernelPackage,rp1-adc)) + + define KernelPackage/hwmon-raspberrypi TITLE:=Raspberry Pi voltage monitor KCONFIG:=CONFIG_SENSORS_RASPBERRYPI_HWMON diff --git a/target/linux/bcm27xx/modules/other.mk b/target/linux/bcm27xx/modules/other.mk index 99b71d4d6..7ec35c95f 100644 --- a/target/linux/bcm27xx/modules/other.mk +++ b/target/linux/bcm27xx/modules/other.mk @@ -53,3 +53,89 @@ define KernelPackage/smi-bcm2835-dev/description endef $(eval $(call KernelPackage,smi-bcm2835-dev)) + + +define KernelPackage/rp1 + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 firmware + KCONFIG:=CONFIG_FIRMWARE_RP1 + FILES:=$(LINUX_DIR)/drivers/firmware/rp1.ko + AUTOLOAD:=$(call AutoLoad,21,rp1) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +endef + +define KernelPackage/rp1/description + This driver provides a firmware interface to the RP1 processor using shared + memory and a mailbox. +endef + +$(eval $(call KernelPackage,rp1)) + + +define KernelPackage/rp1-pio + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 PIO block support + KCONFIG:=CONFIG_RP1_PIO + FILES:=$(LINUX_DIR)/drivers/misc/rp1-pio.ko + AUTOLOAD:=$(call AutoLoad,21,rp1-pio) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-rp1 +endef + +define KernelPackage/rp1-pio/description + Driver providing control of the Raspberry Pi PIO block, as found in RP1 +endef + +$(eval $(call KernelPackage,rp1-pio)) + + +define KernelPackage/pwm-pio-rp1 + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 PWM support + KCONFIG:=CONFIG_PWM_PIO_RP1 + FILES:=$(LINUX_DIR)/drivers/pwm/pwm-pio-rp1.ko + AUTOLOAD:=$(call AutoLoad,21,pwm-pio-rp1) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-rp1-pio +endef + +define KernelPackage/pwm-pio-rp1/description + Enables precise control of PWM signals for tasks like motor control, + LED dimming, and audio signal generation. Leveraging PIO allows for + higher accuracy and flexibility in PWM signal generation compared + to traditional hardware timers. +endef + +$(eval $(call KernelPackage,pwm-pio-rp1)) + + +define KernelPackage/ws2812-pio-rp1 + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 PIO-base WS2812 driver + KCONFIG:=CONFIG_WS2812_PIO_RP1 + FILES:=$(LINUX_DIR)/drivers/misc/ws2812-pio-rp1.ko + AUTOLOAD:=$(call AutoLoad,21,ws2812-pio-rp1) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-rp1-pio +endef + +define KernelPackage/ws2812-pio-rp1/description + Driver for the WS2812 (NeoPixel) LEDs using the RP1 PIO hardware. + The driver creates a character device to which rgbw pixels may be + written. Single-byte writes to offset 0 set the brightness at runtime. +endef + +$(eval $(call KernelPackage,ws2812-pio-rp1)) + + +define KernelPackage/rp1-mailbox + SUBMENU:=$(OTHER_MENU) + TITLE:=RP1 mailbox IPC driver + KCONFIG:=CONFIG_MBOX_RP1 + FILES:=$(LINUX_DIR)/drivers/mailbox/rp1-mailbox.ko + AUTOLOAD:=$(call AutoLoad,21,rp1-mailbox) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +endef + +define KernelPackage/rp1-mailbox/description + This is a RP1 mailbox IPC driver. +endef + +$(eval $(call KernelPackage,rp1-mailbox)) diff --git a/target/linux/bcm27xx/modules/video.mk b/target/linux/bcm27xx/modules/video.mk index b9af0b3e0..e8a61f138 100644 --- a/target/linux/bcm27xx/modules/video.mk +++ b/target/linux/bcm27xx/modules/video.mk @@ -20,6 +20,25 @@ endef $(eval $(call KernelPackage,camera-bcm2835)) +define KernelPackage/rp1-cfe + TITLE:=RP1 Camera Front-End + SUBMENU:=$(VIDEO_MENU) + KCONFIG:= \ + CONFIG_VIDEO_RP1_CFE \ + CONFIG_VIDEO_BCM2835 + FILES:=$(LINUX_DIR)/drivers/media/platform/raspberrypi/rp1_cfe/rp1-cfe.ko + AUTOLOAD:=$(call AutoLoad,67,rp1-cfe) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-video-core +kmod-video-fwnode +kmod-video-dma-contig +kmod-video-async +endef + +define KernelPackage/rp1-cfe/description + Driver for the Camera Serial Interface (CSI) to capture video + streams from connected cameras. +endef + +$(eval $(call KernelPackage,rp1-cfe)) + + define KernelPackage/codec-bcm2835 TITLE:=BCM2835 Video Codec KCONFIG:= \ @@ -115,3 +134,81 @@ define KernelPackage/vchiq-mmal-bcm2835/description endef $(eval $(call KernelPackage,vchiq-mmal-bcm2835)) + + +define KernelPackage/drm-rp1-dsi + SUBMENU:=$(VIDEO_MENU) + TITLE:=RP1 Display Serial Interface for Video + KCONFIG:= \ + CONFIG_DRM_RP1_DSI \ + CONFIG_DRM_GEM_DMA_HELPER \ + CONFIG_DRM_KMS_HELPER \ + CONFIG_DRM_MIPI_DSI=y \ + CONFIG_DRM_VRAM_HELPER=n \ + CONFIG_DRM_TTM=n \ + CONFIG_DRM_TTM_HELPER=n \ + CONFIG_GENERIC_PHY_MIPI_DPHY=n \ + CONFIG_DRM_WERROR=n + FILES:=$(LINUX_DIR)/drivers/gpu/drm/rp1/rp1-dsi/drm-rp1-dsi.ko + AUTOLOAD:=$(call AutoLoad,67,drm-rp1-dsi) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-drm-vc4 +endef + +define KernelPackage/drm-rp1-dsi/description + This module manages the DSI for driving high-resolution LCD panels + such as the official Raspberry Pi displays or other screens that + use the DSI interface. +endef + +$(eval $(call KernelPackage,drm-rp1-dsi)) + + +define KernelPackage/drm-rp1-dpi + SUBMENU:=$(VIDEO_MENU) + TITLE:=RP1 Display Parallel Interface for Video + KCONFIG:= \ + CONFIG_DRM_RP1_DPI \ + CONFIG_DRM_GEM_DMA_HELPER \ + CONFIG_DRM_KMS_HELPER \ + CONFIG_DRM_VRAM_HELPER=n \ + CONFIG_DRM_TTM=n \ + CONFIG_DRM_TTM_HELPER=n + FILES:=$(LINUX_DIR)/drivers/gpu/drm/rp1/rp1-dpi/drm-rp1-dpi.ko + AUTOLOAD:=$(call AutoLoad,67,drm-rp1-dpi) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-drm-vc4 +kmod-rp1-pio +endef + +define KernelPackage/drm-rp1-dpi/description + This module is or driving displays using the DPI standard. + Useful for interfacing with custom or low-level LCD panels + that require parallel RGB signals. Provides direct control + over the timing and signal driving of raw LCD panels. + Typically used in maker projects or with non-HDMI displays. +endef + +$(eval $(call KernelPackage,drm-rp1-dpi)) + + +define KernelPackage/drm-rp1-vec + SUBMENU:=$(VIDEO_MENU) + TITLE:=RP1 Display Composite Video + KCONFIG:= \ + CONFIG_DRM_RP1_VEC \ + CONFIG_DRM_GEM_DMA_HELPER \ + CONFIG_DRM_KMS_HELPER \ + CONFIG_DRM_VRAM_HELPER=n \ + CONFIG_DRM_TTM=n \ + CONFIG_DRM_TTM_HELPER=n + FILES:=$(LINUX_DIR)/drivers/gpu/drm/rp1/rp1-vec/drm-rp1-vec.ko + AUTOLOAD:=$(call AutoLoad,67,drm-rp1-vec) + DEPENDS:=@TARGET_bcm27xx_bcm2712 +kmod-drm-vc4 +endef + +define KernelPackage/drm-rp1-vec/description + This module is used for composite video output, which is typically + transmitted through the RCA jack. Primary use is onnecting older + TVs or monitors that rely on analog signals via a composite interface. + Handles standard-definition analog signals in NTSC and PAL. +endef + +$(eval $(call KernelPackage,drm-rp1-vec)) diff --git a/target/linux/bcm27xx/patches-6.6/950-0025-drm-atomic-helpers-remove-legacy_cursor_update-hacks.patch b/target/linux/bcm27xx/patches-6.6/950-0025-drm-atomic-helpers-remove-legacy_cursor_update-hacks.patch index 97bc25b56..a0981c9c0 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0025-drm-atomic-helpers-remove-legacy_cursor_update-hacks.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0025-drm-atomic-helpers-remove-legacy_cursor_update-hacks.patch @@ -89,7 +89,7 @@ Signed-off-by: Maxime Ripard commit->event = kzalloc(sizeof(*commit->event), --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c -@@ -7280,6 +7280,19 @@ int intel_atomic_commit(struct drm_devic +@@ -7297,6 +7297,19 @@ int intel_atomic_commit(struct drm_devic state->base.legacy_cursor_update = false; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0065-cgroup-Disable-cgroup-memory-by-default.patch b/target/linux/bcm27xx/patches-6.6/950-0065-cgroup-Disable-cgroup-memory-by-default.patch index 5f2d80b69..5600e8573 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0065-cgroup-Disable-cgroup-memory-by-default.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0065-cgroup-Disable-cgroup-memory-by-default.patch @@ -17,7 +17,7 @@ Signed-off-by: Phil Elwell --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c -@@ -6063,6 +6063,9 @@ int __init cgroup_init_early(void) +@@ -6059,6 +6059,9 @@ int __init cgroup_init_early(void) return 0; } @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell /** * cgroup_init - cgroup initialization * -@@ -6096,6 +6099,12 @@ int __init cgroup_init(void) +@@ -6092,6 +6095,12 @@ int __init cgroup_init(void) cgroup_unlock(); @@ -40,7 +40,7 @@ Signed-off-by: Phil Elwell for_each_subsys(ss, ssid) { if (ss->early_init) { struct cgroup_subsys_state *css = -@@ -6736,6 +6745,10 @@ static int __init cgroup_disable(char *s +@@ -6740,6 +6749,10 @@ static int __init cgroup_disable(char *s strcmp(token, ss->legacy_name)) continue; @@ -51,7 +51,7 @@ Signed-off-by: Phil Elwell static_branch_disable(cgroup_subsys_enabled_key[i]); pr_info("Disabling %s control group subsystem\n", ss->name); -@@ -6754,6 +6767,31 @@ static int __init cgroup_disable(char *s +@@ -6758,6 +6771,31 @@ static int __init cgroup_disable(char *s } __setup("cgroup_disable=", cgroup_disable); diff --git a/target/linux/bcm27xx/patches-6.6/950-0087-Add-dwc_otg-driver.patch b/target/linux/bcm27xx/patches-6.6/950-0087-Add-dwc_otg-driver.patch index cb9fd4442..82e969138 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0087-Add-dwc_otg-driver.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0087-Add-dwc_otg-driver.patch @@ -1189,7 +1189,7 @@ Signed-off-by: Alexander Winkowski } --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c -@@ -5710,7 +5710,7 @@ static void port_event(struct usb_hub *h +@@ -5722,7 +5722,7 @@ static void port_event(struct usb_hub *h port_dev->over_current_count++; port_over_current_notify(port_dev); diff --git a/target/linux/bcm27xx/patches-6.6/950-0092-MMC-added-alternative-MMC-driver.patch b/target/linux/bcm27xx/patches-6.6/950-0092-MMC-added-alternative-MMC-driver.patch index 45f25fe1e..9b86d0e6a 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0092-MMC-added-alternative-MMC-driver.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0092-MMC-added-alternative-MMC-driver.patch @@ -285,7 +285,7 @@ Signed-off-by: Phil Elwell static inline int mmc_blk_part_switch(struct mmc_card *card, unsigned int part_type); static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, -@@ -3016,6 +3023,8 @@ static int mmc_blk_probe(struct mmc_card +@@ -3066,6 +3073,8 @@ static int mmc_blk_probe(struct mmc_card { struct mmc_blk_data *md; int ret = 0; @@ -294,7 +294,7 @@ Signed-off-by: Phil Elwell /* * Check that the card supports the command class(es) we need. -@@ -3023,7 +3032,16 @@ static int mmc_blk_probe(struct mmc_card +@@ -3073,7 +3082,16 @@ static int mmc_blk_probe(struct mmc_card if (!(card->csd.cmdclass & CCC_BLOCK_READ)) return -ENODEV; @@ -312,7 +312,7 @@ Signed-off-by: Phil Elwell card->complete_wq = alloc_workqueue("mmc_complete", WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); -@@ -3038,6 +3056,17 @@ static int mmc_blk_probe(struct mmc_card +@@ -3088,6 +3106,17 @@ static int mmc_blk_probe(struct mmc_card goto out_free; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0106-Add-support-for-all-the-downstream-rpi-sound-card-dr.patch b/target/linux/bcm27xx/patches-6.6/950-0106-Add-support-for-all-the-downstream-rpi-sound-card-dr.patch index ecb7bffa0..56712a2b4 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0106-Add-support-for-all-the-downstream-rpi-sound-card-dr.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0106-Add-support-for-all-the-downstream-rpi-sound-card-dr.patch @@ -14927,7 +14927,7 @@ Signed-off-by: Phil Elwell config SND_SOC_TFA989X tristate "NXP/Goodix TFA989X (TFA1) amplifiers" depends on I2C -@@ -2404,4 +2422,8 @@ config SND_SOC_LPASS_TX_MACRO +@@ -2405,4 +2423,8 @@ config SND_SOC_LPASS_TX_MACRO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" @@ -17583,7 +17583,7 @@ Signed-off-by: Phil Elwell * For devices with more than one control interface, we assume the --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c -@@ -2247,6 +2247,8 @@ static const struct usb_audio_quirk_flag +@@ -2254,6 +2254,8 @@ static const struct usb_audio_quirk_flag QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ QUIRK_FLAG_ALIGN_TRANSFER), diff --git a/target/linux/bcm27xx/patches-6.6/950-0161-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch b/target/linux/bcm27xx/patches-6.6/950-0161-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch index 942f47a0a..157fedf27 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0161-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0161-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch @@ -15,7 +15,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c -@@ -1497,6 +1497,109 @@ command_cleanup: +@@ -1498,6 +1498,109 @@ command_cleanup: } /* @@ -125,7 +125,7 @@ Signed-off-by: Jonathan Bell * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it */ -@@ -5347,6 +5450,7 @@ static const struct hc_driver xhci_hc_dr +@@ -5360,6 +5463,7 @@ static const struct hc_driver xhci_hc_dr .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, diff --git a/target/linux/bcm27xx/patches-6.6/950-0162-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch b/target/linux/bcm27xx/patches-6.6/950-0162-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch index 32681a1bc..9d22d7451 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0162-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0162-usb-xhci-workaround-for-bogus-SET_DEQ_PENDING-endpoi.patch @@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -738,9 +738,9 @@ deq_found: +@@ -740,9 +740,9 @@ deq_found: } if ((ep->ep_state & SET_DEQ_PENDING)) { diff --git a/target/linux/bcm27xx/patches-6.6/950-0163-usb-xhci-drop-and-add-the-endpoint-context-in-xhci_f.patch b/target/linux/bcm27xx/patches-6.6/950-0163-usb-xhci-drop-and-add-the-endpoint-context-in-xhci_f.patch index 1e5bb6059..70401f6e7 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0163-usb-xhci-drop-and-add-the-endpoint-context-in-xhci_f.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0163-usb-xhci-drop-and-add-the-endpoint-context-in-xhci_f.patch @@ -19,7 +19,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c -@@ -1586,7 +1586,7 @@ static void xhci_fixup_endpoint(struct u +@@ -1587,7 +1587,7 @@ static void xhci_fixup_endpoint(struct u return; } ctrl_ctx->add_flags = xhci_get_endpoint_flag_from_index(ep_index); diff --git a/target/linux/bcm27xx/patches-6.6/950-0169-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch b/target/linux/bcm27xx/patches-6.6/950-0169-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch index a65d198d2..12518a439 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0169-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0169-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch @@ -23,7 +23,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h -@@ -243,6 +243,9 @@ +@@ -244,6 +244,9 @@ #define USB_VENDOR_ID_BAANTO 0x2453 #define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100 @@ -33,7 +33,7 @@ Signed-off-by: Jonathan Bell #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -@@ -1406,6 +1409,9 @@ +@@ -1439,6 +1442,9 @@ #define USB_VENDOR_ID_XIAOMI 0x2717 #define USB_DEVICE_ID_MI_SILENT_MOUSE 0x5014 diff --git a/target/linux/bcm27xx/patches-6.6/950-0173-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-6.6/950-0173-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch index 03af7a9cb..08ba07231 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0173-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0173-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch @@ -343,7 +343,7 @@ Signed-off-by: Naushir Patuck +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20057,6 +20057,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -20059,6 +20059,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml F: drivers/media/i2c/imx415.c diff --git a/target/linux/bcm27xx/patches-6.6/950-0174-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch b/target/linux/bcm27xx/patches-6.6/950-0174-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch index 3dbfea881..240c63509 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0174-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0174-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch @@ -177,7 +177,7 @@ Signed-off-by: Phil Elwell +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20065,6 +20065,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -20067,6 +20067,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-6.6/950-0175-Documentation-devicetree-Add-documentation-for-imx37.patch b/target/linux/bcm27xx/patches-6.6/950-0175-Documentation-devicetree-Add-documentation-for-imx37.patch index b6df79f25..3af5c0e95 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0175-Documentation-devicetree-Add-documentation-for-imx37.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0175-Documentation-devicetree-Add-documentation-for-imx37.patch @@ -132,7 +132,7 @@ Signed-off-by: David Plowman +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20062,6 +20062,7 @@ M: Raspberry Pi Kernel Maintenance --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c -@@ -181,6 +181,7 @@ v3d_hub_irq(int irq, void *arg) +@@ -189,6 +189,7 @@ v3d_hub_irq(int irq, void *arg) "GMP", }; const char *client = "?"; @@ -21,7 +21,7 @@ Signed-off-by: Phil Elwell V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL)); -@@ -190,6 +191,7 @@ v3d_hub_irq(int irq, void *arg) +@@ -198,6 +199,7 @@ v3d_hub_irq(int irq, void *arg) client = v3d41_axi_ids[axi_id]; } @@ -29,7 +29,7 @@ Signed-off-by: Phil Elwell dev_err(v3d->drm.dev, "MMU error from client %s (%d) at 0x%llx%s%s%s\n", client, axi_id, (long long)vio_addr, ((intsts & V3D_HUB_INT_MMU_WRV) ? -@@ -198,6 +200,7 @@ v3d_hub_irq(int irq, void *arg) +@@ -206,6 +208,7 @@ v3d_hub_irq(int irq, void *arg) ", pte invalid" : ""), ((intsts & V3D_HUB_INT_MMU_CAP) ? ", cap exceeded" : "")); diff --git a/target/linux/bcm27xx/patches-6.6/950-0215-PCI-brcmstb-Add-DT-property-to-control-L1SS.patch b/target/linux/bcm27xx/patches-6.6/950-0215-PCI-brcmstb-Add-DT-property-to-control-L1SS.patch index f29ecab07..c6a5810fa 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0215-PCI-brcmstb-Add-DT-property-to-control-L1SS.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0215-PCI-brcmstb-Add-DT-property-to-control-L1SS.patch @@ -65,7 +65,7 @@ Signed-off-by: Phil Elwell writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); return 0; -@@ -1536,6 +1551,7 @@ static int brcm_pcie_probe(struct platfo +@@ -1537,6 +1552,7 @@ static int brcm_pcie_probe(struct platfo pcie->gen = (ret < 0) ? 0 : ret; pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); diff --git a/target/linux/bcm27xx/patches-6.6/950-0296-media-i2c-ov7251-Add-fwnode-properties-controls.patch b/target/linux/bcm27xx/patches-6.6/950-0296-media-i2c-ov7251-Add-fwnode-properties-controls.patch index 06d19e63f..b8adfd8a7 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0296-media-i2c-ov7251-Add-fwnode-properties-controls.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0296-media-i2c-ov7251-Add-fwnode-properties-controls.patch @@ -13,7 +13,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c -@@ -1541,7 +1541,7 @@ static int ov7251_init_ctrls(struct ov72 +@@ -1543,7 +1543,7 @@ static int ov7251_init_ctrls(struct ov72 s64 pixel_rate; int hblank; @@ -22,7 +22,7 @@ Signed-off-by: Dave Stevenson ov7251->ctrls.lock = &ov7251->lock; v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, -@@ -1600,6 +1600,7 @@ static int ov7251_init_ctrls(struct ov72 +@@ -1602,6 +1602,7 @@ static int ov7251_init_ctrls(struct ov72 static int ov7251_probe(struct i2c_client *client) { @@ -30,7 +30,7 @@ Signed-off-by: Dave Stevenson struct device *dev = &client->dev; struct ov7251 *ov7251; unsigned int rate = 0, clk_rate = 0; -@@ -1690,6 +1691,15 @@ static int ov7251_probe(struct i2c_clien +@@ -1692,6 +1693,15 @@ static int ov7251_probe(struct i2c_clien goto destroy_mutex; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0327-media-i2c-ov7251-Make-the-enable-GPIO-optional.patch b/target/linux/bcm27xx/patches-6.6/950-0327-media-i2c-ov7251-Make-the-enable-GPIO-optional.patch index c00fc4e09..abe16c51c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0327-media-i2c-ov7251-Make-the-enable-GPIO-optional.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0327-media-i2c-ov7251-Make-the-enable-GPIO-optional.patch @@ -14,13 +14,13 @@ Signed-off-by: Dave Stevenson --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c -@@ -1676,7 +1676,8 @@ static int ov7251_probe(struct i2c_clien +@@ -1678,7 +1678,8 @@ static int ov7251_probe(struct i2c_clien return PTR_ERR(ov7251->analog_regulator); } -- ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); +- ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + ov7251->enable_gpio = devm_gpiod_get_optional(dev, "enable", -+ GPIOD_OUT_HIGH); ++ GPIOD_OUT_LOW); if (IS_ERR(ov7251->enable_gpio)) { dev_err(dev, "cannot get enable gpio\n"); return PTR_ERR(ov7251->enable_gpio); diff --git a/target/linux/bcm27xx/patches-6.6/950-0350-media-i2c-ov7251-Reinstate-setting-ov7251_global_ini.patch b/target/linux/bcm27xx/patches-6.6/950-0350-media-i2c-ov7251-Reinstate-setting-ov7251_global_ini.patch index 50c53c6a7..ca9c0d2c2 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0350-media-i2c-ov7251-Reinstate-setting-ov7251_global_ini.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0350-media-i2c-ov7251-Reinstate-setting-ov7251_global_ini.patch @@ -15,7 +15,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c -@@ -1344,6 +1344,14 @@ static int ov7251_s_stream(struct v4l2_s +@@ -1346,6 +1346,14 @@ static int ov7251_s_stream(struct v4l2_s if (ret < 0) goto err_power_down; diff --git a/target/linux/bcm27xx/patches-6.6/950-0396-media-i2c-ov7251-Add-module-param-to-select-ext-trig.patch b/target/linux/bcm27xx/patches-6.6/950-0396-media-i2c-ov7251-Add-module-param-to-select-ext-trig.patch index 09af88873..7bcd8e41c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0396-media-i2c-ov7251-Add-module-param-to-select-ext-trig.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0396-media-i2c-ov7251-Add-module-param-to-select-ext-trig.patch @@ -62,7 +62,7 @@ Signed-off-by: Dave Stevenson static const unsigned long supported_xclk_rates[] = { [OV7251_19_2_MHZ] = 19200000, [OV7251_24_MHZ] = 24000000, -@@ -1372,6 +1384,23 @@ static int ov7251_s_stream(struct v4l2_s +@@ -1374,6 +1386,23 @@ static int ov7251_s_stream(struct v4l2_s dev_err(ov7251->dev, "could not sync v4l2 controls\n"); goto err_power_down; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0407-dtbindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch b/target/linux/bcm27xx/patches-6.6/950-0407-dtbindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch index 06d744a82..986922d79 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0407-dtbindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0407-dtbindings-media-i2c-Add-IMX708-CMOS-sensor-binding.patch @@ -138,7 +138,7 @@ Signed-off-by: Dave Stevenson +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20106,6 +20106,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -20108,6 +20108,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/imx519.yaml F: drivers/media/i2c/imx519.c diff --git a/target/linux/bcm27xx/patches-6.6/950-0416-gpio-pca953x-Add-ti-tca9554-compatible-string.patch b/target/linux/bcm27xx/patches-6.6/950-0416-gpio-pca953x-Add-ti-tca9554-compatible-string.patch index aa11729b7..e0de6fe1c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0416-gpio-pca953x-Add-ti-tca9554-compatible-string.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0416-gpio-pca953x-Add-ti-tca9554-compatible-string.patch @@ -10,7 +10,7 @@ Signed-off-by: Phil Elwell --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c -@@ -1347,6 +1347,7 @@ static const struct of_device_id pca953x +@@ -1311,6 +1311,7 @@ static const struct of_device_id pca953x { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, diff --git a/target/linux/bcm27xx/patches-6.6/950-0434-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch b/target/linux/bcm27xx/patches-6.6/950-0434-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch index a01247a1c..f3d68c454 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0434-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0434-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch @@ -271,7 +271,7 @@ Signed-off-by: Naushir Patuck +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20111,7 +20111,7 @@ M: Raspberry Pi Kernel Maintenance --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c -@@ -4872,6 +4872,7 @@ static const struct { +@@ -4873,6 +4873,7 @@ static const struct { */ static int hci_dev_setup_sync(struct hci_dev *hdev) { @@ -28,7 +28,7 @@ Signed-off-by: Phil Elwell int ret = 0; bool invalid_bdaddr; size_t i; -@@ -4900,7 +4901,8 @@ static int hci_dev_setup_sync(struct hci +@@ -4901,7 +4902,8 @@ static int hci_dev_setup_sync(struct hci test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); if (!ret) { if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks) && diff --git a/target/linux/bcm27xx/patches-6.6/950-0460-dt-bindings-media-imx258-Rename-to-include-vendor-pr.patch b/target/linux/bcm27xx/patches-6.6/950-0460-dt-bindings-media-imx258-Rename-to-include-vendor-pr.patch index b9793de43..50ae21475 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0460-dt-bindings-media-imx258-Rename-to-include-vendor-pr.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0460-dt-bindings-media-imx258-Rename-to-include-vendor-pr.patch @@ -291,7 +291,7 @@ Signed-off-by: Dave Stevenson + }; --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20012,7 +20012,7 @@ M: Sakari Ailus + v3d->gpu_queue_stats[V3D_BIN].last_exec_end = local_clock(); trace_v3d_bcl_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -110,6 +112,7 @@ v3d_irq(int irq, void *arg) + +@@ -112,6 +114,7 @@ v3d_irq(int irq, void *arg) if (intsts & V3D_INT_FRDONE) { struct v3d_fence *fence = to_v3d_fence(v3d->render_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_RENDER].last_exec_end = local_clock(); trace_v3d_rcl_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -120,6 +123,7 @@ v3d_irq(int irq, void *arg) + +@@ -124,6 +127,7 @@ v3d_irq(int irq, void *arg) if (intsts & V3D_INT_CSDDONE) { struct v3d_fence *fence = to_v3d_fence(v3d->csd_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock(); trace_v3d_csd_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); -@@ -157,6 +161,7 @@ v3d_hub_irq(int irq, void *arg) + +@@ -163,6 +167,7 @@ v3d_hub_irq(int irq, void *arg) if (intsts & V3D_HUB_INT_TFUC) { struct v3d_fence *fence = to_v3d_fence(v3d->tfu_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_TFU].last_exec_end = local_clock(); trace_v3d_tfu_irq(&v3d->drm, fence->seqno); - dma_fence_signal(&fence->base); + --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -19,6 +19,7 @@ @@ -423,7 +423,7 @@ Signed-off-by: Jose Maria Casanova Crespo v3d_switch_perfmon(v3d, &job->base); /* XXX: Set the QCFG */ -@@ -190,6 +301,7 @@ v3d_tfu_job_run(struct drm_sched_job *sc +@@ -194,6 +305,7 @@ v3d_tfu_job_run(struct drm_sched_job *sc trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno); @@ -431,7 +431,7 @@ Signed-off-by: Jose Maria Casanova Crespo V3D_WRITE(V3D_TFU_IIA, job->args.iia); V3D_WRITE(V3D_TFU_IIS, job->args.iis); V3D_WRITE(V3D_TFU_ICA, job->args.ica); -@@ -231,6 +343,7 @@ v3d_csd_job_run(struct drm_sched_job *sc +@@ -238,6 +350,7 @@ v3d_csd_job_run(struct drm_sched_job *sc trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno); @@ -439,7 +439,7 @@ Signed-off-by: Jose Maria Casanova Crespo v3d_switch_perfmon(v3d, &job->base); for (i = 1; i <= 6; i++) -@@ -247,7 +360,10 @@ v3d_cache_clean_job_run(struct drm_sched +@@ -254,7 +367,10 @@ v3d_cache_clean_job_run(struct drm_sched struct v3d_job *job = to_v3d_job(sched_job); struct v3d_dev *v3d = job->v3d; @@ -450,7 +450,7 @@ Signed-off-by: Jose Maria Casanova Crespo return NULL; } -@@ -385,8 +501,18 @@ v3d_sched_init(struct v3d_dev *v3d) +@@ -392,8 +508,18 @@ v3d_sched_init(struct v3d_dev *v3d) int hw_jobs_limit = 1; int job_hang_limit = 0; int hang_limit_ms = 500; @@ -469,7 +469,7 @@ Signed-off-by: Jose Maria Casanova Crespo ret = drm_sched_init(&v3d->queue[V3D_BIN].sched, &v3d_bin_sched_ops, hw_jobs_limit, job_hang_limit, -@@ -440,9 +566,20 @@ void +@@ -447,9 +573,20 @@ void v3d_sched_fini(struct v3d_dev *v3d) { enum v3d_queue q; diff --git a/target/linux/bcm27xx/patches-6.6/950-0481-xhci-Use-more-event-ring-segment-table-entries.patch b/target/linux/bcm27xx/patches-6.6/950-0481-xhci-Use-more-event-ring-segment-table-entries.patch index ff8c9223b..874725601 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0481-xhci-Use-more-event-ring-segment-table-entries.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0481-xhci-Use-more-event-ring-segment-table-entries.patch @@ -52,7 +52,7 @@ Signed-off-by: Jonathan Bell erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h -@@ -1425,8 +1425,9 @@ struct urb_priv { +@@ -1426,8 +1426,9 @@ struct urb_priv { * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, * meaning 64 ring segments. diff --git a/target/linux/bcm27xx/patches-6.6/950-0482-xhci-quirks-add-link-TRB-quirk-for-VL805.patch b/target/linux/bcm27xx/patches-6.6/950-0482-xhci-quirks-add-link-TRB-quirk-for-VL805.patch index 93e45b544..e295c225f 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0482-xhci-quirks-add-link-TRB-quirk-for-VL805.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0482-xhci-quirks-add-link-TRB-quirk-for-VL805.patch @@ -24,9 +24,9 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c -@@ -483,6 +483,7 @@ static void xhci_pci_quirks(struct devic - if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) { +@@ -486,6 +486,7 @@ static void xhci_pci_quirks(struct devic xhci->quirks |= XHCI_LPM_SUPPORT; + xhci->quirks |= XHCI_TRB_OVERFETCH; xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; + xhci->quirks |= XHCI_AVOID_DQ_ON_LINK; } @@ -34,7 +34,7 @@ Signed-off-by: Jonathan Bell if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c -@@ -728,6 +728,15 @@ static int xhci_move_dequeue_past_td(str +@@ -730,6 +730,15 @@ static int xhci_move_dequeue_past_td(str } while (!cycle_found || !td_last_trb_found); deq_found: @@ -52,7 +52,7 @@ Signed-off-by: Jonathan Bell addr = xhci_trb_virt_to_dma(new_seg, new_deq); --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h -@@ -1663,6 +1663,9 @@ struct xhci_hcd { +@@ -1664,6 +1664,9 @@ struct xhci_hcd { #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) #define XHCI_ETRON_HOST BIT_ULL(49) diff --git a/target/linux/bcm27xx/patches-6.6/950-0487-drivers-media-imx296-Add-standby-delay-during-probe.patch b/target/linux/bcm27xx/patches-6.6/950-0487-drivers-media-imx296-Add-standby-delay-during-probe.patch deleted file mode 100644 index 786ba7656..000000000 --- a/target/linux/bcm27xx/patches-6.6/950-0487-drivers-media-imx296-Add-standby-delay-during-probe.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 7713ce38e6a26425ace3a57b3d03ba0125c16f89 Mon Sep 17 00:00:00 2001 -From: Naushir Patuck -Date: Fri, 28 Jul 2023 12:00:40 +0100 -Subject: [PATCH 0487/1085] drivers: media: imx296: Add standby delay during - probe - -Add a 2-5ms delay when coming out of standby and before reading the -sensor info register durning probe, as instructed by the datasheet. This -standby delay is already present when the sensor starts streaming. - -Signed-off-by: Naushir Patuck ---- - drivers/media/i2c/imx296.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/drivers/media/i2c/imx296.c -+++ b/drivers/media/i2c/imx296.c -@@ -940,6 +940,8 @@ static int imx296_identify_model(struct - return ret; - } - -+ usleep_range(2000, 5000); -+ - ret = imx296_read(sensor, IMX296_SENSOR_INFO); - if (ret < 0) { - dev_err(sensor->dev, "failed to read sensor information (%d)\n", diff --git a/target/linux/bcm27xx/patches-6.6/950-0509-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch b/target/linux/bcm27xx/patches-6.6/950-0509-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch index a6a976257..cef1bf42a 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0509-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0509-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch @@ -53,16 +53,16 @@ Signed-off-by: Phil Elwell help --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c -@@ -639,6 +639,8 @@ static int brcmstb_gpio_probe(struct pla +@@ -637,6 +637,8 @@ static int brcmstb_gpio_probe(struct pla #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; #endif + if (of_property_read_bool(np, "brcm,gpio-direct")) + flags |= BGPIOF_REG_DIRECT; - of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p, - bank_width) { -@@ -688,7 +690,9 @@ static int brcmstb_gpio_probe(struct pla + of_property_for_each_u32(np, "brcm,gpio-bank-widths", bank_width) { + struct brcmstb_gpio_bank *bank; +@@ -685,7 +687,9 @@ static int brcmstb_gpio_probe(struct pla } gc->owner = THIS_MODULE; @@ -73,7 +73,7 @@ Signed-off-by: Phil Elwell if (!gc->label) { err = -ENOMEM; goto fail; -@@ -697,7 +701,7 @@ static int brcmstb_gpio_probe(struct pla +@@ -694,7 +698,7 @@ static int brcmstb_gpio_probe(struct pla gc->of_gpio_n_cells = 2; gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ @@ -82,7 +82,7 @@ Signed-off-by: Phil Elwell gc->offset = bank->id * MAX_GPIO_PER_BANK; if (priv->parent_irq > 0) gc->to_irq = brcmstb_gpio_to_irq; -@@ -706,8 +710,10 @@ static int brcmstb_gpio_probe(struct pla +@@ -703,8 +707,10 @@ static int brcmstb_gpio_probe(struct pla * Mask all interrupts by default, since wakeup interrupts may * be retained from S5 cold boot */ diff --git a/target/linux/bcm27xx/patches-6.6/950-0518-net-macb-Also-set-DMA-coherent-mask.patch b/target/linux/bcm27xx/patches-6.6/950-0518-net-macb-Also-set-DMA-coherent-mask.patch index cbfab3e4e..988223065 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0518-net-macb-Also-set-DMA-coherent-mask.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0518-net-macb-Also-set-DMA-coherent-mask.patch @@ -134,7 +134,7 @@ Signed-off-by: Jonathan Bell struct napi_struct napi_tx; dma_addr_t rx_ring_dma; -@@ -1285,9 +1304,15 @@ struct macb { +@@ -1287,9 +1306,15 @@ struct macb { u32 caps; unsigned int dma_burst_length; @@ -222,7 +222,7 @@ Signed-off-by: Jonathan Bell wmb(); // ensure softirq can see update } -@@ -2402,6 +2425,11 @@ static netdev_tx_t macb_start_xmit(struc +@@ -2404,6 +2427,11 @@ static netdev_tx_t macb_start_xmit(struc skb_tx_timestamp(skb); spin_lock_irq(&bp->lock); @@ -234,7 +234,7 @@ Signed-off-by: Jonathan Bell macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART)); spin_unlock_irq(&bp->lock); -@@ -2776,6 +2804,37 @@ static void macb_configure_dma(struct ma +@@ -2778,6 +2806,37 @@ static void macb_configure_dma(struct ma } } @@ -272,7 +272,7 @@ Signed-off-by: Jonathan Bell static void macb_init_hw(struct macb *bp) { u32 config; -@@ -2804,6 +2863,11 @@ static void macb_init_hw(struct macb *bp +@@ -2806,6 +2865,11 @@ static void macb_init_hw(struct macb *bp if (bp->caps & MACB_CAPS_JUMBO) bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK; @@ -284,7 +284,7 @@ Signed-off-by: Jonathan Bell macb_configure_dma(bp); /* Enable RX partial store and forward and set watermark */ -@@ -3165,6 +3229,52 @@ static void gem_get_ethtool_strings(stru +@@ -3170,6 +3234,52 @@ static void gem_get_ethtool_strings(stru } } @@ -337,7 +337,7 @@ Signed-off-by: Jonathan Bell static struct net_device_stats *macb_get_stats(struct net_device *dev) { struct macb *bp = netdev_priv(dev); -@@ -3757,6 +3867,8 @@ static const struct ethtool_ops macb_eth +@@ -3764,6 +3874,8 @@ static const struct ethtool_ops macb_eth }; static const struct ethtool_ops gem_ethtool_ops = { @@ -346,7 +346,7 @@ Signed-off-by: Jonathan Bell .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, .get_wol = macb_get_wol, -@@ -3766,6 +3878,8 @@ static const struct ethtool_ops gem_etht +@@ -3773,6 +3885,8 @@ static const struct ethtool_ops gem_etht .get_ethtool_stats = gem_get_ethtool_stats, .get_strings = gem_get_ethtool_strings, .get_sset_count = gem_get_sset_count, @@ -355,7 +355,7 @@ Signed-off-by: Jonathan Bell .get_link_ksettings = macb_get_link_ksettings, .set_link_ksettings = macb_set_link_ksettings, .get_ringparam = macb_get_ringparam, -@@ -5062,6 +5176,11 @@ static int macb_probe(struct platform_de +@@ -5069,6 +5183,11 @@ static int macb_probe(struct platform_de } } } @@ -365,9 +365,9 @@ Signed-off-by: Jonathan Bell + bp->use_aw2b_fill = device_property_read_bool(&pdev->dev, "cdns,use-aw2b-fill"); + spin_lock_init(&bp->lock); + spin_lock_init(&bp->stats_lock); - /* setup capabilities */ -@@ -5117,6 +5236,21 @@ static int macb_probe(struct platform_de +@@ -5125,6 +5244,21 @@ static int macb_probe(struct platform_de else bp->phy_interface = interface; @@ -389,7 +389,7 @@ Signed-off-by: Jonathan Bell /* IP specific init */ err = init(pdev); if (err) -@@ -5193,6 +5327,19 @@ static int macb_remove(struct platform_d +@@ -5201,6 +5335,19 @@ static int macb_remove(struct platform_d return 0; } @@ -409,7 +409,7 @@ Signed-off-by: Jonathan Bell static int __maybe_unused macb_suspend(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); -@@ -5407,6 +5554,7 @@ static const struct dev_pm_ops macb_pm_o +@@ -5415,6 +5562,7 @@ static const struct dev_pm_ops macb_pm_o static struct platform_driver macb_driver = { .probe = macb_probe, .remove = macb_remove, diff --git a/target/linux/bcm27xx/patches-6.6/950-0521-PCI-brcmstb-Add-BCM2712-support.patch b/target/linux/bcm27xx/patches-6.6/950-0521-PCI-brcmstb-Add-BCM2712-support.patch index c4c79ae42..5f5b9de34 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0521-PCI-brcmstb-Add-BCM2712-support.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0521-PCI-brcmstb-Add-BCM2712-support.patch @@ -831,7 +831,7 @@ Signed-off-by: Jonathan Bell return 0; } -@@ -1207,6 +1534,7 @@ static void brcm_pcie_enter_l23(struct b +@@ -1208,6 +1535,7 @@ static void brcm_pcie_enter_l23(struct b static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) { @@ -839,7 +839,7 @@ Signed-off-by: Jonathan Bell static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = { PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT, PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT, -@@ -1239,6 +1567,9 @@ static int brcm_phy_cntl(struct brcm_pci +@@ -1240,6 +1568,9 @@ static int brcm_phy_cntl(struct brcm_pci dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop")); return ret; @@ -849,7 +849,7 @@ Signed-off-by: Jonathan Bell } static inline int brcm_phy_start(struct brcm_pcie *pcie) -@@ -1271,6 +1602,12 @@ static void brcm_pcie_turn_off(struct br +@@ -1272,6 +1603,12 @@ static void brcm_pcie_turn_off(struct br u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); @@ -862,7 +862,7 @@ Signed-off-by: Jonathan Bell /* Shutdown PCIe bridge */ pcie->bridge_sw_init_set(pcie, 1); } -@@ -1301,9 +1638,9 @@ static int brcm_pcie_suspend_noirq(struc +@@ -1302,9 +1639,9 @@ static int brcm_pcie_suspend_noirq(struc if (brcm_phy_stop(pcie)) dev_err(dev, "Could not stop phy for suspend\n"); @@ -874,7 +874,7 @@ Signed-off-by: Jonathan Bell return ret; } -@@ -1398,7 +1735,7 @@ err_regulator: +@@ -1399,7 +1736,7 @@ err_regulator: if (pcie->sr) regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies); err_reset: @@ -883,7 +883,7 @@ Signed-off-by: Jonathan Bell err_disable_clk: clk_disable_unprepare(pcie->clk); return ret; -@@ -1410,8 +1747,8 @@ static void __brcm_pcie_remove(struct br +@@ -1411,8 +1748,8 @@ static void __brcm_pcie_remove(struct br brcm_pcie_turn_off(pcie); if (brcm_phy_stop(pcie)) dev_err(pcie->dev, "Could not stop phy\n"); @@ -894,7 +894,7 @@ Signed-off-by: Jonathan Bell clk_disable_unprepare(pcie->clk); } -@@ -1429,12 +1766,16 @@ static const int pcie_offsets[] = { +@@ -1430,12 +1767,16 @@ static const int pcie_offsets[] = { [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, [EXT_CFG_DATA] = 0x9004, @@ -911,7 +911,7 @@ Signed-off-by: Jonathan Bell }; static const struct pcie_cfg_data generic_cfg = { -@@ -1442,6 +1783,7 @@ static const struct pcie_cfg_data generi +@@ -1443,6 +1784,7 @@ static const struct pcie_cfg_data generi .type = GENERIC, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, @@ -919,7 +919,7 @@ Signed-off-by: Jonathan Bell }; static const struct pcie_cfg_data bcm7425_cfg = { -@@ -1449,6 +1791,7 @@ static const struct pcie_cfg_data bcm742 +@@ -1450,6 +1792,7 @@ static const struct pcie_cfg_data bcm742 .type = BCM7425, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, @@ -927,7 +927,7 @@ Signed-off-by: Jonathan Bell }; static const struct pcie_cfg_data bcm7435_cfg = { -@@ -1463,12 +1806,15 @@ static const struct pcie_cfg_data bcm490 +@@ -1464,12 +1807,15 @@ static const struct pcie_cfg_data bcm490 .type = BCM4908, .perst_set = brcm_pcie_perst_set_4908, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, @@ -943,7 +943,7 @@ Signed-off-by: Jonathan Bell }; static const struct pcie_cfg_data bcm7278_cfg = { -@@ -1476,6 +1822,7 @@ static const struct pcie_cfg_data bcm727 +@@ -1477,6 +1823,7 @@ static const struct pcie_cfg_data bcm727 .type = BCM7278, .perst_set = brcm_pcie_perst_set_7278, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278, @@ -951,7 +951,7 @@ Signed-off-by: Jonathan Bell }; static const struct pcie_cfg_data bcm2711_cfg = { -@@ -1483,10 +1830,27 @@ static const struct pcie_cfg_data bcm271 +@@ -1484,10 +1831,27 @@ static const struct pcie_cfg_data bcm271 .type = BCM2711, .perst_set = brcm_pcie_perst_set_generic, .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, @@ -979,7 +979,7 @@ Signed-off-by: Jonathan Bell { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg }, { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg }, { .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg }, -@@ -1527,7 +1891,7 @@ static int brcm_pcie_probe(struct platfo +@@ -1528,7 +1892,7 @@ static int brcm_pcie_probe(struct platfo data = of_device_get_match_data(&pdev->dev); if (!data) { @@ -988,7 +988,7 @@ Signed-off-by: Jonathan Bell return -EINVAL; } -@@ -1538,6 +1902,7 @@ static int brcm_pcie_probe(struct platfo +@@ -1539,6 +1903,7 @@ static int brcm_pcie_probe(struct platfo pcie->type = data->type; pcie->perst_set = data->perst_set; pcie->bridge_sw_init_set = data->bridge_sw_init_set; @@ -996,7 +996,7 @@ Signed-off-by: Jonathan Bell pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) -@@ -1568,14 +1933,20 @@ static int brcm_pcie_probe(struct platfo +@@ -1569,14 +1934,20 @@ static int brcm_pcie_probe(struct platfo clk_disable_unprepare(pcie->clk); return PTR_ERR(pcie->perst_reset); } @@ -1019,41 +1019,47 @@ Signed-off-by: Jonathan Bell clk_disable_unprepare(pcie->clk); return ret; } -@@ -1598,6 +1969,33 @@ static int brcm_pcie_probe(struct platfo - dev_err(pcie->dev, "probe of internal MSI failed"); - goto fail; - } -+ } else if (pci_msi_enabled() && msi_np != pcie->np) { -+ /* Use RC_BAR1 for MIP access */ -+ u64 msi_pci_addr; -+ u64 msi_phys_addr; -+ -+ if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) { -+ dev_err(pcie->dev, "Unable to find MSI PCI address\n"); -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) { -+ dev_err(pcie->dev, "Unable to find MSI physical address\n"); -+ ret = -EINVAL; -+ goto fail; -+ } -+ -+ writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000), -+ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO); -+ writel(upper_32_bits(msi_pci_addr), -+ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI); -+ -+ writel(lower_32_bits(msi_phys_addr) | -+ PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK, -+ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP); -+ writel(upper_32_bits(msi_phys_addr), -+ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI); - } +@@ -1595,8 +1966,38 @@ static int brcm_pcie_probe(struct platfo + if (pci_msi_enabled()) { + struct device_node *msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); - bridge->ops = pcie->type == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops; -@@ -1614,6 +2012,8 @@ static int brcm_pcie_probe(struct platfo +- if (msi_np == pcie->np) ++ if (msi_np == pcie->np) { + ret = brcm_pcie_enable_msi(pcie); ++ } else { ++ /* Use RC_BAR1 for MIP access */ ++ u64 msi_pci_addr; ++ u64 msi_phys_addr; ++ ++ if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) { ++ dev_err(pcie->dev, "Unable to find MSI PCI address\n"); ++ ret = -EINVAL; ++ of_node_put(msi_np); ++ goto fail; ++ } ++ ++ if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) { ++ dev_err(pcie->dev, "Unable to find MSI physical address\n"); ++ ret = -EINVAL; ++ of_node_put(msi_np); ++ goto fail; ++ } ++ ++ writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000), ++ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO); ++ writel(upper_32_bits(msi_pci_addr), ++ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI); ++ ++ writel(lower_32_bits(msi_phys_addr) | ++ PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK, ++ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP); ++ writel(upper_32_bits(msi_phys_addr), ++ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI); ++ } + + of_node_put(msi_np); + +@@ -1620,6 +2021,8 @@ static int brcm_pcie_probe(struct platfo return ret; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0545-ASoC-dwc-Support-set_bclk_ratio.patch b/target/linux/bcm27xx/patches-6.6/950-0545-ASoC-dwc-Support-set_bclk_ratio.patch index 29077e0d1..bc7cd7ddb 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0545-ASoC-dwc-Support-set_bclk_ratio.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0545-ASoC-dwc-Support-set_bclk_ratio.patch @@ -10,7 +10,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -460,6 +460,40 @@ static int dw_i2s_set_tdm_slot(struct sn +@@ -459,6 +459,40 @@ static int dw_i2s_set_tdm_slot(struct sn return 0; } @@ -51,7 +51,7 @@ Signed-off-by: Phil Elwell static int dw_i2s_dai_probe(struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -@@ -476,6 +510,7 @@ static const struct snd_soc_dai_ops dw_i +@@ -475,6 +509,7 @@ static const struct snd_soc_dai_ops dw_i .trigger = dw_i2s_trigger, .set_fmt = dw_i2s_set_fmt, .set_tdm_slot = dw_i2s_set_tdm_slot, diff --git a/target/linux/bcm27xx/patches-6.6/950-0546-ASoC-dwc-Add-DMACR-handling.patch b/target/linux/bcm27xx/patches-6.6/950-0546-ASoC-dwc-Add-DMACR-handling.patch index 6eba454d5..bc577668a 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0546-ASoC-dwc-Add-DMACR-handling.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0546-ASoC-dwc-Add-DMACR-handling.patch @@ -14,7 +14,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -248,7 +248,7 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -247,7 +247,7 @@ static void dw_i2s_config(struct dw_i2s_ { u32 ch_reg; struct i2s_clk_config_data *config = &dev->config; @@ -23,7 +23,7 @@ Signed-off-by: Phil Elwell i2s_disable_channels(dev, stream); -@@ -260,6 +260,7 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -259,6 +259,7 @@ static void dw_i2s_config(struct dw_i2s_ dev->fifo_th - 1); i2s_write_reg(dev->i2s_base, TER(ch_reg), TER_TXCHEN | dev->tdm_mask << TER_TXSLOT_SHIFT); @@ -31,7 +31,7 @@ Signed-off-by: Phil Elwell } else { i2s_write_reg(dev->i2s_base, RCR(ch_reg), dev->xfer_resolution); -@@ -267,9 +268,15 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -266,9 +267,15 @@ static void dw_i2s_config(struct dw_i2s_ dev->fifo_th - 1); i2s_write_reg(dev->i2s_base, RER(ch_reg), RER_RXCHEN | dev->tdm_mask << RER_RXSLOT_SHIFT); diff --git a/target/linux/bcm27xx/patches-6.6/950-0547-ASOC-dwc-Improve-DMA-shutdown.patch b/target/linux/bcm27xx/patches-6.6/950-0547-ASOC-dwc-Improve-DMA-shutdown.patch index bbe8c7748..f560b694b 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0547-ASOC-dwc-Improve-DMA-shutdown.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0547-ASOC-dwc-Improve-DMA-shutdown.patch @@ -22,7 +22,7 @@ Signed-off-by: Phil Elwell #include #include #include -@@ -208,15 +208,10 @@ static void i2s_start(struct dw_i2s_dev +@@ -206,15 +206,10 @@ static void i2s_start(struct dw_i2s_dev i2s_write_reg(dev->i2s_base, CER, 1); } @@ -37,9 +37,9 @@ Signed-off-by: Phil Elwell - else - i2s_write_reg(dev->i2s_base, IRER, 0); - if (dev->use_pio || dev->is_jh7110) - i2s_disable_irqs(dev, substream->stream, 8); -@@ -225,23 +220,15 @@ static void i2s_stop(struct dw_i2s_dev * + if (!(dev->use_pio || dev->is_jh7110)) + i2s_disable_dma(dev, substream->stream); +@@ -224,23 +219,15 @@ static void i2s_stop(struct dw_i2s_dev * if (!dev->active) { i2s_write_reg(dev->i2s_base, CER, 0); @@ -67,7 +67,7 @@ Signed-off-by: Phil Elwell } static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) -@@ -354,6 +341,62 @@ static int dw_i2s_hw_params(struct snd_p +@@ -353,6 +340,62 @@ static int dw_i2s_hw_params(struct snd_p return 0; } @@ -130,7 +130,7 @@ Signed-off-by: Phil Elwell static int dw_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { -@@ -381,9 +424,12 @@ static int dw_i2s_trigger(struct snd_pcm +@@ -380,9 +423,12 @@ static int dw_i2s_trigger(struct snd_pcm i2s_start(dev, substream); break; @@ -144,7 +144,7 @@ Signed-off-by: Phil Elwell dev->active--; i2s_stop(dev, substream); break; -@@ -512,6 +558,7 @@ static int dw_i2s_dai_probe(struct snd_s +@@ -511,6 +557,7 @@ static int dw_i2s_dai_probe(struct snd_s static const struct snd_soc_dai_ops dw_i2s_dai_ops = { .probe = dw_i2s_dai_probe, .startup = dw_i2s_startup, diff --git a/target/linux/bcm27xx/patches-6.6/950-0548-ASOC-dwc-Fix-16-bit-audio-handling.patch b/target/linux/bcm27xx/patches-6.6/950-0548-ASOC-dwc-Fix-16-bit-audio-handling.patch index b8a1849db..d5f3adaf5 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0548-ASOC-dwc-Fix-16-bit-audio-handling.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0548-ASOC-dwc-Fix-16-bit-audio-handling.patch @@ -22,7 +22,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -271,23 +271,34 @@ static int dw_i2s_hw_params(struct snd_p +@@ -270,23 +270,34 @@ static int dw_i2s_hw_params(struct snd_p { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); struct i2s_clk_config_data *config = &dev->config; @@ -57,7 +57,7 @@ Signed-off-by: Phil Elwell dev->ccr = 0x10; dev->xfer_resolution = 0x05; break; -@@ -519,24 +530,21 @@ static int dw_i2s_set_bclk_ratio(struct +@@ -518,24 +529,21 @@ static int dw_i2s_set_bclk_ratio(struct struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); struct i2s_clk_config_data *config = &dev->config; diff --git a/target/linux/bcm27xx/patches-6.6/950-0555-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch b/target/linux/bcm27xx/patches-6.6/950-0555-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch index 560c2f627..4e8134287 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0555-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0555-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch @@ -295,7 +295,7 @@ v2: fix kernel panic with debug-fs interface to list registers static irqreturn_t v3d_hub_irq(int irq, void *arg); -@@ -120,7 +121,8 @@ v3d_irq(int irq, void *arg) +@@ -124,7 +125,8 @@ v3d_irq(int irq, void *arg) status = IRQ_HANDLED; } @@ -305,7 +305,7 @@ v2: fix kernel panic with debug-fs interface to list registers struct v3d_fence *fence = to_v3d_fence(v3d->csd_job->base.irq_fence); v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock(); -@@ -134,7 +136,7 @@ v3d_irq(int irq, void *arg) +@@ -140,7 +142,7 @@ v3d_irq(int irq, void *arg) /* We shouldn't be triggering these if we have GMP in * always-allowed mode. */ @@ -314,7 +314,7 @@ v2: fix kernel panic with debug-fs interface to list registers dev_err(v3d->drm.dev, "GMP violation\n"); /* V3D 4.2 wires the hub and core IRQs together, so if we & -@@ -209,6 +211,11 @@ v3d_hub_irq(int irq, void *arg) +@@ -217,6 +219,11 @@ v3d_hub_irq(int irq, void *arg) status = IRQ_HANDLED; } @@ -326,7 +326,7 @@ v2: fix kernel panic with debug-fs interface to list registers return status; } -@@ -223,8 +230,8 @@ v3d_irq_init(struct v3d_dev *v3d) +@@ -231,8 +238,8 @@ v3d_irq_init(struct v3d_dev *v3d) * for us. */ for (core = 0; core < v3d->cores; core++) @@ -337,7 +337,7 @@ v2: fix kernel panic with debug-fs interface to list registers irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1); if (irq1 == -EPROBE_DEFER) -@@ -268,12 +275,12 @@ v3d_irq_enable(struct v3d_dev *v3d) +@@ -276,12 +283,12 @@ v3d_irq_enable(struct v3d_dev *v3d) /* Enable our set of interrupts, masking out any others. */ for (core = 0; core < v3d->cores; core++) { @@ -354,7 +354,7 @@ v2: fix kernel panic with debug-fs interface to list registers } void -@@ -288,8 +295,8 @@ v3d_irq_disable(struct v3d_dev *v3d) +@@ -296,8 +303,8 @@ v3d_irq_disable(struct v3d_dev *v3d) /* Clear any pending interrupts we might have left. */ for (core = 0; core < v3d->cores; core++) @@ -600,7 +600,7 @@ v2: fix kernel panic with debug-fs interface to list registers static struct dma_fence * v3d_tfu_job_run(struct drm_sched_job *sched_job) { -@@ -302,20 +304,22 @@ v3d_tfu_job_run(struct drm_sched_job *sc +@@ -306,20 +308,22 @@ v3d_tfu_job_run(struct drm_sched_job *sc trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno); v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_TFU], sched_job); @@ -635,16 +635,16 @@ v2: fix kernel panic with debug-fs interface to list registers return fence; } -@@ -327,7 +331,7 @@ v3d_csd_job_run(struct drm_sched_job *sc +@@ -331,7 +335,7 @@ v3d_csd_job_run(struct drm_sched_job *sc struct v3d_dev *v3d = job->base.v3d; struct drm_device *dev = &v3d->drm; struct dma_fence *fence; - int i; + int i, csd_cfg0_reg, csd_cfg_reg_count; - v3d->csd_job = job; - -@@ -346,10 +350,12 @@ v3d_csd_job_run(struct drm_sched_job *sc + if (unlikely(job->base.base.s_fence->finished.error)) + return NULL; +@@ -353,10 +357,12 @@ v3d_csd_job_run(struct drm_sched_job *sc v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_CSD], sched_job); v3d_switch_perfmon(v3d, &job->base); @@ -660,7 +660,7 @@ v2: fix kernel panic with debug-fs interface to list registers return fence; } -@@ -452,7 +458,8 @@ v3d_csd_job_timedout(struct drm_sched_jo +@@ -459,7 +465,8 @@ v3d_csd_job_timedout(struct drm_sched_jo { struct v3d_csd_job *job = to_csd_job(sched_job); struct v3d_dev *v3d = job->base.v3d; diff --git a/target/linux/bcm27xx/patches-6.6/950-0585-drm-vc4-Introduce-generation-number-enum.patch b/target/linux/bcm27xx/patches-6.6/950-0585-drm-vc4-Introduce-generation-number-enum.patch index 5b14f4d49..f57aa6d1b 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0585-drm-vc4-Introduce-generation-number-enum.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0585-drm-vc4-Introduce-generation-number-enum.patch @@ -22,7 +22,7 @@ Signed-off-by: Maxime Ripard drivers/gpu/drm/vc4/vc4_drv.h | 7 ++- drivers/gpu/drm/vc4/vc4_gem.c | 24 +++++------ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- - drivers/gpu/drm/vc4/vc4_hvs.c | 50 ++++++++++++---------- + drivers/gpu/drm/vc4/vc4_hvs.c | 52 ++++++++++++---------- drivers/gpu/drm/vc4/vc4_irq.c | 10 ++--- drivers/gpu/drm/vc4/vc4_kms.c | 14 +++--- drivers/gpu/drm/vc4/vc4_perfmon.c | 20 ++++----- @@ -31,7 +31,7 @@ Signed-off-by: Maxime Ripard drivers/gpu/drm/vc4/vc4_v3d.c | 10 ++--- drivers/gpu/drm/vc4/vc4_validate.c | 8 ++-- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 +- - 16 files changed, 126 insertions(+), 111 deletions(-) + 16 files changed, 127 insertions(+), 112 deletions(-) --- a/drivers/gpu/drm/vc4/tests/vc4_mock.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -481,6 +481,15 @@ Signed-off-by: Maxime Ripard VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) | --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -303,7 +303,7 @@ static void vc4_hvs_lut_load(struct vc4_ + if (!drm_dev_enter(drm, &idx)) + return; + +- if (hvs->vc4->is_vc5) ++ if (hvs->vc4->gen == VC4_GEN_5) + return; + + /* The LUT memory is laid out with each HVS channel in order, @@ -421,7 +421,7 @@ static void vc4_hvs_irq_enable_eof(const unsigned int channel) { diff --git a/target/linux/bcm27xx/patches-6.6/950-0661-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch b/target/linux/bcm27xx/patches-6.6/950-0661-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch index 1f0907f39..2886d9427 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0661-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0661-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch @@ -61,8 +61,8 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") if (offset >= gc->ngpio || offset < 0) return -EINVAL; -@@ -598,8 +598,8 @@ static int brcmstb_gpio_probe(struct pla - const __be32 *p; +@@ -596,8 +596,8 @@ static int brcmstb_gpio_probe(struct pla + struct resource *res; u32 bank_width; int num_banks = 0; + int num_gpios = 0; @@ -71,7 +71,7 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") unsigned long flags = 0; bool need_wakeup_event = false; -@@ -613,7 +613,6 @@ static int brcmstb_gpio_probe(struct pla +@@ -611,7 +611,6 @@ static int brcmstb_gpio_probe(struct pla if (IS_ERR(reg_base)) return PTR_ERR(reg_base); @@ -79,7 +79,7 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") priv->reg_base = reg_base; priv->pdev = pdev; -@@ -655,7 +654,7 @@ static int brcmstb_gpio_probe(struct pla +@@ -652,7 +651,7 @@ static int brcmstb_gpio_probe(struct pla dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", num_banks); num_banks++; @@ -88,7 +88,7 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") continue; } -@@ -697,7 +696,7 @@ static int brcmstb_gpio_probe(struct pla +@@ -694,7 +693,7 @@ static int brcmstb_gpio_probe(struct pla err = -ENOMEM; goto fail; } @@ -97,7 +97,7 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") gc->of_gpio_n_cells = 2; gc->of_xlate = brcmstb_gpio_of_xlate; /* not all ngpio lines are valid, will use bank width later */ -@@ -721,7 +720,7 @@ static int brcmstb_gpio_probe(struct pla +@@ -718,7 +717,7 @@ static int brcmstb_gpio_probe(struct pla bank->id); goto fail; } @@ -106,7 +106,7 @@ Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs") dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, gc->base, gc->ngpio, bank->width); -@@ -732,7 +731,7 @@ static int brcmstb_gpio_probe(struct pla +@@ -729,7 +728,7 @@ static int brcmstb_gpio_probe(struct pla num_banks++; } diff --git a/target/linux/bcm27xx/patches-6.6/950-0679-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch b/target/linux/bcm27xx/patches-6.6/950-0679-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch index f94efc25b..0b2b28b7f 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0679-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0679-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch @@ -15,7 +15,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c -@@ -1851,7 +1851,7 @@ __drm_fb_helper_initial_config_and_unloc +@@ -1853,7 +1853,7 @@ __drm_fb_helper_initial_config_and_unloc struct drm_device *dev = fb_helper->dev; struct fb_info *info; unsigned int width, height; @@ -24,7 +24,7 @@ Signed-off-by: Dave Stevenson width = dev->mode_config.max_width; height = dev->mode_config.max_height; -@@ -1879,6 +1879,15 @@ __drm_fb_helper_initial_config_and_unloc +@@ -1881,6 +1881,15 @@ __drm_fb_helper_initial_config_and_unloc * register the fbdev emulation instance in kernel_fb_helper_list. */ mutex_unlock(&fb_helper->lock); diff --git a/target/linux/bcm27xx/patches-6.6/950-0684-drm-fb_helper-Change-query-for-FB-designation-from-d.patch b/target/linux/bcm27xx/patches-6.6/950-0684-drm-fb_helper-Change-query-for-FB-designation-from-d.patch index 60cabdffc..a63bd11af 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0684-drm-fb_helper-Change-query-for-FB-designation-from-d.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0684-drm-fb_helper-Change-query-for-FB-designation-from-d.patch @@ -12,7 +12,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c -@@ -1879,11 +1879,11 @@ __drm_fb_helper_initial_config_and_unloc +@@ -1881,11 +1881,11 @@ __drm_fb_helper_initial_config_and_unloc * register the fbdev emulation instance in kernel_fb_helper_list. */ mutex_unlock(&fb_helper->lock); diff --git a/target/linux/bcm27xx/patches-6.6/950-0695-PCI-brcmstb-Change-RCB_-MPS-64B-_MODE-bits.patch b/target/linux/bcm27xx/patches-6.6/950-0695-PCI-brcmstb-Change-RCB_-MPS-64B-_MODE-bits.patch index dba20dc85..70d8229b4 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0695-PCI-brcmstb-Change-RCB_-MPS-64B-_MODE-bits.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0695-PCI-brcmstb-Change-RCB_-MPS-64B-_MODE-bits.patch @@ -45,7 +45,7 @@ Signed-off-by: Phil Elwell writel(tmp, base + PCIE_MISC_MISC_CTRL); brcm_pcie_set_tc_qos(pcie); -@@ -1917,6 +1918,7 @@ static int brcm_pcie_probe(struct platfo +@@ -1918,6 +1919,7 @@ static int brcm_pcie_probe(struct platfo pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss"); diff --git a/target/linux/bcm27xx/patches-6.6/950-0697-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch b/target/linux/bcm27xx/patches-6.6/950-0697-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch index a0006d6a5..604cf21b1 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0697-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0697-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch @@ -61,7 +61,7 @@ Signed-off-by: Jonathan Bell /* * Wait for 100ms after PERST# deassertion; see PCIe CEM specification -@@ -1919,6 +1940,7 @@ static int brcm_pcie_probe(struct platfo +@@ -1920,6 +1941,7 @@ static int brcm_pcie_probe(struct platfo pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss"); pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb"); diff --git a/target/linux/bcm27xx/patches-6.6/950-0732-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch b/target/linux/bcm27xx/patches-6.6/950-0732-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch index d6eb8cbba..0112ecad7 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0732-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0732-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch @@ -18,7 +18,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -528,11 +528,8 @@ static int dw_i2s_set_bclk_ratio(struct +@@ -527,11 +527,8 @@ static int dw_i2s_set_bclk_ratio(struct unsigned int ratio) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); diff --git a/target/linux/bcm27xx/patches-6.6/950-0737-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch b/target/linux/bcm27xx/patches-6.6/950-0737-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch index 31e1a5cfd..f71ca4cdf 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0737-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0737-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch @@ -18,7 +18,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c -@@ -1063,7 +1063,7 @@ static int ov7251_s_ctrl(struct v4l2_ctr +@@ -1065,7 +1065,7 @@ static int ov7251_s_ctrl(struct v4l2_ctr case V4L2_CID_EXPOSURE: ret = ov7251_set_exposure(ov7251, ctrl->val); break; @@ -27,7 +27,7 @@ Signed-off-by: Dave Stevenson ret = ov7251_set_gain(ov7251, ctrl->val); break; case V4L2_CID_TEST_PATTERN: -@@ -1588,7 +1588,7 @@ static int ov7251_init_ctrls(struct ov72 +@@ -1590,7 +1590,7 @@ static int ov7251_init_ctrls(struct ov72 ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_EXPOSURE, 1, 32, 1, 32); ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, diff --git a/target/linux/bcm27xx/patches-6.6/950-0750-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch b/target/linux/bcm27xx/patches-6.6/950-0750-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch index 4a7aaad3a..8bf58f54c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0750-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0750-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -796,7 +796,7 @@ static int dw_configure_dai_by_dt(struct +@@ -795,7 +795,7 @@ static int dw_configure_dai_by_dt(struct u32 idx2; int ret; diff --git a/target/linux/bcm27xx/patches-6.6/950-0752-ASoC-dwc-Fix-full-duplex-mode.patch b/target/linux/bcm27xx/patches-6.6/950-0752-ASoC-dwc-Fix-full-duplex-mode.patch index cffdd3302..d1814f55f 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0752-ASoC-dwc-Fix-full-duplex-mode.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0752-ASoC-dwc-Fix-full-duplex-mode.patch @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -235,10 +235,17 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -234,10 +234,17 @@ static void dw_i2s_config(struct dw_i2s_ { u32 ch_reg; struct i2s_clk_config_data *config = &dev->config; @@ -34,7 +34,7 @@ Signed-off-by: Phil Elwell for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) { i2s_write_reg(dev->i2s_base, TCR(ch_reg), -@@ -258,10 +265,6 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -257,10 +264,6 @@ static void dw_i2s_config(struct dw_i2s_ dmacr |= (DMACR_DMAEN_RXCH0 << ch_reg); } } @@ -45,7 +45,7 @@ Signed-off-by: Phil Elwell i2s_write_reg(dev->i2s_base, I2S_DMACR, dmacr); } -@@ -370,10 +373,13 @@ static int dw_i2s_startup(struct snd_pcm +@@ -369,10 +372,13 @@ static int dw_i2s_startup(struct snd_pcm dw_i2s_config(dev, substream->stream); dmacr = i2s_read_reg(dev->i2s_base, I2S_DMACR); diff --git a/target/linux/bcm27xx/patches-6.6/950-0767-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch b/target/linux/bcm27xx/patches-6.6/950-0767-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch index 824258ff8..607954b20 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0767-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0767-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch @@ -67,7 +67,7 @@ Signed-off-by: Jacopo Mondi +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -18619,6 +18619,13 @@ S: Supported +@@ -18621,6 +18621,13 @@ S: Supported F: drivers/iio/light/rohm-bu27008.c F: drivers/iio/light/rohm-bu27034.c diff --git a/target/linux/bcm27xx/patches-6.6/950-0768-media-i2c-Add-driver-for-OmniVision-OV64A40.patch b/target/linux/bcm27xx/patches-6.6/950-0768-media-i2c-Add-driver-for-OmniVision-OV64A40.patch index 986cc49de..6075a9d5e 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0768-media-i2c-Add-driver-for-OmniVision-OV64A40.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0768-media-i2c-Add-driver-for-OmniVision-OV64A40.patch @@ -16,7 +16,7 @@ Signed-off-by: Jacopo Mondi --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -15871,6 +15871,14 @@ S: Maintained +@@ -15873,6 +15873,14 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov5695.c diff --git a/target/linux/bcm27xx/patches-6.6/950-0809-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch b/target/linux/bcm27xx/patches-6.6/950-0809-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch index 49ca7d633..17b1b3226 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0809-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0809-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch @@ -18,7 +18,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -314,6 +314,25 @@ static int dw_i2s_hw_params(struct snd_p +@@ -313,6 +313,25 @@ static int dw_i2s_hw_params(struct snd_p if (dev->tdm_slots) config->data_width = 32; @@ -44,7 +44,7 @@ Signed-off-by: Phil Elwell config->chan_nr = params_channels(params); switch (config->chan_nr) { -@@ -537,23 +556,7 @@ static int dw_i2s_set_bclk_ratio(struct +@@ -536,23 +555,7 @@ static int dw_i2s_set_bclk_ratio(struct dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio); @@ -69,7 +69,7 @@ Signed-off-by: Phil Elwell return 0; } -@@ -1068,6 +1071,7 @@ static int dw_i2s_probe(struct platform_ +@@ -1067,6 +1070,7 @@ static int dw_i2s_probe(struct platform_ } } diff --git a/target/linux/bcm27xx/patches-6.6/950-0853-drivers-usb-dwc3-add-FS-LS-bus-instance-parkmode-dis.patch b/target/linux/bcm27xx/patches-6.6/950-0853-drivers-usb-dwc3-add-FS-LS-bus-instance-parkmode-dis.patch index 706baccdc..77aebef46 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0853-drivers-usb-dwc3-add-FS-LS-bus-instance-parkmode-dis.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0853-drivers-usb-dwc3-add-FS-LS-bus-instance-parkmode-dis.patch @@ -16,7 +16,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c -@@ -1372,6 +1372,9 @@ static int dwc3_core_init(struct dwc3 *d +@@ -1367,6 +1367,9 @@ static int dwc3_core_init(struct dwc3 *d if (dwc->parkmode_disable_hs_quirk) reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS; @@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) && (dwc->maximum_speed == USB_SPEED_HIGH || dwc->maximum_speed == USB_SPEED_FULL)) -@@ -1670,6 +1673,8 @@ static void dwc3_get_properties(struct d +@@ -1634,6 +1637,8 @@ static void dwc3_get_properties(struct d "snps,parkmode-disable-ss-quirk"); dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev, "snps,parkmode-disable-hs-quirk"); @@ -37,7 +37,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h -@@ -271,6 +271,7 @@ +@@ -268,6 +268,7 @@ #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) #define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16) @@ -45,7 +45,7 @@ Signed-off-by: Jonathan Bell #define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10) /* Global Status Register */ -@@ -1117,10 +1118,12 @@ struct dwc3_scratchpad_array { +@@ -1118,10 +1119,12 @@ struct dwc3_scratchpad_array { * generation after resume from suspend. * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin * VBUS with an external supply. diff --git a/target/linux/bcm27xx/patches-6.6/950-0923-drm-Add-DRM_MODE_TV_MODE_MONOCHROME.patch b/target/linux/bcm27xx/patches-6.6/950-0923-drm-Add-DRM_MODE_TV_MODE_MONOCHROME.patch index 485196b15..2fd127848 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0923-drm-Add-DRM_MODE_TV_MODE_MONOCHROME.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0923-drm-Add-DRM_MODE_TV_MODE_MONOCHROME.patch @@ -21,7 +21,7 @@ Signed-off-by: Nick Hollinghurst --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c -@@ -1049,6 +1049,7 @@ static const struct drm_prop_enum_list d +@@ -1050,6 +1050,7 @@ static const struct drm_prop_enum_list d { DRM_MODE_TV_MODE_PAL_M, "PAL-M" }, { DRM_MODE_TV_MODE_PAL_N, "PAL-N" }, { DRM_MODE_TV_MODE_SECAM, "SECAM" }, @@ -29,7 +29,7 @@ Signed-off-by: Nick Hollinghurst }; DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list) -@@ -1735,6 +1736,12 @@ EXPORT_SYMBOL(drm_connector_attach_dp_su +@@ -1740,6 +1741,12 @@ EXPORT_SYMBOL(drm_connector_attach_dp_su * TV Mode is CCIR System B (aka 625-lines) together with * the SECAM Color Encoding. * diff --git a/target/linux/bcm27xx/patches-6.6/950-0931-fixup-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_param.patch b/target/linux/bcm27xx/patches-6.6/950-0931-fixup-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_param.patch index 9f5c2b3a4..7174cf0a5 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0931-fixup-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_param.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0931-fixup-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_param.patch @@ -13,7 +13,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -288,21 +288,18 @@ static int dw_i2s_hw_params(struct snd_p +@@ -287,21 +287,18 @@ static int dw_i2s_hw_params(struct snd_p case SNDRV_PCM_FORMAT_S16_LE: config->data_width = 16; dma_data->dt.addr_width = 2; @@ -35,7 +35,7 @@ Signed-off-by: Phil Elwell dev->xfer_resolution = 0x05; break; -@@ -314,25 +311,6 @@ static int dw_i2s_hw_params(struct snd_p +@@ -313,25 +310,6 @@ static int dw_i2s_hw_params(struct snd_p if (dev->tdm_slots) config->data_width = 32; @@ -61,7 +61,7 @@ Signed-off-by: Phil Elwell config->chan_nr = params_channels(params); switch (config->chan_nr) { -@@ -348,11 +326,31 @@ static int dw_i2s_hw_params(struct snd_p +@@ -347,11 +325,31 @@ static int dw_i2s_hw_params(struct snd_p dw_i2s_config(dev, substream->stream); @@ -95,7 +95,7 @@ Signed-off-by: Phil Elwell if (dev->i2s_clk_cfg) { ret = dev->i2s_clk_cfg(config); if (ret < 0) { -@@ -360,8 +358,7 @@ static int dw_i2s_hw_params(struct snd_p +@@ -359,8 +357,7 @@ static int dw_i2s_hw_params(struct snd_p return ret; } } else { @@ -105,7 +105,7 @@ Signed-off-by: Phil Elwell ret = clk_set_rate(dev->clk, bitclk); if (ret) { -@@ -370,6 +367,8 @@ static int dw_i2s_hw_params(struct snd_p +@@ -369,6 +366,8 @@ static int dw_i2s_hw_params(struct snd_p return ret; } } diff --git a/target/linux/bcm27xx/patches-6.6/950-0951-ASoC-dwc-Correct-channel-count-reporting.patch b/target/linux/bcm27xx/patches-6.6/950-0951-ASoC-dwc-Correct-channel-count-reporting.patch index 8f54995dc..aaf9e32b4 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0951-ASoC-dwc-Correct-channel-count-reporting.patch +++ b/target/linux/bcm27xx/patches-6.6/950-0951-ASoC-dwc-Correct-channel-count-reporting.patch @@ -17,7 +17,7 @@ Signed-off-by: Phil Elwell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -320,7 +320,7 @@ static int dw_i2s_hw_params(struct snd_p +@@ -319,7 +319,7 @@ static int dw_i2s_hw_params(struct snd_p case TWO_CHANNEL_SUPPORT: break; default: @@ -26,7 +26,7 @@ Signed-off-by: Phil Elwell return -EINVAL; } -@@ -708,7 +708,7 @@ static int dw_configure_dai(struct dw_i2 +@@ -707,7 +707,7 @@ static int dw_configure_dai(struct dw_i2 idx = 1; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->playback.channels_max = @@ -35,7 +35,7 @@ Signed-off-by: Phil Elwell dw_i2s_dai->playback.formats = formats[idx]; dw_i2s_dai->playback.rates = rates; } -@@ -722,7 +722,7 @@ static int dw_configure_dai(struct dw_i2 +@@ -721,7 +721,7 @@ static int dw_configure_dai(struct dw_i2 idx = 1; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->capture.channels_max = diff --git a/target/linux/bcm27xx/patches-6.6/950-1051-drm-panel-Add-and-initialise-an-orientation-field-to.patch b/target/linux/bcm27xx/patches-6.6/950-1051-drm-panel-Add-and-initialise-an-orientation-field-to.patch index 694e2a008..9dcdb585b 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1051-drm-panel-Add-and-initialise-an-orientation-field-to.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1051-drm-panel-Add-and-initialise-an-orientation-field-to.patch @@ -22,7 +22,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c -@@ -2697,10 +2697,15 @@ int drm_connector_set_orientation_from_p +@@ -2702,10 +2702,15 @@ int drm_connector_set_orientation_from_p { enum drm_panel_orientation orientation; diff --git a/target/linux/bcm27xx/patches-6.6/950-1128-pwm-Make-it-possible-to-apply-PWM-changes-in-atomic-.patch b/target/linux/bcm27xx/patches-6.6/950-1128-pwm-Make-it-possible-to-apply-PWM-changes-in-atomic-.patch index 269bb7219..337768312 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1128-pwm-Make-it-possible-to-apply-PWM-changes-in-atomic-.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1128-pwm-Make-it-possible-to-apply-PWM-changes-in-atomic-.patch @@ -48,7 +48,7 @@ Signed-off-by: Thierry Reding drivers cannot. If you rely on getting the inactive state, use .duty_cycle=0, --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17437,7 +17437,7 @@ F: drivers/video/backlight/pwm_bl.c +@@ -17439,7 +17439,7 @@ F: drivers/video/backlight/pwm_bl.c F: include/dt-bindings/pwm/ F: include/linux/pwm.h F: include/linux/pwm_backlight.h diff --git a/target/linux/bcm27xx/patches-6.6/950-1149-media-uapi-Add-Raspberry-Pi-PiSP-Back-End-uAPI.patch b/target/linux/bcm27xx/patches-6.6/950-1149-media-uapi-Add-Raspberry-Pi-PiSP-Back-End-uAPI.patch index a59bcb1e9..a2e36d50d 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1149-media-uapi-Add-Raspberry-Pi-PiSP-Back-End-uAPI.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1149-media-uapi-Add-Raspberry-Pi-PiSP-Back-End-uAPI.patch @@ -23,7 +23,7 @@ Signed-off-by: Jacopo Mondi --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -18032,6 +18032,13 @@ L: linux-wireless@vger.kernel.org +@@ -18034,6 +18034,13 @@ L: linux-wireless@vger.kernel.org S: Orphan F: drivers/net/wireless/legacy/ray* diff --git a/target/linux/bcm27xx/patches-6.6/950-1152-media-dt-bindings-Add-bindings-for-Raspberry-Pi-PiSP.patch b/target/linux/bcm27xx/patches-6.6/950-1152-media-dt-bindings-Add-bindings-for-Raspberry-Pi-PiSP.patch index 30c5cb350..12f91d978 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1152-media-dt-bindings-Add-bindings-for-Raspberry-Pi-PiSP.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1152-media-dt-bindings-Add-bindings-for-Raspberry-Pi-PiSP.patch @@ -87,7 +87,7 @@ Reviewed-by: Naushir Patuck + }; --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -18037,6 +18037,7 @@ M: Jacopo Mondi L: linux-media@vger.kernel.org S: Maintained diff --git a/target/linux/bcm27xx/patches-6.6/950-1164-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch b/target/linux/bcm27xx/patches-6.6/950-1164-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch index 487e508ea..785b36e3c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1164-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1164-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch @@ -94,7 +94,7 @@ Signed-off-by: Richard Oliver + --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -18027,6 +18027,11 @@ F: drivers/ras/ +@@ -18029,6 +18029,11 @@ F: drivers/ras/ F: include/linux/ras.h F: include/ras/ras_event.h diff --git a/target/linux/bcm27xx/patches-6.6/950-1165-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch b/target/linux/bcm27xx/patches-6.6/950-1165-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch index 68e2ef600..b5f5014b0 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1165-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1165-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch @@ -25,7 +25,7 @@ Signed-off-by: Richard Oliver --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -18031,6 +18031,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER +@@ -18033,6 +18033,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER M: Raspberry Pi Kernel Maintenance S: Maintained F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml @@ -56,7 +56,7 @@ Signed-off-by: Richard Oliver depends on RENESAS_RPCIF --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile -@@ -115,6 +115,7 @@ obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockc +@@ -116,6 +116,7 @@ obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockc obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o diff --git a/target/linux/bcm27xx/patches-6.6/950-1197-sound-soc-dwc-i2s-choose-FIFO-thresholds-based-on-DM.patch b/target/linux/bcm27xx/patches-6.6/950-1197-sound-soc-dwc-i2s-choose-FIFO-thresholds-based-on-DM.patch index d0e3bc1fa..18bd76570 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1197-sound-soc-dwc-i2s-choose-FIFO-thresholds-based-on-DM.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1197-sound-soc-dwc-i2s-choose-FIFO-thresholds-based-on-DM.patch @@ -22,7 +22,7 @@ Signed-off-by: Jonathan Bell --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c -@@ -236,6 +236,8 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -235,6 +235,8 @@ static void dw_i2s_config(struct dw_i2s_ u32 ch_reg; struct i2s_clk_config_data *config = &dev->config; u32 dmacr; @@ -31,7 +31,7 @@ Signed-off-by: Jonathan Bell i2s_disable_channels(dev, stream); -@@ -251,7 +253,7 @@ static void dw_i2s_config(struct dw_i2s_ +@@ -250,7 +252,7 @@ static void dw_i2s_config(struct dw_i2s_ i2s_write_reg(dev->i2s_base, TCR(ch_reg), dev->xfer_resolution); i2s_write_reg(dev->i2s_base, TFCR(ch_reg), @@ -40,7 +40,7 @@ Signed-off-by: Jonathan Bell i2s_write_reg(dev->i2s_base, TER(ch_reg), TER_TXCHEN | dev->tdm_mask << TER_TXSLOT_SHIFT); dmacr |= (DMACR_DMAEN_TXCH0 << ch_reg); -@@ -783,8 +785,8 @@ static int dw_configure_dai_by_pd(struct +@@ -782,8 +784,8 @@ static int dw_configure_dai_by_pd(struct dev->capture_dma_data.pd.data = pdata->capture_dma_data; dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; @@ -51,7 +51,7 @@ Signed-off-by: Jonathan Bell dev->play_dma_data.pd.addr_width = bus_widths[idx]; dev->capture_dma_data.pd.addr_width = bus_widths[idx]; dev->play_dma_data.pd.filter = pdata->filter; -@@ -815,7 +817,10 @@ static int dw_configure_dai_by_dt(struct +@@ -814,7 +816,10 @@ static int dw_configure_dai_by_dt(struct dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; dev->play_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2]) >> 8; @@ -63,7 +63,7 @@ Signed-off-by: Jonathan Bell } if (COMP1_RX_ENABLED(comp1)) { idx2 = COMP2_RX_WORDSIZE_0(comp2); -@@ -824,9 +829,14 @@ static int dw_configure_dai_by_dt(struct +@@ -823,9 +828,14 @@ static int dw_configure_dai_by_dt(struct dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; dev->capture_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2] >> 8); @@ -79,7 +79,7 @@ Signed-off-by: Jonathan Bell return 0; } -@@ -1070,6 +1080,7 @@ static int dw_i2s_probe(struct platform_ +@@ -1069,6 +1079,7 @@ static int dw_i2s_probe(struct platform_ } } diff --git a/target/linux/bcm27xx/patches-6.6/950-1218-Bluetooth-hci_sync-Fix-crash-on-NULL-parent.patch b/target/linux/bcm27xx/patches-6.6/950-1218-Bluetooth-hci_sync-Fix-crash-on-NULL-parent.patch index 223e2df2b..6dfd31c70 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1218-Bluetooth-hci_sync-Fix-crash-on-NULL-parent.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1218-Bluetooth-hci_sync-Fix-crash-on-NULL-parent.patch @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c -@@ -4872,7 +4872,8 @@ static const struct { +@@ -4873,7 +4873,8 @@ static const struct { */ static int hci_dev_setup_sync(struct hci_dev *hdev) { diff --git a/target/linux/bcm27xx/patches-6.6/950-1243-media-dt-bindings-i2c-Add-Sony-IMX500.patch b/target/linux/bcm27xx/patches-6.6/950-1243-media-dt-bindings-i2c-Add-Sony-IMX500.patch index 82e58c12b..d4979f73e 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1243-media-dt-bindings-i2c-Add-Sony-IMX500.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1243-media-dt-bindings-i2c-Add-Sony-IMX500.patch @@ -150,7 +150,7 @@ Signed-off-by: Richard Oliver + --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20127,6 +20127,13 @@ F: Documentation/devicetree/bindings/med +@@ -20129,6 +20129,13 @@ F: Documentation/devicetree/bindings/med F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-6.6/950-1244-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch b/target/linux/bcm27xx/patches-6.6/950-1244-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch index 331e5863a..12e5e2b9e 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1244-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1244-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch @@ -21,7 +21,7 @@ Signed-off-by: Richard Oliver --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -20133,6 +20133,7 @@ L: linux-media@vger.kernel.org +@@ -20135,6 +20135,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 diff --git a/target/linux/bcm27xx/patches-6.6/950-1261-mm-vmscan-Maintain-TLB-coherency-in-LRU-code.patch b/target/linux/bcm27xx/patches-6.6/950-1261-mm-vmscan-Maintain-TLB-coherency-in-LRU-code.patch index 70ece9750..3c60865aa 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1261-mm-vmscan-Maintain-TLB-coherency-in-LRU-code.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1261-mm-vmscan-Maintain-TLB-coherency-in-LRU-code.patch @@ -16,7 +16,7 @@ Signed-off-by: Phil Elwell --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4716,7 +4716,7 @@ void lru_gen_look_around(struct page_vma +@@ -4723,7 +4723,7 @@ void lru_gen_look_around(struct page_vma if (!folio) continue; diff --git a/target/linux/bcm27xx/patches-6.6/950-1349-cgroup-Use-kernel-command-line-to-disable-memory-cgr.patch b/target/linux/bcm27xx/patches-6.6/950-1349-cgroup-Use-kernel-command-line-to-disable-memory-cgr.patch index 61dec8938..b1ceaad71 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1349-cgroup-Use-kernel-command-line-to-disable-memory-cgr.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1349-cgroup-Use-kernel-command-line-to-disable-memory-cgr.patch @@ -148,7 +148,7 @@ Signed-off-by: Maíra Canal --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c -@@ -6063,9 +6063,6 @@ int __init cgroup_init_early(void) +@@ -6059,9 +6059,6 @@ int __init cgroup_init_early(void) return 0; } @@ -158,7 +158,7 @@ Signed-off-by: Maíra Canal /** * cgroup_init - cgroup initialization * -@@ -6099,12 +6096,6 @@ int __init cgroup_init(void) +@@ -6095,12 +6092,6 @@ int __init cgroup_init(void) cgroup_unlock(); @@ -171,7 +171,7 @@ Signed-off-by: Maíra Canal for_each_subsys(ss, ssid) { if (ss->early_init) { struct cgroup_subsys_state *css = -@@ -6745,10 +6736,6 @@ static int __init cgroup_disable(char *s +@@ -6749,10 +6740,6 @@ static int __init cgroup_disable(char *s strcmp(token, ss->legacy_name)) continue; @@ -182,7 +182,7 @@ Signed-off-by: Maíra Canal static_branch_disable(cgroup_subsys_enabled_key[i]); pr_info("Disabling %s control group subsystem\n", ss->name); -@@ -6782,7 +6769,7 @@ static int __init cgroup_enable(char *st +@@ -6786,7 +6773,7 @@ static int __init cgroup_enable(char *st strcmp(token, ss->legacy_name)) continue; diff --git a/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch b/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch new file mode 100644 index 000000000..1f5ed7545 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1351-drivers-media-bcm2835_isp-Cache-LS-table-dmabuf.patch @@ -0,0 +1,141 @@ +From 25e6acfe00f589a5989ebd2c8d21a130fb3bf106 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Fri, 18 Oct 2024 09:18:10 +0100 +Subject: [PATCH] drivers: media: bcm2835_isp: Cache LS table dmabuf + +Clients such as libcamera do not change the LS table dmabuf on every +frame. In such cases instead of mapping/remapping the same dmabuf on +every frame to send to the firmware, cache the dmabuf once and only +update and remap if the dmabuf has been changed by the userland client. + +Signed-off-by: Naushir Patuck +--- + .../bcm2835-isp/bcm2835-v4l2-isp.c | 77 +++++++++++-------- + 1 file changed, 46 insertions(+), 31 deletions(-) + +--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c ++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c +@@ -139,6 +139,8 @@ struct bcm2835_isp_dev { + /* Image pipeline controls. */ + int r_gain; + int b_gain; ++ struct dma_buf *last_ls_dmabuf; ++ struct mmal_parameter_lens_shading_v2 ls; + }; + + struct bcm2835_isp_buffer { +@@ -657,18 +659,18 @@ static void bcm2835_isp_node_stop_stream + atomic_dec(&dev->num_streaming); + /* If all ports disabled, then disable the component */ + if (atomic_read(&dev->num_streaming) == 0) { +- struct bcm2835_isp_lens_shading ls; + /* + * The ISP component on the firmware has a reference to the + * dmabuf handle for the lens shading table. Pass a null handle + * to remove that reference now. + */ +- memset(&ls, 0, sizeof(ls)); ++ memset(&dev->ls, 0, sizeof(dev->ls)); + /* Must set a valid grid size for the FW */ +- ls.grid_cell_size = 16; ++ dev->ls.grid_cell_size = 16; + set_isp_param(&dev->node[0], + MMAL_PARAMETER_LENS_SHADING_OVERRIDE, +- &ls, sizeof(ls)); ++ &dev->ls, sizeof(dev->ls)); ++ dev->last_ls_dmabuf = NULL; + + ret = vchiq_mmal_component_disable(dev->mmal_instance, + dev->component); +@@ -719,6 +721,36 @@ static inline unsigned int get_sizeimage + return (bpl * height * fmt->size_multiplier_x2) >> 1; + } + ++static int map_ls_table(struct bcm2835_isp_dev *dev, struct dma_buf *dmabuf, ++ const struct bcm2835_isp_lens_shading *v4l2_ls) ++{ ++ void *vcsm_handle; ++ int ret; ++ ++ if (IS_ERR_OR_NULL(dmabuf)) ++ return -EINVAL; ++ ++ /* ++ * struct bcm2835_isp_lens_shading and struct ++ * mmal_parameter_lens_shading_v2 match so that we can do a ++ * simple memcpy here. ++ * Only the dmabuf to the actual table needs any manipulation. ++ */ ++ memcpy(&dev->ls, v4l2_ls, sizeof(dev->ls)); ++ ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle); ++ if (ret) { ++ dma_buf_put(dmabuf); ++ return ret; ++ } ++ ++ dev->ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle); ++ dev->last_ls_dmabuf = dmabuf; ++ ++ vc_sm_cma_free(vcsm_handle); ++ ++ return 0; ++} ++ + static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct bcm2835_isp_dev *dev = +@@ -754,44 +786,27 @@ static int bcm2835_isp_s_ctrl(struct v4l + case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING: + { + struct bcm2835_isp_lens_shading *v4l2_ls; +- struct mmal_parameter_lens_shading_v2 ls; +- struct dma_buf *dmabuf; +- void *vcsm_handle; + + v4l2_ls = (struct bcm2835_isp_lens_shading *)ctrl->p_new.p_u8; +- /* +- * struct bcm2835_isp_lens_shading and struct +- * mmal_parameter_lens_shading_v2 match so that we can do a +- * simple memcpy here. +- * Only the dmabuf to the actual table needs any manipulation. +- */ +- memcpy(&ls, v4l2_ls, sizeof(ls)); ++ struct dma_buf *dmabuf = dma_buf_get(v4l2_ls->dmabuf); + +- dmabuf = dma_buf_get(v4l2_ls->dmabuf); +- if (IS_ERR_OR_NULL(dmabuf)) +- return -EINVAL; +- +- ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle); +- if (ret) { +- dma_buf_put(dmabuf); +- return -EINVAL; +- } ++ if (dmabuf != dev->last_ls_dmabuf) ++ ret = map_ls_table(dev, dmabuf, v4l2_ls); + +- ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle); +- if (ls.mem_handle_table) +- /* The VPU will take a reference on the vcsm handle, ++ if (!ret && dev->ls.mem_handle_table) ++ /* ++ * The VPU will take a reference on the vcsm handle, + * which in turn will retain a reference on the dmabuf. + * This code can therefore safely release all + * references to the buffer. + */ +- ret = set_isp_param(node, +- MMAL_PARAMETER_LENS_SHADING_OVERRIDE, +- &ls, +- sizeof(ls)); ++ ret = ++ set_isp_param(node, ++ MMAL_PARAMETER_LENS_SHADING_OVERRIDE, ++ &dev->ls, sizeof(dev->ls)); + else + ret = -EINVAL; + +- vc_sm_cma_free(vcsm_handle); + dma_buf_put(dmabuf); + break; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch new file mode 100644 index 000000000..5b89f60bc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1353-pwm-Add-GPIO-PWM-driver.patch @@ -0,0 +1,330 @@ +From 3ab72fc21ea8576e59f6aad10bd6b1a0eae6e5eb Mon Sep 17 00:00:00 2001 +From: Vincent Whitchurch +Date: Tue, 4 Jun 2024 23:00:41 +0200 +Subject: [PATCH] pwm: Add GPIO PWM driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 7f61257cd6e1ad4769b4b819668cab00f68f2556 upstream. + +Add a software PWM which toggles a GPIO from a high-resolution timer. + +This will naturally not be as accurate or as efficient as a hardware +PWM, but it is useful in some cases. I have for example used it for +evaluating LED brightness handling (via leds-pwm) on a board where the +LED was just hooked up to a GPIO, and for a simple verification of the +timer frequency on another platform. + +Since high-resolution timers are used, sleeping GPIO chips are not +supported and are rejected in the probe function. + +Signed-off-by: Vincent Whitchurch +Co-developed-by: Stefan Wahren +Signed-off-by: Stefan Wahren +Co-developed-by: Linus Walleij +Reviewed-by: Andy Shevchenko +Signed-off-by: Linus Walleij +Reviewed-by: Dhruva Gole +Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-2-6b67cf60db92@linaro.org +Signed-off-by: Uwe Kleine-König +Signed-off-by: Tim Gover + +pwm: Backport pwm-gpio.c to rpi-6.6.y +--- + .../driver-api/gpio/drivers-on-gpio.rst | 7 +- + drivers/pwm/Kconfig | 11 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-gpio.c | 240 ++++++++++++++++++ + 4 files changed, 258 insertions(+), 1 deletion(-) + create mode 100644 drivers/pwm/pwm-gpio.c + +--- a/Documentation/driver-api/gpio/drivers-on-gpio.rst ++++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst +@@ -27,7 +27,12 @@ hardware descriptions such as device tre + to the lines for a more permanent solution of this type. + + - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from +- an external speaker connected to a GPIO line. ++ an external speaker connected to a GPIO line. (If the beep is controlled by ++ off/on, for an actual PWM waveform, see pwm-gpio below.) ++ ++- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high ++ resolution timer producing a PWM waveform on the GPIO line, as well as ++ Linux high resolution timers can do. + + - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an + external connector status, such as a headset line for an audio driver or an +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -217,6 +217,17 @@ config PWM_FSL_FTM + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + ++config PWM_GPIO ++ tristate "GPIO PWM support" ++ depends on GPIOLIB ++ depends on HIGH_RES_TIMERS ++ help ++ Generic PWM framework driver for software PWM toggling a GPIO pin ++ from kernel high-resolution timers. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-gpio. ++ + config PWM_HIBVT + tristate "HiSilicon BVT PWM support" + depends on ARCH_HISI || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec + obj-$(CONFIG_PWM_DWC) += pwm-dwc.o + obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o + obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o ++obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o + obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o + obj-$(CONFIG_PWM_IMG) += pwm-img.o + obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +--- /dev/null ++++ b/drivers/pwm/pwm-gpio.c +@@ -0,0 +1,240 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Generic software PWM for modulating GPIOs ++ * ++ * Copyright (C) 2020 Axis Communications AB ++ * Copyright (C) 2020 Nicola Di Lieto ++ * Copyright (C) 2024 Stefan Wahren ++ * Copyright (C) 2024 Linus Walleij ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct pwm_gpio { ++ struct hrtimer gpio_timer; ++ struct gpio_desc *gpio; ++ struct pwm_state state; ++ struct pwm_state next_state; ++ ++ /* Protect internal state between pwm_ops and hrtimer */ ++ spinlock_t lock; ++ ++ bool changing; ++ bool running; ++ bool level; ++ struct pwm_chip chip; ++}; ++ ++static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src) ++{ ++ u64 dividend; ++ u32 remainder; ++ ++ *dest = *src; ++ ++ /* Round down to hrtimer resolution */ ++ dividend = dest->period; ++ remainder = do_div(dividend, hrtimer_resolution); ++ dest->period -= remainder; ++ ++ dividend = dest->duty_cycle; ++ remainder = do_div(dividend, hrtimer_resolution); ++ dest->duty_cycle -= remainder; ++} ++ ++static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level) ++{ ++ const struct pwm_state *state = &gpwm->state; ++ bool invert = state->polarity == PWM_POLARITY_INVERSED; ++ ++ gpwm->level = level; ++ gpiod_set_value(gpwm->gpio, gpwm->level ^ invert); ++ ++ if (!state->duty_cycle || state->duty_cycle == state->period) { ++ gpwm->running = false; ++ return 0; ++ } ++ ++ gpwm->running = true; ++ return level ? state->duty_cycle : state->period - state->duty_cycle; ++} ++ ++static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer) ++{ ++ struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio, ++ gpio_timer); ++ u64 next_toggle; ++ bool new_level; ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ /* Apply new state at end of current period */ ++ if (!gpwm->level && gpwm->changing) { ++ gpwm->changing = false; ++ gpwm->state = gpwm->next_state; ++ new_level = !!gpwm->state.duty_cycle; ++ } else { ++ new_level = !gpwm->level; ++ } ++ ++ next_toggle = pwm_gpio_toggle(gpwm, new_level); ++ if (next_toggle) ++ hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer), ++ ns_to_ktime(next_toggle)); ++ ++ return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART; ++} ++ ++static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip); ++ bool invert = state->polarity == PWM_POLARITY_INVERSED; ++ ++ if (state->duty_cycle && state->duty_cycle < hrtimer_resolution) ++ return -EINVAL; ++ ++ if (state->duty_cycle != state->period && ++ (state->period - state->duty_cycle < hrtimer_resolution)) ++ return -EINVAL; ++ ++ if (!state->enabled) { ++ hrtimer_cancel(&gpwm->gpio_timer); ++ } else if (!gpwm->running) { ++ int ret; ++ ++ /* ++ * This just enables the output, but pwm_gpio_toggle() ++ * really starts the duty cycle. ++ */ ++ ret = gpiod_direction_output(gpwm->gpio, invert); ++ if (ret) ++ return ret; ++ } ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ if (!state->enabled) { ++ pwm_gpio_round(&gpwm->state, state); ++ gpwm->running = false; ++ gpwm->changing = false; ++ ++ gpiod_set_value(gpwm->gpio, invert); ++ } else if (gpwm->running) { ++ pwm_gpio_round(&gpwm->next_state, state); ++ gpwm->changing = true; ++ } else { ++ unsigned long next_toggle; ++ ++ pwm_gpio_round(&gpwm->state, state); ++ gpwm->changing = false; ++ ++ next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle); ++ if (next_toggle) ++ hrtimer_start(&gpwm->gpio_timer, next_toggle, ++ HRTIMER_MODE_REL); ++ } ++ ++ return 0; ++} ++ ++static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip); ++ ++ guard(spinlock_irqsave)(&gpwm->lock); ++ ++ if (gpwm->changing) ++ *state = gpwm->next_state; ++ else ++ *state = gpwm->state; ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_gpio_ops = { ++ .apply = pwm_gpio_apply, ++ .get_state = pwm_gpio_get_state, ++}; ++ ++static void pwm_gpio_disable_hrtimer(void *data) ++{ ++ struct pwm_gpio *gpwm = data; ++ ++ hrtimer_cancel(&gpwm->gpio_timer); ++} ++ ++static int pwm_gpio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct pwm_chip *chip; ++ struct pwm_gpio *gpwm; ++ int ret; ++ ++ gpwm = devm_kzalloc(&pdev->dev, sizeof(*gpwm), GFP_KERNEL); ++ if (IS_ERR(gpwm)) ++ return PTR_ERR(gpwm); ++ ++ chip = &gpwm->chip; ++ ++ spin_lock_init(&gpwm->lock); ++ ++ gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS); ++ if (IS_ERR(gpwm->gpio)) ++ return dev_err_probe(dev, PTR_ERR(gpwm->gpio), ++ "%pfw: could not get gpio\n", ++ dev_fwnode(dev)); ++ ++ if (gpiod_cansleep(gpwm->gpio)) ++ return dev_err_probe(dev, -EINVAL, ++ "%pfw: sleeping GPIO not supported\n", ++ dev_fwnode(dev)); ++ ++ chip->dev = dev; ++ chip->ops = &pwm_gpio_ops; ++ chip->atomic = true; ++ chip->npwm = 1; ++ ++ hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm); ++ if (ret) ++ return ret; ++ ++ gpwm->gpio_timer.function = pwm_gpio_timer; ++ ++ return devm_pwmchip_add(dev, chip); ++} ++ ++static const struct of_device_id pwm_gpio_dt_ids[] = { ++ { .compatible = "pwm-gpio" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids); ++ ++static struct platform_driver pwm_gpio_driver = { ++ .driver = { ++ .name = "pwm-gpio", ++ .of_match_table = pwm_gpio_dt_ids, ++ }, ++ .probe = pwm_gpio_probe, ++}; ++module_platform_driver(pwm_gpio_driver); ++ ++MODULE_DESCRIPTION("PWM GPIO driver"); ++MODULE_AUTHOR("Vincent Whitchurch"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch b/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch new file mode 100644 index 000000000..65c6be6f3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1355-dtoverlay-Add-a-dtoverlay-for-pwm-gpio.patch @@ -0,0 +1,79 @@ +From ff0fe12ab875d587348b6f2b9e73ae928049ebee Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Thu, 31 Oct 2024 16:12:54 +0000 +Subject: [PATCH] dtoverlay: Add a dtoverlay for pwm-gpio + +Signed-off-by: Tim Gover +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 6 +++ + .../boot/dts/overlays/pwm-gpio-overlay.dts | 38 +++++++++++++++++++ + 3 files changed, 45 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -217,6 +217,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + proto-codec.dtbo \ + pwm.dtbo \ + pwm-2chan.dtbo \ ++ pwm-gpio.dtbo \ + pwm-ir-tx.dtbo \ + pwm1.dtbo \ + qca7000.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3903,6 +3903,12 @@ Params: pin Output p + clock PWM clock frequency (informational) + + ++Name: pwm-gpio ++Info: Configures the software PWM GPIO driver ++Load: dtoverlay=pwm-gpio,= ++Params: gpio Output pin (default 4) ++ ++ + Name: pwm-ir-tx + Info: Use GPIO pin as pwm-assisted infrared transmitter output. + This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts +@@ -0,0 +1,38 @@ ++// Device tree overlay for software GPIO PWM. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_gpio_pins: pwm_gpio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ brcm,function = <1>; /* output */ ++ brcm,pull = <0>; /* pull-none */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_gpio: pwm_gpio@4 { ++ compatible = "pwm-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_gpio_pins>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&pwm_gpio>,"gpios:4", ++ <&pwm_gpio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&pwm_gpio>,"reg:0", ++ <&pwm_gpio_pins>,"reg:0"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch b/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch new file mode 100644 index 000000000..245c575e2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1356-dts-2712-Drop-some-numa-options-from-bootargs.patch @@ -0,0 +1,29 @@ +From 624eb357e1a16385b3d6171e9194e4c5f8d4fd5f Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Wed, 23 Oct 2024 19:09:18 +0100 +Subject: [PATCH] dts: 2712: Drop some numa options from bootargs + +iommu_dma_numa_policy=interleave is not valid in the current tree +It generates an unknown setting will be passed to usespace warning + +system_heap.max_order=0 is wanted when numa is enabled, but may not +be when it is disabled. + +Add it on firmware side when we know if numa=fake= is used. + +Signed-off-by: Dom Cobley +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -99,7 +99,7 @@ + + / { + chosen: chosen { +- bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0"; ++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave"; + stdout-path = "serial10:115200n8"; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch b/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch new file mode 100644 index 000000000..ef5a5a5dc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1357-mmc-quirks-add-more-broken-Kingston-Canvas-Go-SD-car.patch @@ -0,0 +1,42 @@ +From 74f3ca5e39586ea26201fe6eaf1b9e6793b101b7 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Tue, 29 Oct 2024 13:33:21 +0000 +Subject: [PATCH] mmc: quirks: add more broken Kingston Canvas Go! SD card date + ranges + +A user has reported that a card of this model from late 2021 doesn't +work, so extend the date range and make it match on all card sizes. + +Signed-off-by: Jonathan Bell +--- + drivers/mmc/core/quirks.h | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -18,10 +18,22 @@ + static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { + /* + * Kingston Canvas Go! Plus microSD cards never finish SD cache flush. +- * This has so far only been observed on cards from 11/2019, while new +- * cards from 2023/05 do not exhibit this behavior. ++ * This has been observed on cards from 2019/11 and 2021/11, while new ++ * cards from 2023/05 and 2024/08 do not exhibit this behavior. + */ +- _FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11, ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2019, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2020, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2021, CID_MONTH_ANY, ++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), ++ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2022, CID_MONTH_ANY, + 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + diff --git a/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch b/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch new file mode 100644 index 000000000..eaa340f24 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1358-dt-bindings-usb-snps-dwc3-add-FS-HS-periodic-NAK-pol.patch @@ -0,0 +1,35 @@ +From 6c0f34fb0f83741f7f03f6bfd3fcbc89cb2c7cde Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:26:55 +0000 +Subject: [PATCH] dt-bindings: usb: snps,dwc3: add FS/HS periodic NAK polling + quirk + +Add two quirk properties that control whether or not the controller +issues many more handshakes to FS/HS Async endpoints in a single +(micro)frame. Enabling these can significantly increase throughput for +endpoints that frequently respond with NAKs. + +Signed-off-by: Jonathan Bell +--- + Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml ++++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +@@ -231,6 +231,16 @@ properties: + description: When set, disable u2mac linestate check during HS transmit + type: boolean + ++ snps,enhanced-nak-fs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async FS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ ++ snps,enhanced-nak-hs-quirk: ++ description: ++ When set, the controller schedules many more handshakes to Async HS ++ endpoints, improving throughput when they frequently respond with NAKs. ++ + snps,parkmode-disable-ss-quirk: + description: + When set, disable park mode for all Superspeed bus instances. diff --git a/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch b/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch new file mode 100644 index 000000000..db0cce8d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1359-usb-dwc3-core-add-support-for-setting-NAK-enhancemen.patch @@ -0,0 +1,77 @@ +From bb53ca75f9e3631e753f397ccab704a8f975658b Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:45:24 +0000 +Subject: [PATCH] usb: dwc3: core: add support for setting NAK enhancement bits + for FS/HS + +If a device frequently NAKs, it can exhaust the scheduled handshakes in +a frame. It will then not get polled by the controller until the next +frame interval. This is most noticeable on FS devices as the controller +schedules a small set of transactions only once per full-speed frame. + +Setting the ENH_PER_NAK_FS/LS bits in the GUCTL1 register increases the +number of transactions that can be scheduled to Async (Control/Bulk) +endpoints in the respective frame time. In the FS case, this only +applies to FS devices directly connected to root ports. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/dwc3/core.c | 10 ++++++++++ + drivers/usb/dwc3/core.h | 6 ++++++ + 2 files changed, 16 insertions(+) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1361,6 +1361,12 @@ static int dwc3_core_init(struct dwc3 *d + if (dwc->dis_tx_ipgap_linecheck_quirk) + reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS; + ++ if (dwc->enh_nak_fs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_FS; ++ ++ if (dwc->enh_nak_hs_quirk) ++ reg |= DWC3_GUCTL1_NAK_PER_ENH_HS; ++ + if (dwc->parkmode_disable_ss_quirk) + reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS; + +@@ -1633,6 +1639,10 @@ static void dwc3_get_properties(struct d + "snps,resume-hs-terminations"); + dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev, + "snps,ulpi-ext-vbus-drv"); ++ dwc->enh_nak_fs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-fs-quirk"); ++ dwc->enh_nak_hs_quirk = device_property_read_bool(dev, ++ "snps,enhanced-nak-hs-quirk"); + dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, + "snps,parkmode-disable-ss-quirk"); + dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev, +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -266,6 +266,8 @@ + #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) + #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26) + #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) ++#define DWC3_GUCTL1_NAK_PER_ENH_FS BIT(19) ++#define DWC3_GUCTL1_NAK_PER_ENH_HS BIT(18) + #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17) + #define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16) + #define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS BIT(15) +@@ -1119,6 +1121,8 @@ struct dwc3_scratchpad_array { + * generation after resume from suspend. + * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin + * VBUS with an external supply. ++ * @enh_nak_fs_quirk: Set to schedule more handshakes to Async FS endpoints. ++ * @enh_nak_hs_quirk: Set to schedule more handshakes to Async HS endpoints. + * @parkmode_disable_ss_quirk: If set, disable park mode feature for all + * Superspeed instances. + * @parkmode_disable_hs_quirk: If set, disable park mode feature for all +@@ -1348,6 +1352,8 @@ struct dwc3 { + unsigned dis_tx_ipgap_linecheck_quirk:1; + unsigned resume_hs_terminations:1; + unsigned ulpi_ext_vbus_drv:1; ++ unsigned enh_nak_fs_quirk:1; ++ unsigned enh_nak_hs_quirk:1; + unsigned parkmode_disable_ss_quirk:1; + unsigned parkmode_disable_hs_quirk:1; + unsigned parkmode_disable_fsls_quirk:1; diff --git a/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch b/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch new file mode 100644 index 000000000..a305dc13f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1360-DTS-rp1-set-enhanced-FS-NAK-quirk-for-usb3-controlle.patch @@ -0,0 +1,30 @@ +From 803757627b48bdad9530b50053321fdea6dfcab4 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 10:54:58 +0000 +Subject: [PATCH] DTS: rp1: set enhanced FS NAK quirk for usb3 controllers + +There seem to be only benefits, and no downsides. + +Signed-off-by: Jonathan Bell +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -1077,6 +1077,7 @@ + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; +@@ -1093,6 +1094,7 @@ + usb3-lpm-capable; + snps,axi-pipe-limit = /bits/ 8 <8>; + snps,dis_rxdet_inp3_quirk; ++ snps,enhanced-nak-fs-quirk; + snps,parkmode-disable-ss-quirk; + snps,parkmode-disable-hs-quirk; + snps,parkmode-disable-fsls-quirk; diff --git a/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch b/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch new file mode 100644 index 000000000..50819fc7f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1361-drivers-usb-xhci-prevent-a-theoretical-race-on-non-c.patch @@ -0,0 +1,50 @@ +From e9e852af347ae3ccee4e7abb01f9ef91387980f9 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 6 Nov 2024 11:07:55 +0000 +Subject: [PATCH] drivers: usb: xhci: prevent a theoretical race on + non-coherent platforms + +For platforms that have xHCI controllers attached over PCIe, and +non-coherent routes to main memory, a theoretical race exists between +posting new TRBs to a ring, and writing to the doorbell register. + +In a contended system, write traffic from the CPU may be stalled before +the memory controller, whereas the CPU to Endpoint route is separate +and not likely to be contended. Similarly, the DMA route from the +endpoint to main memory may be separate and uncontended. + +Therefore the xHCI can receive a doorbell write and find a stale view +of a transfer ring. In cases where only a single TRB is ping-ponged at +a time, this can cause the endpoint to not get polled at all. + +Adding a readl() before the write forces a round-trip transaction +across PCIe, definitively serialising the CPU along the PCI +producer-consumer ordering rules. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/host/xhci-ring.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -507,6 +507,19 @@ void xhci_ring_ep_doorbell(struct xhci_h + + trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); + ++ /* ++ * For non-coherent systems with PCIe DMA (such as Pi 4, Pi 5) there ++ * is a theoretical race between the TRB write and barrier, which ++ * is reported complete as soon as the write leaves the CPU domain, ++ * the doorbell write, which may be reported as complete by the RC ++ * at some arbitrary point, and the visibility of new TRBs in system ++ * RAM by the endpoint DMA engine. ++ * ++ * This read before the write positively serialises the CPU state ++ * by incurring a round-trip across the link. ++ */ ++ readl(db_addr); ++ + writel(DB_VALUE(ep_index, stream_id), db_addr); + /* flush the write */ + readl(db_addr); diff --git a/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch b/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch new file mode 100644 index 000000000..19935d0ea --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1362-iio-humidity-dht11-Allow-non-zero-decimals.patch @@ -0,0 +1,32 @@ +From ce65ed02cb6707ae5c9f3a304f5b0124f4eed559 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 4 Nov 2024 14:10:53 +0000 +Subject: [PATCH] iio: humidity: dht11: Allow non-zero decimals + +The DHT11 datasheet is pretty cryptic, but it does suggest that after +each integer value (humidity and temperature) there are "decimal" +values. Validate these as integers in the range 0-9 and treat them as +tenths of a unit. + +Link: https://github.com/raspberrypi/linux/issues/6220 + +Signed-off-by: Phil Elwell +--- + drivers/iio/humidity/dht11.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/iio/humidity/dht11.c ++++ b/drivers/iio/humidity/dht11.c +@@ -152,9 +152,9 @@ static int dht11_decode(struct dht11 *dh + dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * + ((temp_int & 0x80) ? -100 : 100); + dht11->humidity = ((hum_int << 8) + hum_dec) * 100; +- } else if (temp_dec == 0 && hum_dec == 0) { /* DHT11 */ +- dht11->temperature = temp_int * 1000; +- dht11->humidity = hum_int * 1000; ++ } else if (temp_dec < 10 && hum_dec < 10) { /* DHT11 */ ++ dht11->temperature = temp_int * 1000 + temp_dec * 100; ++ dht11->humidity = hum_int * 1000 + hum_dec * 100; + } else { + dev_err(dht11->dev, + "Don't know how to decode data: %d %d %d %d\n", diff --git a/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch b/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch new file mode 100644 index 000000000..9b018248a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1363-drm-vc4-Correct-condition-for-ignoring-a-plane-to-sr.patch @@ -0,0 +1,41 @@ +From c3393ac1098d1f191e37eed73bf366ebc88ac4ee Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Sep 2024 14:49:05 +0100 +Subject: [PATCH] drm/vc4: Correct condition for ignoring a plane to src rect + =0, not <1.0 + +The logic for dropping a plane less than zero didn't account for the +possibility that a plane could be being upscaled with a src_rect with +width/height < 1 pixel, but not 0 subpixels. + +Check for not 0 subpixels, not < 1, in both vc4 and vc6 paths. + +Fixes: dac616899f87 ("drm/vc4: Drop planes that have 0 destination size") +Fixes: f73b18eb0d48 ("drm/vc4: Drop planes that are completely off-screen") +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -1160,7 +1160,8 @@ static int vc4_plane_mode_set(struct drm + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + +- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen */ + vc4_state->dlist_initialized = 1; + return 0; +@@ -1698,7 +1699,8 @@ static int vc6_plane_mode_set(struct drm + width = vc4_state->src_w[0] >> 16; + height = vc4_state->src_h[0] >> 16; + +- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) { ++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] || ++ !vc4_state->crtc_w || !vc4_state->crtc_h) { + /* 0 source size probably means the plane is offscreen. + * 0 destination size is a redundant plane. + */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch b/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch new file mode 100644 index 000000000..5a7b3e4c1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1364-drm-vc4-Use-the-TPZ-scaling-filter-for-1x1-source-im.patch @@ -0,0 +1,59 @@ +From ca621585c573cae54dc1235d90822e8bcef2f73d Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Sep 2024 15:23:33 +0100 +Subject: [PATCH] drm/vc4: Use the TPZ scaling filter for 1x1 source images + +The documentation says that the TPZ filter can not upscale, +and requesting a scaling factor > 1:1 will output the original +image in the top left, and repeat the right/bottom most pixels +thereafter. +That fits perfectly with upscaling a 1x1 image which is done +a fair amount by some compositors to give solid colour, and it +saves a large amount of LBM (TPZ is based on src size, whilst +PPF is based on dest size). + +Select TPZ filter for images with source rectangle <=1. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -265,7 +265,11 @@ static enum vc4_scaling_mode vc4_get_sca + { + if (dst == src >> 16) + return VC4_SCALING_NONE; +- if (3 * dst >= 2 * (src >> 16)) ++ ++ if (src <= (1 << 16)) ++ /* Source rectangle <= 1 pixel can use TPZ for resize/upscale */ ++ return VC4_SCALING_TPZ; ++ else if (3 * dst >= 2 * (src >> 16)) + return VC4_SCALING_PPF; + else + return VC4_SCALING_TPZ; +@@ -560,12 +564,17 @@ static void vc4_write_tpz(struct vc4_pla + + WARN_ON_ONCE(vc4->gen > VC4_GEN_6); + +- scale = src / dst; ++ if ((dst << 16) < src) { ++ scale = src / dst; + +- /* The specs note that while the reciprocal would be defined +- * as (1<<32)/scale, ~0 is close enough. +- */ +- recip = ~0 / scale; ++ /* The specs note that while the reciprocal would be defined ++ * as (1<<32)/scale, ~0 is close enough. ++ */ ++ recip = ~0 / scale; ++ } else { ++ scale = (1 << 16) + 1; ++ recip = (1 << 16) - 1; ++ } + + vc4_dlist_write(vc4_state, + /* diff --git a/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch b/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch new file mode 100644 index 000000000..a1ac849e3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1365-drm-Set-non-desktop-property-to-true-for-writeback-a.patch @@ -0,0 +1,30 @@ +From 68b0ff3549148e614e1733d773cee8e689c763c6 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 20 Aug 2024 16:25:10 +0100 +Subject: [PATCH] drm: Set non-desktop property to true for writeback and + virtual connectors + +The non-desktop property "Indicates the output should be ignored for +purposes of displaying a standard desktop environment or console." + +That sounds like it should be true for all writeback and virtual +connectors as you shouldn't render a desktop to them, so set it +by default. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_connector.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/drm_connector.c ++++ b/drivers/gpu/drm/drm_connector.c +@@ -362,7 +362,8 @@ static int __drm_connector_init(struct d + + drm_object_attach_property(&connector->base, + config->non_desktop_property, +- 0); ++ (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && ++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1; + drm_object_attach_property(&connector->base, + config->tile_property, + 0); diff --git a/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch b/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch new file mode 100644 index 000000000..e27058d44 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1366-drm-Increase-plane_mask-to-64bit.patch @@ -0,0 +1,101 @@ +From 8181e682d6f4ef209845ec24f0a1eb37764d6731 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 21 Oct 2022 14:26:12 +0100 +Subject: [PATCH] drm: Increase plane_mask to 64bit. + +The limit of 32 planes per DRM device is dictated by the use +of planes_mask returning a u32. + +Change to a u64 such that 64 planes can be supported by a device. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic.c | 2 +- + drivers/gpu/drm/drm_framebuffer.c | 2 +- + drivers/gpu/drm/drm_mode_config.c | 2 +- + drivers/gpu/drm/drm_plane.c | 2 +- + drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c | 2 +- + include/drm/drm_crtc.h | 2 +- + include/drm/drm_plane.h | 4 ++-- + 7 files changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/drm_atomic.c ++++ b/drivers/gpu/drm/drm_atomic.c +@@ -451,7 +451,7 @@ static void drm_atomic_crtc_print_state( + drm_printf(p, "\tactive_changed=%d\n", state->active_changed); + drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed); + drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); +- drm_printf(p, "\tplane_mask=%x\n", state->plane_mask); ++ drm_printf(p, "\tplane_mask=%llx\n", state->plane_mask); + drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); + drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); + drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); +--- a/drivers/gpu/drm/drm_framebuffer.c ++++ b/drivers/gpu/drm/drm_framebuffer.c +@@ -959,7 +959,7 @@ static int atomic_remove_fb(struct drm_f + struct drm_connector *conn __maybe_unused; + struct drm_connector_state *conn_state; + int i, ret; +- unsigned plane_mask; ++ u64 plane_mask; + bool disable_crtcs = false; + + retry_disable: +--- a/drivers/gpu/drm/drm_mode_config.c ++++ b/drivers/gpu/drm/drm_mode_config.c +@@ -636,7 +636,7 @@ void drm_mode_config_validate(struct drm + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct drm_plane *plane; +- u32 primary_with_crtc = 0, cursor_with_crtc = 0; ++ u64 primary_with_crtc = 0, cursor_with_crtc = 0; + unsigned int num_primary = 0; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) +--- a/drivers/gpu/drm/drm_plane.c ++++ b/drivers/gpu/drm/drm_plane.c +@@ -249,7 +249,7 @@ static int __drm_universal_plane_init(st + int ret; + + /* plane index is used with 32bit bitmasks */ +- if (WARN_ON(config->num_total_plane >= 32)) ++ if (WARN_ON(config->num_total_plane >= 64)) + return -EINVAL; + + /* +--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c ++++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +@@ -230,7 +230,7 @@ static int ipu_crtc_atomic_check(struct + { + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); +- u32 primary_plane_mask = drm_plane_mask(crtc->primary); ++ u64 primary_plane_mask = drm_plane_mask(crtc->primary); + + if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0) + return -EINVAL; +--- a/include/drm/drm_crtc.h ++++ b/include/drm/drm_crtc.h +@@ -192,7 +192,7 @@ struct drm_crtc_state { + * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to + * this CRTC. + */ +- u32 plane_mask; ++ u64 plane_mask; + + /** + * @connector_mask: Bitmask of drm_connector_mask(connector) of +--- a/include/drm/drm_plane.h ++++ b/include/drm/drm_plane.h +@@ -915,9 +915,9 @@ static inline unsigned int drm_plane_ind + * drm_plane_mask - find the mask of a registered plane + * @plane: plane to find mask for + */ +-static inline u32 drm_plane_mask(const struct drm_plane *plane) ++static inline u64 drm_plane_mask(const struct drm_plane *plane) + { +- return 1 << drm_plane_index(plane); ++ return 1ULL << drm_plane_index(plane); + } + + struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx); diff --git a/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch b/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch new file mode 100644 index 000000000..564d7abeb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1367-drm-vc4-Increase-number-of-overlay-planes-from-16-to.patch @@ -0,0 +1,42 @@ +From 5dc4cef7d7fcda4ea59b9e456a835fa54336af6b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 21 Oct 2022 14:27:45 +0100 +Subject: [PATCH] drm/vc4: Increase number of overlay planes from 16 to 48 + +The HVS can accept an arbitrary number of planes, provided +that the overall pixel read load is within limits, and +the display list can fit into the dlist memory. + +Now that DRM will support 64 planes per device, increase +the number of overlay planes from 16 to 48 so that the +dlist complexity can be increased (eg 4x4 video wall on +each of 3 displays). + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_connector.c | 2 +- + drivers/gpu/drm/vc4/vc4_plane.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/drm_connector.c ++++ b/drivers/gpu/drm/drm_connector.c +@@ -363,7 +363,7 @@ static int __drm_connector_init(struct d + drm_object_attach_property(&connector->base, + config->non_desktop_property, + (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && +- connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1; ++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1); + drm_object_attach_property(&connector->base, + config->tile_property, + 0); +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -2517,7 +2517,7 @@ struct drm_plane *vc4_plane_init(struct + return plane; + } + +-#define VC4_NUM_OVERLAY_PLANES 16 ++#define VC4_NUM_OVERLAY_PLANES 48 + + int vc4_plane_create_additional_planes(struct drm_device *drm) + { diff --git a/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch b/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch new file mode 100644 index 000000000..40eff3cc1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1368-drm-vc4-Assign-32-overlay-planes-to-writeback-only.patch @@ -0,0 +1,71 @@ +From dd340cb082a020fbd42b794493ffd063dd8e15b4 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 15 Aug 2023 15:44:34 +0100 +Subject: [PATCH] drm/vc4: Assign 32 overlay planes to writeback only + +Instead of having 48 generic overlay planes, assign 32 to the +writeback connector so that there is no ambiguity in wlroots +when trying to find a plane for composition using the writeback +connector vs display. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_plane.c | 34 +++++++++++++++++++++++++++++++-- + 1 file changed, 32 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -2517,13 +2517,28 @@ struct drm_plane *vc4_plane_init(struct + return plane; + } + +-#define VC4_NUM_OVERLAY_PLANES 48 ++#define VC4_NUM_OVERLAY_PLANES 16 ++#define VC4_NUM_TXP_OVERLAY_PLANES 32 + + int vc4_plane_create_additional_planes(struct drm_device *drm) + { + struct drm_plane *cursor_plane; + struct drm_crtc *crtc; + unsigned int i; ++ struct drm_crtc *txp_crtc; ++ uint32_t non_txp_crtc_mask; ++ ++ drm_for_each_crtc(crtc, drm) { ++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); ++ ++ if (vc4_crtc->feeds_txp) { ++ txp_crtc = crtc; ++ break; ++ } ++ } ++ ++ non_txp_crtc_mask = GENMASK(drm->mode_config.num_crtc - 1, 0) - ++ drm_crtc_mask(txp_crtc); + + /* Set up some arbitrary number of planes. We're not limited + * by a set number of physical registers, just the space in +@@ -2537,7 +2552,22 @@ int vc4_plane_create_additional_planes(s + for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) { + struct drm_plane *plane = + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, +- GENMASK(drm->mode_config.num_crtc - 1, 0)); ++ non_txp_crtc_mask); ++ ++ if (IS_ERR(plane)) ++ continue; ++ ++ /* Create zpos property. Max of all the overlays + 1 primary + ++ * 1 cursor plane on a crtc. ++ */ ++ drm_plane_create_zpos_property(plane, i + 1, 1, ++ VC4_NUM_OVERLAY_PLANES + 1); ++ } ++ ++ for (i = 0; i < VC4_NUM_TXP_OVERLAY_PLANES; i++) { ++ struct drm_plane *plane = ++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, ++ drm_crtc_mask(txp_crtc)); + + if (IS_ERR(plane)) + continue; diff --git a/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch b/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch new file mode 100644 index 000000000..ccda8b05c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1369-drm-Add-a-DRM_MODE_TRANSPOSE-option-to-the-DRM-rotat.patch @@ -0,0 +1,47 @@ +From b3b3d12cf0734318a0fed0b33e13d714188369db Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 22 Oct 2024 17:17:31 +0100 +Subject: [PATCH] drm: Add a DRM_MODE_TRANSPOSE option to the DRM rotation + property + +Some hardware will implement transpose as a rotation operation, +which when combined with X and Y reflect can result in a rotation, +but is a discrete operation in its own right. + +Add an option for transpose only. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_blend.c | 3 +++ + include/uapi/drm/drm_mode.h | 1 + + 2 files changed, 4 insertions(+) + +--- a/drivers/gpu/drm/drm_blend.c ++++ b/drivers/gpu/drm/drm_blend.c +@@ -263,6 +263,8 @@ EXPORT_SYMBOL(drm_plane_create_alpha_pro + * "reflect-x" + * DRM_MODE_REFLECT_Y: + * "reflect-y" ++ * DRM_MODE_TRANSPOSE: ++ * "transpose" + * + * Rotation is the specified amount in degrees in counter clockwise direction, + * the X and Y axis are within the source rectangle, i.e. the X/Y axis before +@@ -280,6 +282,7 @@ int drm_plane_create_rotation_property(s + { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, + { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, + { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, ++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, + }; + struct drm_property *prop; + +--- a/include/uapi/drm/drm_mode.h ++++ b/include/uapi/drm/drm_mode.h +@@ -203,6 +203,7 @@ extern "C" { + */ + #define DRM_MODE_REFLECT_X (1<<4) + #define DRM_MODE_REFLECT_Y (1<<5) ++#define DRM_MODE_TRANSPOSE (1<<6) + + /* + * DRM_MODE_REFLECT_MASK diff --git a/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch b/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch new file mode 100644 index 000000000..e803a7732 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1370-drm-Add-a-rotation-parameter-to-connectors.patch @@ -0,0 +1,164 @@ +From 8fec3ff870499256f2c18fe7983f6ed3fea4faaf Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 22 Oct 2024 17:22:40 +0100 +Subject: [PATCH] drm: Add a rotation parameter to connectors. + +Some connectors, particularly writeback, can implement flip +or transpose operations as writing back to memory. + +Add a connector rotation property to control this. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic_uapi.c | 4 +++ + drivers/gpu/drm/drm_blend.c | 50 ++++++++++++++++++++++++------- + include/drm/drm_blend.h | 5 ++++ + include/drm/drm_connector.h | 11 +++++++ + 4 files changed, 60 insertions(+), 10 deletions(-) + +--- a/drivers/gpu/drm/drm_atomic_uapi.c ++++ b/drivers/gpu/drm/drm_atomic_uapi.c +@@ -811,6 +811,8 @@ static int drm_atomic_connector_set_prop + state->max_requested_bpc = val; + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; ++ } else if (property == connector->rotation_property) { ++ state->rotation = val; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, + state, property, val); +@@ -900,6 +902,8 @@ drm_atomic_connector_get_property(struct + *val = state->max_requested_bpc; + } else if (property == connector->privacy_screen_sw_state_property) { + *val = state->privacy_screen_sw_state; ++ } else if (property == connector->rotation_property) { ++ *val = state->rotation; + } else if (connector->funcs->atomic_get_property) { + return connector->funcs->atomic_get_property(connector, + state, property, val); +--- a/drivers/gpu/drm/drm_blend.c ++++ b/drivers/gpu/drm/drm_blend.c +@@ -235,6 +235,16 @@ int drm_plane_create_alpha_property(stru + } + EXPORT_SYMBOL(drm_plane_create_alpha_property); + ++static const struct drm_prop_enum_list drm_rotate_props[] = { ++ { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, ++ { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, ++ { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, ++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, ++}; ++ + /** + * drm_plane_create_rotation_property - create a new rotation property + * @plane: drm plane +@@ -275,15 +285,6 @@ int drm_plane_create_rotation_property(s + unsigned int rotation, + unsigned int supported_rotations) + { +- static const struct drm_prop_enum_list props[] = { +- { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" }, +- { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" }, +- { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" }, +- { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" }, +- { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" }, +- { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" }, +- { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" }, +- }; + struct drm_property *prop; + + WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); +@@ -291,7 +292,8 @@ int drm_plane_create_rotation_property(s + WARN_ON(rotation & ~supported_rotations); + + prop = drm_property_create_bitmask(plane->dev, 0, "rotation", +- props, ARRAY_SIZE(props), ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), + supported_rotations); + if (!prop) + return -ENOMEM; +@@ -307,6 +309,34 @@ int drm_plane_create_rotation_property(s + } + EXPORT_SYMBOL(drm_plane_create_rotation_property); + ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations) ++{ ++ struct drm_property *prop; ++ ++ WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0); ++ WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK)); ++ WARN_ON(rotation & ~supported_rotations); ++ ++ prop = drm_property_create_bitmask(conn->dev, 0, "rotation", ++ drm_rotate_props, ++ ARRAY_SIZE(drm_rotate_props), ++ supported_rotations); ++ if (!prop) ++ return -ENOMEM; ++ ++ drm_object_attach_property(&conn->base, prop, rotation); ++ ++ if (conn->state) ++ conn->state->rotation = rotation; ++ ++ conn->rotation_property = prop; ++ ++ return 0; ++} ++EXPORT_SYMBOL(drm_connector_create_rotation_property); ++ + /** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified +--- a/include/drm/drm_blend.h ++++ b/include/drm/drm_blend.h +@@ -34,6 +34,7 @@ + struct drm_device; + struct drm_atomic_state; + struct drm_plane; ++struct drm_connector; + + static inline bool drm_rotation_90_or_270(unsigned int rotation) + { +@@ -58,4 +59,8 @@ int drm_atomic_normalize_zpos(struct drm + struct drm_atomic_state *state); + int drm_plane_create_blend_mode_property(struct drm_plane *plane, + unsigned int supported_modes); ++ ++int drm_connector_create_rotation_property(struct drm_connector *conn, ++ unsigned int rotation, ++ unsigned int supported_rotations); + #endif +--- a/include/drm/drm_connector.h ++++ b/include/drm/drm_connector.h +@@ -1029,6 +1029,11 @@ struct drm_connector_state { + * DRM blob property for HDR output metadata + */ + struct drm_property_blob *hdr_output_metadata; ++ ++ /** ++ * @rotation: Connector property to rotate the maximum output image. ++ */ ++ u32 rotation; + }; + + /** +@@ -1696,6 +1701,12 @@ struct drm_connector { + */ + struct drm_property *privacy_screen_hw_state_property; + ++ /** ++ * @rotation_property: Optional DRM property controlling rotation of the ++ * output. ++ */ ++ struct drm_property *rotation_property; ++ + #define DRM_CONNECTOR_POLL_HPD (1 << 0) + #define DRM_CONNECTOR_POLL_CONNECT (1 << 1) + #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) diff --git a/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch b/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch new file mode 100644 index 000000000..b0e5566ac --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1371-drm-vc4-txp-Add-a-rotation-property-to-the-writeback.patch @@ -0,0 +1,65 @@ +From 8346446098032c62d1de891a97c7f62264b18f81 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 14 Aug 2024 16:41:07 +0100 +Subject: [PATCH] drm/vc4: txp: Add a rotation property to the writeback + connector + +The txp block can implement transpose as it writes out the image +data, so expose that through the new connector rotation property. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_txp.c | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_txp.c ++++ b/drivers/gpu/drm/vc4/vc4_txp.c +@@ -15,6 +15,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -259,10 +260,15 @@ static int vc4_txp_connector_atomic_chec + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + + fb = conn_state->writeback_job->fb; +- if (fb->width != crtc_state->mode.hdisplay || +- fb->height != crtc_state->mode.vdisplay) { +- DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", +- fb->width, fb->height); ++ if ((conn_state->rotation == DRM_MODE_ROTATE_0 && ++ fb->width != crtc_state->mode.hdisplay && ++ fb->height != crtc_state->mode.vdisplay) || ++ (conn_state->rotation == (DRM_MODE_ROTATE_0 | DRM_MODE_TRANSPOSE) && ++ fb->width != crtc_state->mode.vdisplay && ++ fb->height != crtc_state->mode.hdisplay)) { ++ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u vs mode %ux%u\n", ++ fb->width, fb->height, ++ crtc_state->mode.hdisplay, crtc_state->mode.vdisplay); + return -EINVAL; + } + +@@ -330,6 +336,9 @@ static void vc4_txp_connector_atomic_com + */ + ctrl |= TXP_ALPHA_INVERT; + ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE) ++ ctrl |= TXP_TRANSPOSE; ++ + if (!drm_dev_enter(drm, &idx)) + return; + +@@ -608,6 +617,10 @@ static int vc4_txp_bind(struct device *d + if (ret) + return ret; + ++ drm_connector_create_rotation_property(&txp->connector.base, DRM_MODE_ROTATE_0, ++ DRM_MODE_ROTATE_0 | ++ DRM_MODE_TRANSPOSE); ++ + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, + dev_name(dev), txp); + if (ret) diff --git a/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch b/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch new file mode 100644 index 000000000..90688668b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1372-dmaengine-dw-axi-dmac-Allow-client-chosen-width.patch @@ -0,0 +1,40 @@ +From a2fa911d90495762047c05dec4241308ae61ca36 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:05:00 +0100 +Subject: [PATCH] dmaengine: dw-axi-dmac: Allow client-chosen width + +For devices where transfer lengths are not known upfront, there is a +danger when the destination is wider than the source that partial words +can be lost at the end of a transfer. Ideally the controller would be +able to flush the residue, but it can't - it's not even possible to tell +that there is any. + +Instead, allow the client driver to avoid the problem by setting a +smaller width. + +Signed-off-by: Phil Elwell +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -724,6 +724,18 @@ static int dw_axi_dma_set_hw_desc(struct + case DMA_DEV_TO_MEM: + reg_burst_msize = axi_dma_encode_msize(chan->config.src_maxburst); + reg_width = __ffs(chan->config.src_addr_width); ++ /* ++ * For devices where transfer lengths are not known upfront, ++ * there is a danger when the destination is wider than the ++ * source that partial words can be lost at the end of a transfer. ++ * Ideally the controller would be able to flush the residue, but ++ * it can't - it's not even possible to tell that there is any. ++ * Instead, allow the client driver to avoid the problem by setting ++ * a smaller width. ++ */ ++ if (chan->config.dst_addr_width && ++ (chan->config.dst_addr_width < mem_width)) ++ mem_width = chan->config.dst_addr_width; + device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr); + ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS | + mem_width << CH_CTL_L_DST_WIDTH_POS | diff --git a/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch b/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch new file mode 100644 index 000000000..786af0403 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1373-spi-dw-Let-the-DMAC-set-the-transfer-widths.patch @@ -0,0 +1,31 @@ +From 5cf7209c294a58029984880d4858e2d3c7e46a3c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:12:12 +0100 +Subject: [PATCH] spi: dw: Let the DMAC set the transfer widths + +SPI transfers are of defined length, unlike some UART traffic, so it is +safe to let the DMA controller choose a suitable memory width. + +Signed-off-by: Phil Elwell +--- + drivers/spi/spi-dw-dma.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/spi/spi-dw-dma.c ++++ b/drivers/spi/spi-dw-dma.c +@@ -330,7 +330,6 @@ static int dw_spi_dma_config_tx(struct d + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = dws->txburst; +- txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + txconf.device_fc = false; + +@@ -431,7 +430,6 @@ static int dw_spi_dma_config_rx(struct d + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = dws->rxburst; +- rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + rxconf.device_fc = false; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch b/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch new file mode 100644 index 000000000..860d35b2f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1374-serial-pl011-Request-a-memory-width-of-1-byte.patch @@ -0,0 +1,25 @@ +From 8894298105f4cb41dfa41e0b0d3c40c3f7b92c44 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Sep 2024 18:22:24 +0100 +Subject: [PATCH] serial: pl011: Request a memory width of 1 byte + +In order to avoid losing residue bytes when a receive is terminated +early, set the destination width to single bytes. + +Link: https://github.com/raspberrypi/linux/issues/6365 + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/amba-pl011.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/tty/serial/amba-pl011.c ++++ b/drivers/tty/serial/amba-pl011.c +@@ -468,6 +468,7 @@ static void pl011_dma_probe(struct uart_ + .src_addr = uap->port.mapbase + + pl011_reg_to_offset(uap, REG_DR), + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, ++ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_DEV_TO_MEM, + .src_maxburst = uap->fifosize >> 2, + .device_fc = false, diff --git a/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch b/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch new file mode 100644 index 000000000..230537f50 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1377-drivers-usb-xhci-set-HID-bit-in-streaming-endpoint-c.patch @@ -0,0 +1,56 @@ +From 66aef6ce3557edd9d58d794e4a800c5be49ca0e7 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Mon, 11 Nov 2024 10:30:38 +0000 +Subject: [PATCH] drivers: usb: xhci: set HID bit in streaming endpoint + contexts + +The xHC may commence Host Initiated Data Moves for streaming endpoints - +see USB3.2 spec s8.12.1.4.2.4. However, this behaviour is typically +counterproductive as the submission of UAS URBs in {Status, Data, +Command} order and 1 outstanding IO per stream ID means the device never +enters Move Data after a HIMD for Status or Data stages with the same +stream ID. For OUT transfers this is especially inefficient as the host +will start transmitting multiple bulk packets as a burst, all of which +get NAKed by the device - wasting bandwidth. + +Also, some buggy UAS adapters don't properly handle the EP flow control +state this creates - e.g. RTL9210. + +Set Host Initiated Data Move Disable to always defer stream selection to +the device. xHC implementations may treat this field as "don't care, +forced to 1" anyway - xHCI 1.2 s4.12.1. + +Signed-off-by: Jonathan Bell +--- + drivers/usb/host/xhci-mem.c | 8 ++++++++ + drivers/usb/host/xhci.h | 2 ++ + 2 files changed, 10 insertions(+) + +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -716,6 +716,14 @@ void xhci_setup_streams_ep_input_ctx(str + ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); + ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) + | EP_HAS_LSA); ++ ++ /* ++ * Set Host Initiated Data Move Disable to always defer stream ++ * selection to the device. xHC implementations may treat this ++ * field as "don't care, forced to 1" anyway - xHCI 1.2 s4.12.1. ++ */ ++ ep_ctx->ep_info2 |= EP_HID; ++ + ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma); + } + +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -492,6 +492,8 @@ struct xhci_ep_ctx { + #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10) + /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ + #define EP_HAS_LSA (1 << 15) ++/* Host initiated data move disable in info2 */ ++#define EP_HID (1 << 7) + /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */ + #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch b/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch new file mode 100644 index 000000000..ee0605195 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1379-media-i2c-imx477-Add-options-for-slightly-modifying-.patch @@ -0,0 +1,199 @@ +From 35e50ee3d66e014d869f0d7a3468bef964d26d32 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 14 Nov 2024 13:14:02 +0000 +Subject: [PATCH] media: i2c: imx477: Add options for slightly modifying the + link freq + +The default link frequency of 450MHz has been noted to interfere +with GPS if they are in close proximty. +Add the option for 453 and 456MHz to move the signal slightly out +of the band. (447MHz can not be offered as corruption is then observed +on the 133x992 10bit mode). + +Signed-off-by: Dave Stevenson + +fixup imx477 gps +--- + drivers/media/i2c/imx477.c | 86 +++++++++++++++++++++++++++++--------- + 1 file changed, 67 insertions(+), 19 deletions(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -164,8 +164,48 @@ struct imx477_mode { + struct imx477_reg_list reg_list; + }; + +-static const s64 imx477_link_freq_menu[] = { +- IMX477_DEFAULT_LINK_FREQ, ++/* Link frequency setup */ ++enum { ++ IMX477_LINK_FREQ_450MHZ, ++ IMX477_LINK_FREQ_453MHZ, ++ IMX477_LINK_FREQ_456MHZ, ++}; ++ ++static const s64 link_freqs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = 450000000, ++ [IMX477_LINK_FREQ_453MHZ] = 453000000, ++ [IMX477_LINK_FREQ_456MHZ] = 456000000, ++}; ++ ++/* 450MHz is the nominal "default" link frequency */ ++static const struct imx477_reg link_450Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x96}, ++}; ++ ++static const struct imx477_reg link_453Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x97}, ++}; ++ ++static const struct imx477_reg link_456Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x98}, ++}; ++ ++static const struct imx477_reg_list link_freq_regs[] = { ++ [IMX477_LINK_FREQ_450MHZ] = { ++ .regs = link_450Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_450Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_453MHZ] = { ++ .regs = link_453Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_453Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_456MHZ] = { ++ .regs = link_456Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_456Mhz_regs) ++ }, + }; + + static const struct imx477_reg mode_common_regs[] = { +@@ -558,8 +598,6 @@ static const struct imx477_reg mode_4056 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -659,8 +697,6 @@ static const struct imx477_reg mode_2028 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -760,8 +796,6 @@ static const struct imx477_reg mode_2028 + {0x0309, 0x0c}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -890,8 +924,6 @@ static const struct imx477_reg mode_1332 + {0x0309, 0x0a}, + {0x030b, 0x02}, + {0x030d, 0x02}, +- {0x030e, 0x00}, +- {0x030f, 0x96}, + {0x0310, 0x01}, + {0x0820, 0x07}, + {0x0821, 0x08}, +@@ -1121,6 +1153,8 @@ struct imx477 { + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + ++ unsigned int link_freq_idx; ++ + /* Current mode */ + const struct imx477_mode *mode; + +@@ -1712,7 +1746,7 @@ static int imx477_get_selection(struct v + static int imx477_start_streaming(struct imx477 *imx477) + { + struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd); +- const struct imx477_reg_list *reg_list; ++ const struct imx477_reg_list *reg_list, *freq_regs; + const struct imx477_reg_list *extra_regs; + int ret, tm; + +@@ -1725,6 +1759,13 @@ static int imx477_start_streaming(struct + extra_regs->num_of_regs); + } + ++ if (!ret) { ++ /* Update the link frequency registers */ ++ freq_regs = &link_freq_regs[imx477->link_freq_idx]; ++ ret = imx477_write_regs(imx477, freq_regs->regs, ++ freq_regs->num_of_regs); ++ } ++ + if (ret) { + dev_err(&client->dev, "%s failed to set common settings\n", + __func__); +@@ -2010,9 +2051,8 @@ static int imx477_init_controls(struct i + /* LINK_FREQ is also read only */ + imx477->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, +- V4L2_CID_LINK_FREQ, +- ARRAY_SIZE(imx477_link_freq_menu) - 1, 0, +- imx477_link_freq_menu); ++ V4L2_CID_LINK_FREQ, 1, 0, ++ &link_freqs[imx477->link_freq_idx]); + if (imx477->link_freq) + imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + +@@ -2110,13 +2150,14 @@ static void imx477_free_controls(struct + mutex_destroy(&imx477->mutex); + } + +-static int imx477_check_hwcfg(struct device *dev) ++static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477) + { + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; ++ int i; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { +@@ -2141,11 +2182,18 @@ static int imx477_check_hwcfg(struct dev + goto error_out; + } + +- if (ep_cfg.nr_of_link_frequencies != 1 || +- ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) { ++ for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { ++ if (link_freqs[i] == ep_cfg.link_frequencies[0]) { ++ imx477->link_freq_idx = i; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(link_freqs)) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); +- goto error_out; ++ ret = -EINVAL; ++ goto error_out; + } + + ret = 0; +@@ -2206,7 +2254,7 @@ static int imx477_probe(struct i2c_clien + (const struct imx477_compatible_data *)match->data; + + /* Check the hardware configuration in device tree */ +- if (imx477_check_hwcfg(dev)) ++ if (imx477_check_hwcfg(dev, imx477)) + return -EINVAL; + + /* Default the trigger mode from OF to -1, which means invalid */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch b/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch new file mode 100644 index 000000000..47d3d3920 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1380-dtoverlays-Add-link-frequency-override-to-imx477-378.patch @@ -0,0 +1,43 @@ +From 7e253a062d5a14de13ccfb410570975099c238be Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 14 Nov 2024 13:15:24 +0000 +Subject: [PATCH] dtoverlays: Add link-frequency override to imx477/378 overlay + +Copy of the imx708 change. + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 4 ++++ + arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 1 + + 2 files changed, 5 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2780,6 +2780,8 @@ Params: rotation Mounting + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. + + + Name: imx462 +@@ -2822,6 +2824,8 @@ Params: rotation Mounting + camera clamping I/Os such as XVS to 0V. + sync-source Configure as vsync source + sync-sink Configure as vsync sink ++ link-frequency Allowable link frequency values to use in Hz: ++ 450000000 (default), 453000000, 456000000. + + + Name: imx500 +--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi ++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi +@@ -80,6 +80,7 @@ + <&cam_node>, "clocks:0=",<&cam0_clk>, + <&cam_node>, "VANA-supply:0=",<&cam0_reg>; + always-on = <0>, "+99"; ++ link-frequency = <&cam_endpoint>,"link-frequencies#0"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch b/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch new file mode 100644 index 000000000..81b7f805f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1381-dmaengine-dw-axi-dmac-Only-start-idle-channels.patch @@ -0,0 +1,29 @@ +From 59a8855b51c1d8acf37d3c80f34782d71f474617 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 13 Nov 2024 10:37:22 +0000 +Subject: [PATCH] dmaengine: dw-axi-dmac: Only start idle channels + +Attempting to start a non-idle channel causes an error message to be +logged, and is inefficient. Test for emptiness of the desc_issued list +before doing so. + +Signed-off-by: Phil Elwell +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -536,9 +536,11 @@ static void dma_chan_issue_pending(struc + { + struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan); + unsigned long flags; ++ bool was_empty; + + spin_lock_irqsave(&chan->vc.lock, flags); +- if (vchan_issue_pending(&chan->vc)) ++ was_empty = list_empty(&chan->vc.desc_issued); ++ if (vchan_issue_pending(&chan->vc) && was_empty) + axi_chan_start_first_queued(chan); + spin_unlock_irqrestore(&chan->vc.lock, flags); + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch b/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch new file mode 100644 index 000000000..23bdb6a8f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1382-mailbox-Add-RP1-mailbox-support.patch @@ -0,0 +1,252 @@ +From 0d58d8cfb6f989f290d983552fcaa116e582e84a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 17:33:38 +0000 +Subject: [PATCH] mailbox: Add RP1 mailbox support + +The Raspberry Pi RP1 includes 2 M3 cores running firmware. This driver +adds a mailbox communication channel to them via a doorbell and some +shared memory. + +Signed-off-by: Phil Elwell +--- + drivers/mailbox/Kconfig | 9 ++ + drivers/mailbox/Makefile | 2 + + drivers/mailbox/rp1-mailbox.c | 208 ++++++++++++++++++++++++++++++++++ + 3 files changed, 219 insertions(+) + create mode 100644 drivers/mailbox/rp1-mailbox.c + +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,13 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config MBOX_RP1 ++ tristate "RP1 Mailbox" ++ depends on MFD_RP1 ++ help ++ An implementation of a mailbox interface to the Raspberry Pi RP1 I/O ++ interface. Although written as a mailbox driver, the hardware only ++ provides an array of 32 doorbells. ++ Say Y here if you want to use the RP1 Mailbox. ++ + endif +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_MBOX_RP1) += rp1-mailbox.o +--- /dev/null ++++ b/drivers/mailbox/rp1-mailbox.c +@@ -0,0 +1,208 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - bcm2835-mailbox.c ++ * Copyright (C) 2010,2015 Broadcom ++ * Copyright (C) 2013-2014 Lubomir Rintel ++ * Copyright (C) 2013 Craig McGeachie ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * RP1's PROC_EVENTS register can generate interrupts on the M3 cores (when ++ * enabled). The 32-bit register is treated as 32 events, all of which share a ++ * common interrupt. HOST_EVENTS is the same in the reverse direction. ++ */ ++#define SYSCFG_PROC_EVENTS 0x00000008 ++#define SYSCFG_HOST_EVENTS 0x0000000c ++#define SYSCFG_HOST_EVENT_IRQ_EN 0x00000010 ++#define SYSCFG_HOST_EVENT_IRQ 0x00000014 ++ ++#define HW_SET_BITS 0x00002000 ++#define HW_CLR_BITS 0x00003000 ++ ++#define MAX_CHANS 4 /* 32 is the hardware limit */ ++ ++struct rp1_mbox { ++ void __iomem *regs; ++ unsigned int irq; ++ struct mbox_controller controller; ++}; ++ ++static struct rp1_mbox *rp1_chan_mbox(struct mbox_chan *chan) ++{ ++ return container_of(chan->mbox, struct rp1_mbox, controller); ++} ++ ++static unsigned int rp1_chan_event(struct mbox_chan *chan) ++{ ++ return (unsigned int)(uintptr_t)chan->con_priv; ++} ++ ++static irqreturn_t rp1_mbox_irq(int irq, void *dev_id) ++{ ++ struct rp1_mbox *mbox = dev_id; ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ writel(evs, mbox->regs + SYSCFG_HOST_EVENTS + HW_CLR_BITS); ++ ++ while (evs) { ++ doorbell = __ffs(evs); ++ chan = &mbox->controller.chans[doorbell]; ++ mbox_chan_received_data(chan, NULL); ++ evs &= ~(1 << doorbell); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int rp1_send_data(struct mbox_chan *chan, void *data) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_PROC_EVENTS + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static int rp1_startup(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_SET_BITS); ++ ++ return 0; ++} ++ ++static void rp1_shutdown(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ ++ writel(event, mbox->regs + SYSCFG_HOST_EVENT_IRQ_EN + HW_CLR_BITS); ++} ++ ++static bool rp1_last_tx_done(struct mbox_chan *chan) ++{ ++ struct rp1_mbox *mbox = rp1_chan_mbox(chan); ++ unsigned int event = rp1_chan_event(chan); ++ unsigned int evs; ++ ++ evs = readl(mbox->regs + SYSCFG_HOST_EVENT_IRQ); ++ ++ return !(evs & event); ++} ++ ++static const struct mbox_chan_ops rp1_mbox_chan_ops = { ++ .send_data = rp1_send_data, ++ .startup = rp1_startup, ++ .shutdown = rp1_shutdown, ++ .last_tx_done = rp1_last_tx_done ++}; ++ ++static struct mbox_chan *rp1_mbox_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *spec) ++{ ++ struct mbox_chan *chan; ++ unsigned int doorbell; ++ ++ if (spec->args_count != 1) ++ return ERR_PTR(-EINVAL); ++ ++ doorbell = spec->args[0]; ++ if (doorbell >= MAX_CHANS) ++ return ERR_PTR(-EINVAL); ++ ++ chan = &mbox->chans[doorbell]; ++ if (chan->con_priv) ++ return ERR_PTR(-EBUSY); ++ ++ chan->con_priv = (void *)(uintptr_t)(1 << doorbell); ++ ++ return chan; ++} ++ ++static int rp1_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mbox_chan *chans; ++ struct rp1_mbox *mbox; ++ int ret = 0; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (mbox == NULL) ++ return -ENOMEM; ++ ++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0), ++ rp1_mbox_irq, 0, dev_name(dev), mbox); ++ if (ret) { ++ dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", ++ ret); ++ return -ENODEV; ++ } ++ ++ mbox->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mbox->regs)) { ++ ret = PTR_ERR(mbox->regs); ++ return ret; ++ } ++ ++ chans = devm_kcalloc(dev, MAX_CHANS, sizeof(*chans), GFP_KERNEL); ++ if (!chans) ++ return -ENOMEM; ++ ++ mbox->controller.txdone_poll = true; ++ mbox->controller.txpoll_period = 5; ++ mbox->controller.ops = &rp1_mbox_chan_ops; ++ mbox->controller.of_xlate = &rp1_mbox_xlate; ++ mbox->controller.dev = dev; ++ mbox->controller.num_chans = MAX_CHANS; ++ mbox->controller.chans = chans; ++ ++ ret = devm_mbox_controller_register(dev, &mbox->controller); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, mbox); ++ ++ return 0; ++} ++ ++static const struct of_device_id rp1_mbox_of_match[] = { ++ { .compatible = "raspberrypi,rp1-mbox", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_mbox_of_match); ++ ++static struct platform_driver rp1_mbox_driver = { ++ .driver = { ++ .name = "rp1-mbox", ++ .of_match_table = rp1_mbox_of_match, ++ }, ++ .probe = rp1_mbox_probe, ++}; ++ ++module_platform_driver(rp1_mbox_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 mailbox IPC driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch b/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch new file mode 100644 index 000000000..3a9eb03c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1383-firmware-Add-an-RP1-firmware-interface-over-mbox.patch @@ -0,0 +1,421 @@ +From 67daeadcaa7cee1f4b9df7aa108d199e73f35451 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 17:36:54 +0000 +Subject: [PATCH] firmware: Add an RP1 firmware interface over mbox + +The RP1 firmware runs a simple communications channel over some shared +memory and a mailbox. This driver provides access to that channel. + +Signed-off-by: Phil Elwell +--- + drivers/firmware/Kconfig | 9 + + drivers/firmware/Makefile | 1 + + drivers/firmware/rp1.c | 316 +++++++++++++++++++++++++++++++++++ + include/linux/rp1-firmware.h | 53 ++++++ + 4 files changed, 379 insertions(+) + create mode 100644 drivers/firmware/rp1.c + create mode 100644 include/linux/rp1-firmware.h + +--- a/drivers/firmware/Kconfig ++++ b/drivers/firmware/Kconfig +@@ -153,6 +153,15 @@ config RASPBERRYPI_FIRMWARE + This option enables support for communicating with the firmware on the + Raspberry Pi. + ++config FIRMWARE_RP1 ++ tristate "RP1 Firmware Driver" ++ depends on MBOX_RP1 ++ help ++ The Raspberry Pi RP1 processor presents a firmware ++ interface using shared memory and a mailbox. To enable ++ the driver that communicates with it, say Y. Otherwise, ++ say N. ++ + config FW_CFG_SYSFS + tristate "QEMU fw_cfg device support in sysfs" + depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || SPARC || X86) +--- a/drivers/firmware/Makefile ++++ b/drivers/firmware/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o + obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o + obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o + obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o ++obj-$(CONFIG_FIRMWARE_RP1) += rp1.o + obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o + obj-$(CONFIG_QCOM_SCM) += qcom-scm.o + qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o +--- /dev/null ++++ b/drivers/firmware/rp1.c +@@ -0,0 +1,316 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023-24 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - raspberrypi.c, by Eric Anholt ++ * Copyright (C) 2015 Broadcom ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RP1_MAILBOX_FIRMWARE 0 ++ ++enum rp1_firmware_ops { ++ MBOX_SUCCESS = 0x0000, ++ GET_FIRMWARE_VERSION = 0x0001, // na -> 160-bit version ++ GET_FEATURE = 0x0002, // FOURCC -> op base (0 == unsupported), op count ++ ++ COMMON_COUNT ++}; ++ ++struct rp1_firmware { ++ struct mbox_client cl; ++ struct mbox_chan *chan; /* The doorbell channel */ ++ uint32_t __iomem *buf; /* The shared buffer */ ++ u32 buf_size; /* The size of the shared buffer */ ++ struct completion c; ++ ++ struct kref consumers; ++}; ++ ++struct rp1_get_feature_resp { ++ uint32_t op_base; ++ uint32_t op_count; ++}; ++ ++static DEFINE_MUTEX(transaction_lock); ++ ++static const struct of_device_id rp1_firmware_of_match[] = { ++ { .compatible = "raspberrypi,rp1-firmware", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rp1_firmware_of_match); ++ ++static void response_callback(struct mbox_client *cl, void *msg) ++{ ++ struct rp1_firmware *fw = container_of(cl, struct rp1_firmware, cl); ++ ++ complete(&fw->c); ++} ++ ++/* ++ * Sends a request to the RP1 firmware and synchronously waits for the reply. ++ * Returns zero or a positive count of response bytes on success, negative on ++ * error. ++ */ ++ ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ int ret; ++ u32 rc; ++ ++ if (data_len + 4 > fw->buf_size) ++ return -EINVAL; ++ ++ mutex_lock(&transaction_lock); ++ ++ memcpy_toio(&fw->buf[1], data, data_len); ++ writel((op << 16) | data_len, fw->buf); ++ ++ reinit_completion(&fw->c); ++ ret = mbox_send_message(fw->chan, NULL); ++ if (ret >= 0) { ++ if (wait_for_completion_timeout(&fw->c, HZ)) ++ ret = 0; ++ else ++ ret = -ETIMEDOUT; ++ } else { ++ dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); ++ } ++ ++ if (ret == 0) { ++ rc = readl(fw->buf); ++ if (rc & 0x80000000) { ++ ret = (int32_t)rc; ++ } else { ++ ret = min(rc, resp_space); ++ memcpy_fromio(resp, &fw->buf[1], ret); ++ } ++ } ++ ++ mutex_unlock(&transaction_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_message); ++ ++static void rp1_firmware_delete(struct kref *kref) ++{ ++ struct rp1_firmware *fw = container_of(kref, struct rp1_firmware, consumers); ++ ++ mbox_free_channel(fw->chan); ++ kfree(fw); ++} ++ ++void rp1_firmware_put(struct rp1_firmware *fw) ++{ ++ kref_put(&fw->consumers, rp1_firmware_delete); ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_put); ++ ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ struct rp1_get_feature_resp resp; ++ int ret; ++ ++ memset(&resp, 0, sizeof(resp)); ++ ret = rp1_firmware_message(fw, GET_FEATURE, ++ &fourcc, sizeof(fourcc), ++ &resp, sizeof(resp)); ++ *op_base = resp.op_base; ++ *op_count = resp.op_count; ++ if (ret < 0) ++ return ret; ++ if (ret < sizeof(resp) || !resp.op_base) ++ return -EOPNOTSUPP; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get_feature); ++ ++static void devm_rp1_firmware_put(void *data) ++{ ++ struct rp1_firmware *fw = data; ++ ++ rp1_firmware_put(fw); ++} ++ ++/** ++ * rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * ++ * The reference to rp1_firmware has to be released with rp1_firmware_put(). ++ * ++ * Returns an error pointer on failure. ++ */ ++struct rp1_firmware *rp1_firmware_get(struct device_node *client) ++{ ++ const char *match = rp1_firmware_of_match[0].compatible; ++ struct platform_device *pdev; ++ struct device_node *fwnode; ++ struct rp1_firmware *fw; ++ ++ if (client) { ++ fwnode = of_parse_phandle(client, "firmware", 0); ++ if (!fwnode) ++ fwnode = of_get_parent(client); ++ if (fwnode && !of_device_is_compatible(fwnode, match)) { ++ of_node_put(fwnode); ++ fwnode = NULL; ++ } ++ } ++ ++ if (!fwnode) ++ fwnode = of_find_matching_node(NULL, rp1_firmware_of_match); ++ ++ if (!fwnode) ++ return ERR_PTR(-ENOENT); ++ ++ pdev = of_find_device_by_node(fwnode); ++ of_node_put(fwnode); ++ ++ if (!pdev) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ fw = platform_get_drvdata(pdev); ++ if (!fw) ++ goto err_defer; ++ ++ if (!kref_get_unless_zero(&fw->consumers)) ++ goto err_defer; ++ ++ put_device(&pdev->dev); ++ ++ return fw; ++ ++err_defer: ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++} ++EXPORT_SYMBOL_GPL(rp1_firmware_get); ++ ++/** ++ * devm_rp1_firmware_get - Get pointer to rp1_firmware structure. ++ * @firmware_node: Pointer to the firmware Device Tree node. ++ * ++ * Returns NULL is the firmware device is not ready. ++ */ ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *client) ++{ ++ struct rp1_firmware *fw; ++ int ret; ++ ++ fw = rp1_firmware_get(client); ++ if (IS_ERR(fw)) ++ return fw; ++ ++ ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ return fw; ++} ++EXPORT_SYMBOL_GPL(devm_rp1_firmware_get); ++ ++static int rp1_firmware_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *shmem; ++ struct rp1_firmware *fw; ++ struct resource res; ++ uint32_t version[5]; ++ int ret; ++ ++ shmem = of_parse_phandle(dev->of_node, "shmem", 0); ++ if (!of_device_is_compatible(shmem, "raspberrypi,rp1-shmem")) { ++ of_node_put(shmem); ++ return -ENXIO; ++ } ++ ++ ret = of_address_to_resource(shmem, 0, &res); ++ of_node_put(shmem); ++ if (ret) { ++ dev_err(dev, "failed to get shared memory (%pOF) - %d\n", shmem, ret); ++ return ret; ++ } ++ ++ /* ++ * Memory will be freed by rp1_firmware_delete() once all users have ++ * released their firmware handles. Don't use devm_kzalloc() here. ++ */ ++ fw = kzalloc(sizeof(*fw), GFP_KERNEL); ++ if (!fw) ++ return -ENOMEM; ++ ++ fw->buf_size = resource_size(&res); ++ fw->buf = devm_ioremap(dev, res.start, fw->buf_size); ++ if (!fw->buf) { ++ dev_err(dev, "failed to ioremap shared memory\n"); ++ kfree(fw); ++ return -EADDRNOTAVAIL; ++ } ++ ++ fw->cl.dev = dev; ++ fw->cl.rx_callback = response_callback; ++ fw->cl.tx_block = false; ++ ++ fw->chan = mbox_request_channel(&fw->cl, RP1_MAILBOX_FIRMWARE); ++ if (IS_ERR(fw->chan)) { ++ int ret = PTR_ERR(fw->chan); ++ ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "Failed to get mbox channel: %d\n", ret); ++ kfree(fw); ++ return ret; ++ } ++ ++ init_completion(&fw->c); ++ kref_init(&fw->consumers); ++ ++ platform_set_drvdata(pdev, fw); ++ ++ ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION, ++ NULL, 0, &version, sizeof(version)); ++ if (ret == sizeof(version)) { ++ dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n", ++ version[0], version[1], version[2], version[3], version[4]); ++ ret = 0; ++ } else if (ret >= 0) { ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int rp1_firmware_remove(struct platform_device *pdev) ++{ ++ struct rp1_firmware *fw = platform_get_drvdata(pdev); ++ ++ rp1_firmware_put(fw); ++ ++ return 0; ++} ++ ++static struct platform_driver rp1_firmware_driver = { ++ .driver = { ++ .name = "rp1-firmware", ++ .of_match_table = rp1_firmware_of_match, ++ }, ++ .probe = rp1_firmware_probe, ++ .remove = rp1_firmware_remove, ++}; ++ ++module_platform_driver(rp1_firmware_driver); ++ ++MODULE_AUTHOR("Phil Elwell "); ++MODULE_DESCRIPTION("RP1 firmware driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/rp1-firmware.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_H__ ++#define __SOC_RP1_FIRMWARE_H__ ++ ++#include ++#include ++ ++#define RP1_FOURCC(s) ((uint32_t)((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0))) ++ ++struct rp1_firmware; ++ ++#if IS_ENABLED(CONFIG_FIRMWARE_RP1) ++int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space); ++void rp1_firmware_put(struct rp1_firmware *fw); ++struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode); ++struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *fwnode); ++int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count); ++#else ++static inline int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op, ++ const void *data, unsigned int data_len, ++ void *resp, unsigned int resp_space) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline void rp1_firmware_put(struct rp1_firmware *fw) { } ++ ++static inline struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, ++ struct device_node *fwnode) ++{ ++ return NULL; ++} ++ ++static inline int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc, ++ uint32_t *op_base, uint32_t *op_count) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif /* __SOC_RP1_FIRMWARE_H__ */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch new file mode 100644 index 000000000..521129b91 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1384-misc-Add-RP1-PIO-driver.patch @@ -0,0 +1,1385 @@ +From 55fd5c9018e1520d45f08cf08630a493ec7dedea Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 31 Oct 2024 18:26:00 +0000 +Subject: [PATCH] misc: Add RP1 PIO driver + +Provide remote access to the PIO hardware in RP1. There is a single +instance, with 4 state machines. + +Signed-off-by: Phil Elwell +--- + drivers/misc/Kconfig | 8 + + drivers/misc/Makefile | 1 + + drivers/misc/rp1-fw-pio.h | 53 ++ + drivers/misc/rp1-pio.c | 1064 ++++++++++++++++++++++++++++++++ + include/uapi/misc/rp1_pio_if.h | 212 +++++++ + 5 files changed, 1338 insertions(+) + create mode 100644 drivers/misc/rp1-fw-pio.h + create mode 100644 drivers/misc/rp1-pio.c + create mode 100644 include/uapi/misc/rp1_pio_if.h + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -17,6 +17,14 @@ config BCM2835_SMI + Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. + Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h + ++config RP1_PIO ++ tristate "Raspberry Pi RP1 PIO driver" ++ select FIRMWARE_RP1 ++ default n ++ help ++ Driver providing control of the Raspberry Pi PIO block, as found in ++ RP1. ++ + config AD525X_DPOT + tristate "Analog Devices Digital Potentiometers" + depends on (I2C || SPI) && SYSFS +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7 + obj-$(CONFIG_PHANTOM) += phantom.o + obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o + obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o ++obj-$(CONFIG_RP1_PIO) += rp1-pio.o + obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o + obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o + obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o +--- /dev/null ++++ b/drivers/misc/rp1-fw-pio.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2023 2023-2024 Raspberry Pi Ltd. ++ */ ++ ++#ifndef __SOC_RP1_FIRMWARE_OPS_H__ ++#define __SOC_RP1_FIRMWARE_OPS_H__ ++ ++#include ++ ++#define FOURCC_PIO RP1_FOURCC("PIO ") ++ ++enum rp1_pio_ops { ++ PIO_CAN_ADD_PROGRAM, // u16 num_instrs, u16 origin -> origin ++ PIO_ADD_PROGRAM, // u16 num_instrs, u16 origin, u16 prog[] -> rc ++ PIO_REMOVE_PROGRAM, // u16 num_instrs, u16 origin ++ PIO_CLEAR_INSTR_MEM, // - ++ ++ PIO_SM_CLAIM, // u16 mask -> sm ++ PIO_SM_UNCLAIM, // u16 mask ++ PIO_SM_IS_CLAIMED, // u16 mask -> claimed ++ ++ PIO_SM_INIT, // u16 sm, u16 initial_pc, u32 sm_config[4] ++ PIO_SM_SET_CONFIG, // u16 sm, u16 rsvd, u32 sm_config[4] ++ PIO_SM_EXEC, // u16 sm, u16 instr, u8 blocking, u8 rsvd ++ PIO_SM_CLEAR_FIFOS, // u16 sm ++ PIO_SM_SET_CLKDIV, // u16 sm, u16 div_int, u8 div_frac, u8 rsvd ++ PIO_SM_SET_PINS, // u16 sm, u16 rsvd, u32 values, u32 mask ++ PIO_SM_SET_PINDIRS, // u16 sm, u16 rsvd, u32 dirs, u32 mask ++ PIO_SM_SET_ENABLED, // u16 mask, u8 enable, u8 rsvd ++ PIO_SM_RESTART, // u16 mask ++ PIO_SM_CLKDIV_RESTART, // u16 mask ++ PIO_SM_ENABLE_SYNC, // u16 mask ++ PIO_SM_PUT, // u16 sm, u8 blocking, u8 rsvd, u32 data ++ PIO_SM_GET, // u16 sm, u8 blocking, u8 rsvd -> u32 data ++ PIO_SM_SET_DMACTRL, // u16 sm, u16 is_tx, u32 ctrl ++ ++ GPIO_INIT, // u16 gpio ++ GPIO_SET_FUNCTION, // u16 gpio, u16 fn ++ GPIO_SET_PULLS, // u16 gpio, u8 up, u8 down ++ GPIO_SET_OUTOVER, // u16 gpio, u16 value ++ GPIO_SET_INOVER, // u16 gpio, u16 value ++ GPIO_SET_OEOVER, // u16 gpio, u16 value ++ GPIO_SET_INPUT_ENABLED, // u16 gpio, u16 value ++ GPIO_SET_DRIVE_STRENGTH, // u16 gpio, u16 value ++ ++ READ_HW, // src address, len -> data bytes ++ WRITE_HW, // dst address, data ++ ++ PIO_COUNT ++}; ++ ++#endif +--- /dev/null ++++ b/drivers/misc/rp1-pio.c +@@ -0,0 +1,1064 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// PIO driver for RP1 ++// ++// Copyright (C) 2023-2024 Raspberry Pi Ltd. ++// ++// Parts of this driver are based on: ++// - vcio.c, by Noralf Trønnes ++// Copyright (C) 2010 Broadcom ++// Copyright (C) 2015 Noralf Trønnes ++// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. ++// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren ++// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rp1-fw-pio.h" ++ ++#define DRIVER_NAME "rp1-pio" ++ ++#define RP1_PIO_SMS_COUNT 4 ++#define RP1_PIO_INSTR_COUNT 32 ++ ++#define MAX_ARG_SIZE 256 ++ ++#define RP1_PIO_FIFO_TX0 0x00 ++#define RP1_PIO_FIFO_TX1 0x04 ++#define RP1_PIO_FIFO_TX2 0x08 ++#define RP1_PIO_FIFO_TX3 0x0c ++#define RP1_PIO_FIFO_RX0 0x10 ++#define RP1_PIO_FIFO_RX1 0x14 ++#define RP1_PIO_FIFO_RX2 0x18 ++#define RP1_PIO_FIFO_RX3 0x1c ++ ++#define RP1_PIO_DMACTRL_DEFAULT 0x80000104 ++ ++#define HANDLER(_n, _f) \ ++ [_IOC_NR(PIO_IOC_ ## _n)] = { #_n, rp1_pio_ ## _f, _IOC_SIZE(PIO_IOC_ ## _n) } ++ ++ ++#define ROUND_UP(x, y) (((x) + (y) - 1) - (((x) + (y) - 1) % (y))) ++ ++#define DMA_BOUNCE_BUFFER_SIZE 0x1000 ++#define DMA_BOUNCE_BUFFER_COUNT 4 ++ ++struct dma_buf_info { ++ void *buf; ++ dma_addr_t phys; ++ struct scatterlist sgl; ++}; ++ ++struct dma_info { ++ struct semaphore buf_sem; ++ struct dma_chan *chan; ++ size_t buf_size; ++ size_t buf_count; ++ unsigned int head_idx; ++ unsigned int tail_idx; ++ struct dma_buf_info bufs[DMA_BOUNCE_BUFFER_COUNT]; ++}; ++ ++struct rp1_pio_device { ++ struct platform_device *pdev; ++ struct rp1_firmware *fw; ++ uint16_t fw_pio_base; ++ uint16_t fw_pio_count; ++ dev_t dev_num; ++ struct class *dev_class; ++ struct cdev cdev; ++ phys_addr_t phys_addr; ++ uint32_t claimed_sms; ++ uint32_t claimed_dmas; ++ spinlock_t lock; ++ struct mutex instr_mutex; ++ struct dma_info dma_configs[RP1_PIO_SMS_COUNT][RP1_PIO_DIR_COUNT]; ++ uint32_t used_instrs; ++ uint8_t instr_refcounts[RP1_PIO_INSTR_COUNT]; ++ uint16_t instrs[RP1_PIO_INSTR_COUNT]; ++ uint client_count; ++}; ++ ++struct rp1_pio_client { ++ struct rp1_pio_device *pio; ++ uint32_t claimed_sms; ++ uint32_t claimed_instrs; ++ uint32_t claimed_dmas; ++}; ++ ++static struct rp1_pio_device *g_pio; ++ ++static int rp1_pio_message(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len) ++{ ++ uint32_t rc; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ &rc, sizeof(rc)); ++ if (ret == 4) ++ ret = rc; ++ return ret; ++} ++ ++static int rp1_pio_message_resp(struct rp1_pio_device *pio, ++ uint16_t op, const void *data, unsigned int data_len, ++ void *resp, void __user *userbuf, unsigned int resp_len) ++{ ++ uint32_t resp_buf[1 + 32]; ++ int ret; ++ ++ if (op >= pio->fw_pio_count) ++ return -EOPNOTSUPP; ++ if (resp_len + 4 >= sizeof(resp_buf)) ++ return -EINVAL; ++ if (!resp && !userbuf) ++ return -EINVAL; ++ ret = rp1_firmware_message(pio->fw, pio->fw_pio_base + op, ++ data, data_len, ++ resp_buf, resp_len + 4); ++ if (ret >= 4 && !resp_buf[0]) { ++ ret -= 4; ++ if (resp) ++ memcpy(resp, &resp_buf[1], ret); ++ else if (copy_to_user(userbuf, &resp_buf[1], ret)) ++ ret = -EFAULT; ++ } else if (ret >= 0) { ++ ret = -EIO; ++ } ++ return ret; ++} ++ ++static int rp1_pio_read_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ ++ return rp1_pio_message_resp(pio, READ_HW, ++ args, 8, NULL, args->data, args->len); ++} ++ ++static int rp1_pio_write_hw(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct rp1_access_hw_args *args = param; ++ uint32_t write_buf[32 + 1]; ++ int len; ++ ++ len = min(args->len, sizeof(write_buf) - 4); ++ write_buf[0] = args->addr; ++ if (copy_from_user(&write_buf[1], args->data, len)) ++ return -EFAULT; ++ return rp1_firmware_message(pio->fw, pio->fw_pio_base + WRITE_HW, ++ write_buf, 4 + len, NULL, 0); ++} ++ ++static int rp1_pio_find_program(struct rp1_pio_device *pio, ++ struct rp1_pio_add_program_args *prog) ++{ ++ uint start, end, prog_size; ++ uint32_t used_mask; ++ uint i; ++ ++ start = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : 0; ++ end = (prog->origin != RP1_PIO_ORIGIN_ANY) ? prog->origin : ++ (RP1_PIO_INSTRUCTION_COUNT - prog->num_instrs); ++ prog_size = sizeof(prog->instrs[0]) * prog->num_instrs; ++ used_mask = (uint32_t)(~0) >> (32 - prog->num_instrs); ++ ++ /* Find the best match */ ++ for (i = start; i <= end; i++) { ++ uint32_t mask = used_mask << i; ++ ++ if ((pio->used_instrs & mask) != mask) ++ continue; ++ if (!memcmp(pio->instrs + i, prog->instrs, prog_size)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ mutex_unlock(&pio->instr_mutex); ++ if (offset >= 0) ++ return offset; ++ ++ /* Don't send the instructions, just the header */ ++ return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, ++ offsetof(struct rp1_pio_add_program_args, instrs)); ++} ++ ++static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_add_program_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int offset; ++ uint i; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ ((args->origin != RP1_PIO_ORIGIN_ANY) && ++ (args->origin >= RP1_PIO_INSTR_COUNT || ++ ((args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT)))) ++ return -EINVAL; ++ ++ mutex_lock(&pio->instr_mutex); ++ offset = rp1_pio_find_program(pio, args); ++ if (offset < 0) ++ offset = rp1_pio_message(client->pio, PIO_ADD_PROGRAM, args, sizeof(*args)); ++ ++ if (offset >= 0) { ++ uint32_t used_mask; ++ uint prog_size; ++ ++ used_mask = ((uint32_t)(~0) >> (-args->num_instrs & 0x1f)) << offset; ++ prog_size = sizeof(args->instrs[0]) * args->num_instrs; ++ ++ if ((pio->used_instrs & used_mask) != used_mask) { ++ pio->used_instrs |= used_mask; ++ memcpy(pio->instrs + offset, args->instrs, prog_size); ++ } ++ client->claimed_instrs |= used_mask; ++ for (i = 0; i < args->num_instrs; i++) ++ pio->instr_refcounts[offset + i]++; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return offset; ++} ++ ++static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) ++{ ++ struct rp1_pio_remove_program_args args; ++ uint i; ++ ++ mutex_lock(&pio->instr_mutex); ++ args.num_instrs = 0; ++ for (i = 0; ; i++, mask >>= 1) { ++ if ((mask & 1) && pio->instr_refcounts[i] && !--pio->instr_refcounts[i]) { ++ pio->used_instrs &= ~(1 << i); ++ args.num_instrs++; ++ } else if (args.num_instrs) { ++ args.origin = i - args.num_instrs; ++ rp1_pio_message(pio, PIO_REMOVE_PROGRAM, &args, sizeof(args)); ++ args.num_instrs = 0; ++ } ++ if (!mask) ++ break; ++ } ++ mutex_unlock(&pio->instr_mutex); ++} ++ ++static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_remove_program_args *args = param; ++ uint32_t used_mask; ++ int ret = -ENOENT; ++ ++ if (args->num_instrs > RP1_PIO_INSTR_COUNT || ++ args->origin >= RP1_PIO_INSTR_COUNT || ++ (args->origin + args->num_instrs) > RP1_PIO_INSTR_COUNT) ++ return -EINVAL; ++ ++ used_mask = ((uint32_t)(~0) >> (32 - args->num_instrs)) << args->origin; ++ if ((client->claimed_instrs & used_mask) == used_mask) { ++ client->claimed_instrs &= ~used_mask; ++ rp1_pio_remove_instrs(client->pio, used_mask); ++ ret = 0; ++ } ++ return ret; ++} ++ ++static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_CLEAR_INSTR_MEM, NULL, 0); ++ memset(pio->instr_refcounts, 0, sizeof(pio->instr_refcounts)); ++ pio->used_instrs = 0; ++ client->claimed_instrs = 0; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++ ++static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ int ret; ++ ++ mutex_lock(&pio->instr_mutex); ++ ret = rp1_pio_message(client->pio, PIO_SM_CLAIM, args, sizeof(*args)); ++ if (ret >= 0) { ++ if (args->mask) ++ client->claimed_sms |= args->mask; ++ else ++ client->claimed_sms |= (1 << ret); ++ pio->claimed_sms |= client->claimed_sms; ++ } ++ mutex_unlock(&pio->instr_mutex); ++ return ret; ++} ++ ++static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ ++ mutex_lock(&pio->instr_mutex); ++ (void)rp1_pio_message(client->pio, PIO_SM_UNCLAIM, args, sizeof(*args)); ++ client->claimed_sms &= ~args->mask; ++ pio->claimed_sms &= ~args->mask; ++ mutex_unlock(&pio->instr_mutex); ++ return 0; ++} ++ ++static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_claim_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_config_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_exec_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_clkdiv_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pins_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_pindirs_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_enabled_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_restart_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_enable_sync_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_put_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_get_args *args = param; ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_GET, args, sizeof(*args), ++ &args->data, NULL, sizeof(args->data)); ++ if (ret >= 0) ++ return offsetof(struct rp1_pio_sm_get_args, data) + ret; ++ return ret; ++} ++ ++static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_set_dmactrl_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_init_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_function_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_pulls_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); ++} ++ ++static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_gpio_set_args *args = param; ++ ++ return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); ++} ++ ++static void rp1_pio_sm_dma_callback(void *param) ++{ ++ struct dma_info *dma = param; ++ ++ up(&dma->buf_sem); ++} ++ ++static void rp1_pio_sm_dma_free(struct device *dev, struct dma_info *dma) ++{ ++ dmaengine_terminate_all(dma->chan); ++ while (dma->buf_count > 0) { ++ dma->buf_count--; ++ dma_free_coherent(dev, ROUND_UP(dma->buf_size, PAGE_SIZE), ++ dma->bufs[dma->buf_count].buf, dma->bufs[dma->buf_count].phys); ++ } ++ ++ dma_release_channel(dma->chan); ++} ++ ++static int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer_args *args = param; ++ struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; ++ struct rp1_pio_device *pio = client->pio; ++ struct platform_device *pdev = pio->pdev; ++ struct device *dev = &pdev->dev; ++ struct dma_slave_config config = {}; ++ phys_addr_t fifo_addr; ++ struct dma_info *dma; ++ uint32_t dma_mask; ++ char chan_name[4]; ++ uint buf_size; ++ int ret = 0; ++ ++ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || ++ !args->buf_size || (args->buf_size & 3) || ++ !args->buf_count || args->buf_count > DMA_BOUNCE_BUFFER_COUNT) ++ return -EINVAL; ++ ++ dma_mask = 1 << (args->sm * 2 + args->dir); ++ ++ dma = &pio->dma_configs[args->sm][args->dir]; ++ ++ spin_lock(&pio->lock); ++ if (pio->claimed_dmas & dma_mask) ++ rp1_pio_sm_dma_free(dev, dma); ++ pio->claimed_dmas |= dma_mask; ++ client->claimed_dmas |= dma_mask; ++ spin_unlock(&pio->lock); ++ ++ dma->buf_size = args->buf_size; ++ /* Round up the allocations */ ++ buf_size = ROUND_UP(args->buf_size, PAGE_SIZE); ++ sema_init(&dma->buf_sem, 0); ++ ++ /* Allocate and configure a DMA channel */ ++ /* Careful - each SM FIFO has its own DREQ value */ ++ chan_name[0] = (args->dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; ++ chan_name[1] = 'x'; ++ chan_name[2] = '0' + args->sm; ++ chan_name[3] = '\0'; ++ ++ dma->chan = dma_request_chan(dev, chan_name); ++ if (IS_ERR(dma->chan)) ++ return PTR_ERR(dma->chan); ++ ++ /* Alloc and map bounce buffers */ ++ for (dma->buf_count = 0; dma->buf_count < args->buf_count; dma->buf_count++) { ++ struct dma_buf_info *dbi = &dma->bufs[dma->buf_count]; ++ ++ dbi->buf = dma_alloc_coherent(dma->chan->device->dev, buf_size, ++ &dbi->phys, GFP_KERNEL); ++ if (!dbi->buf) { ++ ret = -ENOMEM; ++ goto err_dma_free; ++ } ++ sg_init_table(&dbi->sgl, 1); ++ sg_dma_address(&dbi->sgl) = dbi->phys; ++ } ++ ++ fifo_addr = pio->phys_addr; ++ fifo_addr += args->sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); ++ fifo_addr += (args->dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; ++ ++ config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ config.src_addr = fifo_addr; ++ config.dst_addr = fifo_addr; ++ config.direction = (args->dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; ++ ++ ret = dmaengine_slave_config(dma->chan, &config); ++ if (ret) ++ goto err_dma_free; ++ ++ set_dmactrl_args.sm = args->sm; ++ set_dmactrl_args.is_tx = (args->dir == RP1_PIO_DIR_TO_SM); ++ set_dmactrl_args.ctrl = RP1_PIO_DMACTRL_DEFAULT; ++ if (args->dir == RP1_PIO_DIR_FROM_SM) ++ set_dmactrl_args.ctrl = (RP1_PIO_DMACTRL_DEFAULT & ~0x1f) | 1; ++ ++ ret = rp1_pio_sm_set_dmactrl(client, &set_dmactrl_args); ++ if (ret) ++ goto err_dma_free; ++ ++ return 0; ++ ++err_dma_free: ++ rp1_pio_sm_dma_free(dev, dma); ++ ++ spin_lock(&pio->lock); ++ client->claimed_dmas &= ~dma_mask; ++ pio->claimed_dmas &= ~dma_mask; ++ spin_unlock(&pio->lock); ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ const void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ // Clean the slate - we're running synchronously ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes > 0) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* grab the next free buffer, waiting if they're all full */ ++ if (dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA bounce timed out\n"); ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ ++ ret = copy_from_user(dbi->buf, userbuf, copy_bytes); ++ if (ret < 0) ++ break; ++ ++ userbuf += copy_bytes; ++ ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_MEM_TO_DEV, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failedzn"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ /* Submit the buffer - the callback will kick the semaphore */ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ret = 0; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ // Block for completion ++ while (dma->tail_idx != dma->head_idx) { ++ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ dma->tail_idx++; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_rx_user(struct rp1_pio_device *pio, struct dma_info *dma, ++ void __user *userbuf, size_t bytes) ++{ ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ /* Clean the slate - we're running synchronously */ ++ dma->head_idx = 0; ++ dma->tail_idx = 0; ++ ++ while (bytes || dma->tail_idx != dma->head_idx) { ++ size_t copy_bytes = min(bytes, dma->buf_size); ++ struct dma_buf_info *dbi; ++ ++ /* ++ * wait for the next RX to complete if all the buffers are ++ * outstanding or we're finishing up. ++ */ ++ if (!bytes || dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, ++ msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out"); ++ ret = -ETIMEDOUT; ++ break; ++ } ++ ++ dbi = &dma->bufs[dma->tail_idx++ % dma->buf_count]; ++ ret = copy_to_user(userbuf, dbi->buf, sg_dma_len(&dbi->sgl)); ++ if (ret < 0) ++ break; ++ userbuf += sg_dma_len(&dbi->sgl); ++ ++ if (!bytes) ++ continue; ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ sg_dma_len(&dbi->sgl) = copy_bytes; ++ desc = dmaengine_prep_slave_sg(dma->chan, &dbi->sgl, 1, ++ DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed"); ++ ret = -EIO; ++ break; ++ } ++ ++ desc->callback = rp1_pio_sm_dma_callback; ++ desc->callback_param = dma; ++ ++ // Submit the buffer - the callback will kick the semaphore ++ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) ++ break; ++ ++ dma_async_issue_pending(dma->chan); ++ ++ dma->head_idx++; ++ bytes -= copy_bytes; ++ } ++ ++ return ret; ++} ++ ++static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_xfer_data_args *args = param; ++ struct rp1_pio_device *pio = client->pio; ++ struct dma_info *dma; ++ ++ if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || ++ !args->data_bytes || !args->data) ++ return -EINVAL; ++ ++ dma = &pio->dma_configs[args->sm][args->dir]; ++ ++ if (args->dir == RP1_PIO_DIR_TO_SM) ++ return rp1_pio_sm_tx_user(pio, dma, args->data, args->data_bytes); ++ else ++ return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); ++} ++ ++struct handler_info { ++ const char *name; ++ int (*func)(struct rp1_pio_client *client, void *param); ++ int argsize; ++} ioctl_handlers[] = { ++ HANDLER(SM_CONFIG_XFER, sm_config_xfer), ++ HANDLER(SM_XFER_DATA, sm_xfer_data), ++ ++ HANDLER(CAN_ADD_PROGRAM, can_add_program), ++ HANDLER(ADD_PROGRAM, add_program), ++ HANDLER(REMOVE_PROGRAM, remove_program), ++ HANDLER(CLEAR_INSTR_MEM, clear_instr_mem), ++ ++ HANDLER(SM_CLAIM, sm_claim), ++ HANDLER(SM_UNCLAIM, sm_unclaim), ++ HANDLER(SM_IS_CLAIMED, sm_is_claimed), ++ ++ HANDLER(SM_INIT, sm_init), ++ HANDLER(SM_SET_CONFIG, sm_set_config), ++ HANDLER(SM_EXEC, sm_exec), ++ HANDLER(SM_CLEAR_FIFOS, sm_clear_fifos), ++ HANDLER(SM_SET_CLKDIV, sm_set_clkdiv), ++ HANDLER(SM_SET_PINS, sm_set_pins), ++ HANDLER(SM_SET_PINDIRS, sm_set_pindirs), ++ HANDLER(SM_SET_ENABLED, sm_set_enabled), ++ HANDLER(SM_RESTART, sm_restart), ++ HANDLER(SM_CLKDIV_RESTART, sm_clkdiv_restart), ++ HANDLER(SM_ENABLE_SYNC, sm_enable_sync), ++ HANDLER(SM_PUT, sm_put), ++ HANDLER(SM_GET, sm_get), ++ HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), ++ ++ HANDLER(GPIO_INIT, gpio_init), ++ HANDLER(GPIO_SET_FUNCTION, gpio_set_function), ++ HANDLER(GPIO_SET_PULLS, gpio_set_pulls), ++ HANDLER(GPIO_SET_OUTOVER, gpio_set_outover), ++ HANDLER(GPIO_SET_INOVER, gpio_set_inover), ++ HANDLER(GPIO_SET_OEOVER, gpio_set_oeover), ++ HANDLER(GPIO_SET_INPUT_ENABLED, gpio_set_input_enabled), ++ HANDLER(GPIO_SET_DRIVE_STRENGTH, gpio_set_drive_strength), ++ ++ HANDLER(READ_HW, read_hw), ++ HANDLER(WRITE_HW, write_hw), ++}; ++ ++static int rp1_pio_open(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_device *pio = g_pio; ++ struct rp1_pio_client *client; ++ ++ client = kzalloc(sizeof(*client), GFP_KERNEL); ++ ++ client->pio = pio; ++ filp->private_data = client; ++ ++ return 0; ++} ++ ++static int rp1_pio_release(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ struct rp1_pio_device *pio = client->pio; ++ uint claimed_dmas = client->claimed_dmas; ++ int i; ++ ++ /* Free any allocated resources */ ++ ++ for (i = 0; claimed_dmas; i++) { ++ uint mask = (1 << i); ++ ++ if (claimed_dmas & mask) { ++ struct dma_info *dma = &pio->dma_configs[i >> 1][i & 1]; ++ ++ claimed_dmas &= ~mask; ++ rp1_pio_sm_dma_free(&pio->pdev->dev, dma); ++ } ++ } ++ ++ spin_lock(&pio->lock); ++ pio->claimed_dmas &= ~client->claimed_dmas; ++ spin_unlock(&pio->lock); ++ ++ if (client->claimed_sms) { ++ struct rp1_pio_sm_set_enabled_args se_args = { ++ .mask = client->claimed_sms, .enable = 0 ++ }; ++ struct rp1_pio_sm_claim_args uc_args = { ++ .mask = client->claimed_sms ++ }; ++ ++ rp1_pio_sm_set_enabled(client, &se_args); ++ rp1_pio_sm_unclaim(client, &uc_args); ++ } ++ ++ if (client->claimed_instrs) ++ rp1_pio_remove_instrs(pio, client->claimed_instrs); ++ ++ /* Reinitialise the SM? */ ++ ++ kfree(client); ++ ++ return 0; ++} ++ ++static long rp1_pio_ioctl(struct file *filp, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ struct device *dev = &client->pio->pdev->dev; ++ void __user *argp = (void __user *)ioctl_param; ++ int nr = _IOC_NR(ioctl_num); ++ int sz = _IOC_SIZE(ioctl_num); ++ struct handler_info *hdlr = &ioctl_handlers[nr]; ++ uint32_t argbuf[MAX_ARG_SIZE/sizeof(uint32_t)]; ++ int ret; ++ ++ if (nr >= ARRAY_SIZE(ioctl_handlers) || !hdlr->func) { ++ dev_err(dev, "unknown ioctl: %x\n", ioctl_num); ++ return -EOPNOTSUPP; ++ } ++ ++ if (sz != hdlr->argsize) { ++ dev_err(dev, "wrong %s argsize (expected %d, got %d)\n", ++ hdlr->name, hdlr->argsize, sz); ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(argbuf, argp, sz)) ++ return -EFAULT; ++ ++ ret = (hdlr->func)(client, argbuf); ++ dev_dbg(dev, "%s: %s -> %d\n", __func__, hdlr->name, ret); ++ if (ret > 0) { ++ if (copy_to_user(argp, argbuf, ret)) ++ ret = -EFAULT; ++ } ++ ++ return ret; ++} ++ ++const struct file_operations rp1_pio_fops = { ++ .owner = THIS_MODULE, ++ .open = rp1_pio_open, ++ .release = rp1_pio_release, ++ .unlocked_ioctl = rp1_pio_ioctl, ++}; ++ ++static int rp1_pio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *ioresource; ++ struct rp1_pio_device *pio; ++ struct rp1_firmware *fw; ++ uint32_t op_count = 0; ++ uint32_t op_base = 0; ++ struct device *cdev; ++ char dev_name[16]; ++ void *p; ++ int ret; ++ int i; ++ ++ /* Run-time check for a build-time misconfiguration */ ++ for (i = 0; i < ARRAY_SIZE(ioctl_handlers); i++) { ++ struct handler_info *hdlr = &ioctl_handlers[i]; ++ ++ if (WARN_ON(hdlr->argsize > MAX_ARG_SIZE)) ++ return -EINVAL; ++ } ++ ++ fw = devm_rp1_firmware_get(dev, dev->of_node); ++ if (IS_ERR(fw)) ++ return PTR_ERR(fw); ++ ++ ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); ++ if (ret < 0) ++ return ret; ++ ++ pio = devm_kzalloc(&pdev->dev, sizeof(*pio), GFP_KERNEL); ++ if (!pio) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pio); ++ pio->fw_pio_base = op_base; ++ pio->fw_pio_count = op_count; ++ pio->pdev = pdev; ++ pio->fw = fw; ++ spin_lock_init(&pio->lock); ++ mutex_init(&pio->instr_mutex); ++ ++ p = devm_platform_get_and_ioremap_resource(pdev, 0, &ioresource); ++ if (IS_ERR(p)) ++ return PTR_ERR(p); ++ ++ pio->phys_addr = ioresource->start; ++ ++ ret = alloc_chrdev_region(&pio->dev_num, 0, 1, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); ++ goto out_err; ++ } ++ ++ cdev_init(&pio->cdev, &rp1_pio_fops); ++ ret = cdev_add(&pio->cdev, pio->dev_num, 1); ++ if (ret) { ++ dev_err(dev, "cdev_add failed (err %d)\n", ret); ++ goto out_unregister; ++ } ++ ++ pio->dev_class = class_create(DRIVER_NAME); ++ if (IS_ERR(pio->dev_class)) { ++ ret = PTR_ERR(pio->dev_class); ++ dev_err(dev, "class_create failed (err %d)\n", ret); ++ goto out_cdev_del; ++ } ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); ++ if (pdev->id < 0) { ++ dev_err(dev, "alias is missing\n"); ++ return -EINVAL; ++ goto out_class_destroy; ++ } ++ sprintf(dev_name, "pio%d", pdev->id); ++ cdev = device_create(pio->dev_class, NULL, pio->dev_num, NULL, dev_name); ++ if (IS_ERR(cdev)) { ++ ret = PTR_ERR(cdev); ++ dev_err(dev, "%s: device_create failed (err %d)\n", __func__, ret); ++ goto out_class_destroy; ++ } ++ ++ g_pio = pio; ++ ++ dev_info(dev, "Created instance as %s\n", dev_name); ++ return 0; ++ ++out_class_destroy: ++ class_destroy(pio->dev_class); ++ ++out_cdev_del: ++ cdev_del(&pio->cdev); ++ ++out_unregister: ++ unregister_chrdev_region(pio->dev_num, 1); ++ ++out_err: ++ return ret; ++} ++ ++static void rp1_pio_remove(struct platform_device *pdev) ++{ ++ struct rp1_pio_device *pio = platform_get_drvdata(pdev); ++ ++ /* There should be no clients */ ++ ++ if (g_pio == pio) ++ g_pio = NULL; ++} ++ ++static const struct of_device_id rp1_pio_ids[] = { ++ { .compatible = "raspberrypi,rp1-pio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rp1_pio_ids); ++ ++static struct platform_driver rp1_pio_driver = { ++ .driver = { ++ .name = "rp1-pio", ++ .of_match_table = of_match_ptr(rp1_pio_ids), ++ }, ++ .probe = rp1_pio_probe, ++ .remove_new = rp1_pio_remove, ++ .shutdown = rp1_pio_remove, ++}; ++ ++module_platform_driver(rp1_pio_driver); ++ ++MODULE_DESCRIPTION("PIO controller driver for Raspberry Pi RP1"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -0,0 +1,212 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2023-24 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++#ifndef _PIO_RP1_IF_H ++#define _PIO_RP1_IF_H ++ ++#include ++ ++#define RP1_PIO_INSTRUCTION_COUNT 32 ++#define RP1_PIO_SM_COUNT 4 ++#define RP1_PIO_GPIO_COUNT 28 ++#define RP1_GPIO_FUNC_PIO 7 ++ ++#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) ++ ++#define RP1_PIO_DIR_TO_SM 0 ++#define RP1_PIO_DIR_FROM_SM 1 ++#define RP1_PIO_DIR_COUNT 2 ++ ++typedef struct { ++ uint32_t clkdiv; ++ uint32_t execctrl; ++ uint32_t shiftctrl; ++ uint32_t pinctrl; ++} rp1_pio_sm_config; ++ ++struct rp1_pio_add_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++ uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; ++}; ++ ++struct rp1_pio_remove_program_args { ++ uint16_t num_instrs; ++ uint16_t origin; ++}; ++ ++struct rp1_pio_sm_claim_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_init_args { ++ uint16_t sm; ++ uint16_t initial_pc; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_set_config_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ rp1_pio_sm_config config; ++}; ++ ++struct rp1_pio_sm_exec_args { ++ uint16_t sm; ++ uint16_t instr; ++ uint8_t blocking; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_clear_fifos_args { ++ uint16_t sm; ++}; ++ ++struct rp1_pio_sm_set_clkdiv_args { ++ uint16_t sm; ++ uint16_t div_int; ++ uint8_t div_frac; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_set_pins_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t values; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_pindirs_args { ++ uint16_t sm; ++ uint16_t rsvd; ++ uint32_t dirs; ++ uint32_t mask; ++}; ++ ++struct rp1_pio_sm_set_enabled_args { ++ uint16_t mask; ++ uint8_t enable; ++ uint8_t rsvd; ++}; ++ ++struct rp1_pio_sm_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_clkdiv_restart_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_enable_sync_args { ++ uint16_t mask; ++}; ++ ++struct rp1_pio_sm_put_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; ++}; ++ ++struct rp1_pio_sm_get_args { ++ uint16_t sm; ++ uint8_t blocking; ++ uint8_t rsvd; ++ uint32_t data; /* IN/OUT */ ++}; ++ ++struct rp1_pio_sm_set_dmactrl_args { ++ uint16_t sm; ++ uint8_t is_tx; ++ uint8_t rsvd; ++ uint32_t ctrl; ++}; ++ ++struct rp1_gpio_init_args { ++ uint16_t gpio; ++}; ++ ++struct rp1_gpio_set_function_args { ++ uint16_t gpio; ++ uint16_t fn; ++}; ++ ++struct rp1_gpio_set_pulls_args { ++ uint16_t gpio; ++ uint8_t up; ++ uint8_t down; ++}; ++ ++struct rp1_gpio_set_args { ++ uint16_t gpio; ++ uint16_t value; ++}; ++ ++struct rp1_pio_sm_config_xfer_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t buf_size; ++ uint16_t buf_count; ++}; ++ ++struct rp1_pio_sm_xfer_data_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t data_bytes; ++ void *data; ++}; ++ ++struct rp1_access_hw_args { ++ uint32_t addr; ++ uint32_t len; ++ void *data; ++}; ++ ++#define PIO_IOC_MAGIC 102 ++ ++#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) ++#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) ++ ++#ifdef CONFIG_COMPAT ++//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) ++#endif ++ ++#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) ++#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) ++ ++#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) ++#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) ++#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) ++#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) ++ ++#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) ++#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) ++ ++#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) ++#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) ++#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) ++#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) ++#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) ++#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) ++#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) ++#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) ++#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) ++#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) ++#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) ++#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) ++#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) ++ ++#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) ++#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) ++#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) ++#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) ++#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) ++ ++#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch b/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch new file mode 100644 index 000000000..2afb809e5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1386-dts-bcm2712-rpi-Add-RP1-firmware-and-mailboxes.patch @@ -0,0 +1,118 @@ +From 0b76dec8dfba8c1a4793dff0c86bf73d088a812e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 1 Nov 2024 09:12:01 +0000 +Subject: [PATCH] dts: bcm2712-rpi: Add RP1 firmware and mailboxes + +Declare the communications channel to RP1. + +Signed-off-by: Phil Elwell +--- + .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 4 +-- + .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 4 +-- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 4 +++ + arch/arm64/boot/dts/broadcom/rp1.dtsi | 27 +++++++++++++++++++ + 4 files changed, 35 insertions(+), 4 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +@@ -195,7 +195,7 @@ i2c_rp1boot: &_i2c3 { }; + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 +- 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx +@@ -207,7 +207,7 @@ i2c_rp1boot: &_i2c3 { }; + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 +- 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -176,7 +176,7 @@ i2c_rp1boot: &_i2c3 { }; + // This is the RP1 peripheral space + ranges = <0xc0 0x40000000 + 0x02000000 0x00 0x00000000 +- 0x00 0x00400000>; ++ 0x00 0x00410000>; + + dma-ranges = + // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx +@@ -188,7 +188,7 @@ i2c_rp1boot: &_i2c3 { }; + // This allows the RP1 DMA controller to address RP1 hardware + <0xc0 0x40000000 + 0x02000000 0x0 0x00000000 +- 0x0 0x00400000>, ++ 0x0 0x00410000>, + + // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx + <0x00 0x00000000 +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -294,6 +294,10 @@ pciex4: &pcie2 { }; + status = "okay"; + }; + ++&rp1_mbox { ++ status = "okay"; ++}; ++ + /* Add some gpiomem nodes to make the devices accessible to userspace. + * /dev/gpiomem should expose the registers for the interface with DT alias + * gpio. +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -13,6 +13,14 @@ + + // ranges and dma-ranges must be provided by the includer + ++ rp1_mbox: mailbox@8000 { ++ compatible = "raspberrypi,rp1-mbox"; ++ status = "disabled"; ++ reg = <0xc0 0x40008000 0x0 0x4000>; // SYSCFG ++ interrupts = ; ++ #mbox-cells = <1>; ++ }; ++ + rp1_clocks: clocks@18000 { + compatible = "raspberrypi,rp1-clocks"; + #clock-cells = <1>; +@@ -1183,6 +1191,19 @@ + assigned-clocks = <&rp1_clocks RP1_CLK_DPI>; + assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>; + }; ++ ++ sram: sram@400000 { ++ compatible = "mmio-sram"; ++ reg = <0xc0 0x40400000 0x0 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0xc0 0x40400000 0x10000>; ++ ++ rp1_fw_shmem: shmem@ff00 { ++ compatible = "raspberrypi,rp1-shmem"; ++ reg = <0xff00 0x100>; // firmware mailbox buffer ++ }; ++ }; + }; + }; + +@@ -1281,6 +1302,12 @@ + }; + + / { ++ rp1_firmware: rp1_firmware { ++ compatible = "raspberrypi,rp1-firmware", "simple-mfd"; ++ mboxes = <&rp1_mbox 0>; ++ shmem = <&rp1_fw_shmem>; ++ }; ++ + rp1_vdd_3v3: rp1_vdd_3v3 { + compatible = "regulator-fixed"; + regulator-name = "vdd-3v3"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch b/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch new file mode 100644 index 000000000..29e54277c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1387-dts-bcm2712-rpi-Add-the-RP1-PIO-device.patch @@ -0,0 +1,55 @@ +From 3e3c1b9922b22d362a4a9133361597ac80b974bb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 1 Nov 2024 09:13:53 +0000 +Subject: [PATCH] dts: bcm2712-rpi: Add the RP1 PIO device + +Declare the device that proxies RP1's PIO hardware. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 5 +++++ + arch/arm64/boot/dts/broadcom/rp1.dtsi | 12 ++++++++++++ + 2 files changed, 17 insertions(+) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +@@ -97,6 +97,10 @@ + }; + }; + ++pio: &rp1_pio { ++ status = "okay"; ++}; ++ + / { + chosen: chosen { + bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave"; +@@ -129,6 +133,7 @@ + i2c12 = &i2c_rp1boot; + mailbox = &mailbox; + mmc0 = &sdio1; ++ pio0 = &pio; + serial0 = &uart0; + serial1 = &uart1; + serial10 = &uart10; +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -1028,6 +1028,18 @@ + status = "disabled"; + }; + ++ rp1_pio: pio@178000 { ++ reg = <0xc0 0x40178000 0x0 0x20>; ++ compatible = "raspberrypi,rp1-pio"; ++ firmware = <&rp1_firmware>; ++ dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>, ++ <&rp1_dma RP1_DMA_PIO_CH3_TX>, <&rp1_dma RP1_DMA_PIO_CH3_RX>; ++ dma-names = "tx0", "rx0", "tx1", "rx1", "tx2", "rx2", "tx3", "rx3"; ++ status = "disabled"; ++ }; ++ + rp1_mmc0: mmc@180000 { + reg = <0xc0 0x40180000 0x0 0x100>; + compatible = "raspberrypi,rp1-dwcmshc"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch b/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch new file mode 100644 index 000000000..6accaf000 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1388-misc-rp1-pio-Add-an-in-kernel-API.patch @@ -0,0 +1,1797 @@ +From 2819a61eb000c207589c97eef9d69a237c6cfdf3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 8 Nov 2024 09:31:38 +0000 +Subject: [PATCH] misc: rp1-pio: Add an in-kernel API + +The header file linux/pio_rp1.h adds a pico-sdk-like interface to the +RP1 PIO subsystem for other drivers. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 169 ++++-- + include/linux/pio_instructions.h | 481 +++++++++++++++++ + include/linux/pio_rp1.h | 873 +++++++++++++++++++++++++++++++ + 3 files changed, 1474 insertions(+), 49 deletions(-) + create mode 100644 include/linux/pio_instructions.h + create mode 100644 include/linux/pio_rp1.h + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1,15 +1,17 @@ + // SPDX-License-Identifier: GPL-2.0 +-// PIO driver for RP1 +-// +-// Copyright (C) 2023-2024 Raspberry Pi Ltd. +-// +-// Parts of this driver are based on: +-// - vcio.c, by Noralf Trønnes +-// Copyright (C) 2010 Broadcom +-// Copyright (C) 2015 Noralf Trønnes +-// Copyright (C) 2021 Raspberry Pi (Trading) Ltd. +-// - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren +-// Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++/* ++ * PIO driver for RP1 ++ * ++ * Copyright (C) 2023-2024 Raspberry Pi Ltd. ++ * ++ * Parts of this driver are based on: ++ * - vcio.c, by Noralf Trønnes ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2015 Noralf Trønnes ++ * Copyright (C) 2021 Raspberry Pi (Trading) Ltd. ++ * - bcm2835_smi.c & bcm2835_smi_dev.c by Luke Wren ++ * Copyright (c) 2015 Raspberry Pi (Trading) Ltd. ++ */ + + #include + #include +@@ -97,6 +99,7 @@ struct rp1_pio_client { + uint32_t claimed_sms; + uint32_t claimed_instrs; + uint32_t claimed_dmas; ++ int error; + }; + + static struct rp1_pio_device *g_pio; +@@ -195,7 +198,7 @@ static int rp1_pio_find_program(struct r + return -1; + } + +-static int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_add_program_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -217,8 +220,9 @@ static int rp1_pio_can_add_program(struc + return rp1_pio_message(pio, PIO_CAN_ADD_PROGRAM, args, + offsetof(struct rp1_pio_add_program_args, instrs)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_can_add_program); + +-static int rp1_pio_add_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_add_program_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -254,6 +258,7 @@ static int rp1_pio_add_program(struct rp + mutex_unlock(&pio->instr_mutex); + return offset; + } ++EXPORT_SYMBOL_GPL(rp1_pio_add_program); + + static void rp1_pio_remove_instrs(struct rp1_pio_device *pio, uint32_t mask) + { +@@ -277,7 +282,7 @@ static void rp1_pio_remove_instrs(struct + mutex_unlock(&pio->instr_mutex); + } + +-static int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_remove_program_args *args = param; + uint32_t used_mask; +@@ -296,8 +301,9 @@ static int rp1_pio_remove_program(struct + } + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_remove_program); + +-static int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_device *pio = client->pio; + +@@ -309,8 +315,9 @@ static int rp1_pio_clear_instr_mem(struc + mutex_unlock(&pio->instr_mutex); + return 0; + } ++EXPORT_SYMBOL_GPL(rp1_pio_clear_instr_mem); + +-static int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -328,8 +335,9 @@ static int rp1_pio_sm_claim(struct rp1_p + mutex_unlock(&pio->instr_mutex); + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_claim); + +-static int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -341,99 +349,113 @@ static int rp1_pio_sm_unclaim(struct rp1 + mutex_unlock(&pio->instr_mutex); + return 0; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_unclaim); + +-static int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_claim_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_IS_CLAIMED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_is_claimed); + +-static int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_init_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_INIT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_init); + +-static int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_config_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_CONFIG, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_config); + +-static int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_exec_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_EXEC, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_exec); + +-static int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_clear_fifos_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_CLEAR_FIFOS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clear_fifos); + +-static int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_clkdiv_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_CLKDIV, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_clkdiv); + +-static int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_pins_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_PINS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pins); + +-static int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_pindirs_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_PINDIRS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_pindirs); + +-static int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_enabled_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_ENABLED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_enabled); + +-static int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_restart_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_RESTART, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_restart); + +-static int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_restart_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_CLKDIV_RESTART, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_clkdiv_restart); + +-static int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_enable_sync_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_ENABLE_SYNC, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_enable_sync); + +-static int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_put_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_PUT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_put); + +-static int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_get_args *args = param; + int ret; +@@ -444,69 +466,79 @@ static int rp1_pio_sm_get(struct rp1_pio + return offsetof(struct rp1_pio_sm_get_args, data) + ret; + return ret; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_get); + +-static int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_set_dmactrl_args *args = param; + + return rp1_pio_message(client->pio, PIO_SM_SET_DMACTRL, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); + +-static int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_init_args *args = param; + + return rp1_pio_message(client->pio, GPIO_INIT, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_init); + +-static int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_function_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_FUNCTION, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_function); + +-static int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_pulls_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_PULLS, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_pulls); + +-static int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_OUTOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_outover); + +-static int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_INOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_inover); + +-static int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_OEOVER, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_oeover); + +-static int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_INPUT_ENABLED, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_input_enabled); + +-static int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_set_args *args = param; + + return rp1_pio_message(client->pio, GPIO_SET_DRIVE_STRENGTH, args, sizeof(*args)); + } ++EXPORT_SYMBOL_GPL(rp1_pio_gpio_set_drive_strength); + + static void rp1_pio_sm_dma_callback(void *param) + { +@@ -633,7 +665,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + struct device *dev = &pdev->dev; + int ret = 0; + +- // Clean the slate - we're running synchronously ++ /* Clean the slate - we're running synchronously */ + dma->head_idx = 0; + dma->tail_idx = 0; + +@@ -686,7 +718,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + bytes -= copy_bytes; + } + +- // Block for completion ++ /* Block for completion */ + while (dma->tail_idx != dma->head_idx) { + if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { + dev_err(dev, "DMA wait timed out\n"); +@@ -830,22 +862,22 @@ struct handler_info { + HANDLER(WRITE_HW, write_hw), + }; + +-static int rp1_pio_open(struct inode *inode, struct file *filp) ++struct rp1_pio_client *pio_open(void) + { +- struct rp1_pio_device *pio = g_pio; + struct rp1_pio_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); + +- client->pio = pio; +- filp->private_data = client; ++ client->pio = g_pio; + +- return 0; ++ return client; + } ++EXPORT_SYMBOL_GPL(pio_open); + +-static int rp1_pio_release(struct inode *inode, struct file *filp) ++void pio_close(struct rp1_pio_client *client) + { +- struct rp1_pio_client *client = filp->private_data; + struct rp1_pio_device *pio = client->pio; + uint claimed_dmas = client->claimed_dmas; + int i; +@@ -885,6 +917,45 @@ static int rp1_pio_release(struct inode + /* Reinitialise the SM? */ + + kfree(client); ++} ++EXPORT_SYMBOL_GPL(pio_close); ++ ++void pio_set_error(struct rp1_pio_client *client, int err) ++{ ++ client->error = err; ++} ++EXPORT_SYMBOL_GPL(pio_set_error); ++ ++int pio_get_error(const struct rp1_pio_client *client) ++{ ++ return client->error; ++} ++EXPORT_SYMBOL_GPL(pio_get_error); ++ ++void pio_clear_error(struct rp1_pio_client *client) ++{ ++ client->error = 0; ++} ++EXPORT_SYMBOL_GPL(pio_clear_error); ++ ++static int rp1_pio_open(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client; ++ ++ client = pio_open(); ++ if (IS_ERR(client)) ++ return PTR_ERR(client); ++ ++ filp->private_data = client; ++ ++ return 0; ++} ++ ++static int rp1_pio_release(struct inode *inode, struct file *filp) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ ++ pio_close(client); + + return 0; + } +--- /dev/null ++++ b/include/linux/pio_instructions.h +@@ -0,0 +1,481 @@ ++/* SPDX-License-Identifier: BSD-3-Clause */ ++/* ++ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. ++ */ ++ ++#ifndef _HARDWARE_PIO_INSTRUCTIONS_H ++#define _HARDWARE_PIO_INSTRUCTIONS_H ++ ++/** \brief PIO instruction encoding ++ * \defgroup pio_instructions pio_instructions ++ * \ingroup hardware_pio ++ * ++ * Functions for generating PIO instruction encodings programmatically. In debug builds ++ *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function ++ * parameters. ++ * ++ * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" ++ */ ++ ++// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions ++#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS ++#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++enum pio_instr_bits { ++ pio_instr_bits_jmp = 0x0000, ++ pio_instr_bits_wait = 0x2000, ++ pio_instr_bits_in = 0x4000, ++ pio_instr_bits_out = 0x6000, ++ pio_instr_bits_push = 0x8000, ++ pio_instr_bits_pull = 0x8080, ++ pio_instr_bits_mov = 0xa000, ++ pio_instr_bits_irq = 0xc000, ++ pio_instr_bits_set = 0xe000, ++}; ++ ++#ifndef NDEBUG ++#define _PIO_INVALID_IN_SRC 0x08u ++#define _PIO_INVALID_OUT_DEST 0x10u ++#define _PIO_INVALID_SET_DEST 0x20u ++#define _PIO_INVALID_MOV_SRC 0x40u ++#define _PIO_INVALID_MOV_DEST 0x80u ++#else ++#define _PIO_INVALID_IN_SRC 0u ++#define _PIO_INVALID_OUT_DEST 0u ++#define _PIO_INVALID_SET_DEST 0u ++#define _PIO_INVALID_MOV_SRC 0u ++#define _PIO_INVALID_MOV_DEST 0u ++#endif ++ ++/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions ++ * \ingroup pio_instructions ++ * ++ * \note Not all values are suitable for all functions. Validity is only checked in debug mode when ++ * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 ++ */ ++enum pio_src_dest { ++ pio_pins = 0u, ++ pio_x = 1u, ++ pio_y = 2u, ++ pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++ pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, ++ pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, ++ pio_isr = 6u | _PIO_INVALID_SET_DEST, ++ pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, ++ pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, ++}; ++ ++static inline uint _pio_major_instr_bits(uint instr) { ++ return instr & 0xe000u; ++} ++ ++static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { ++ valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); ++#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) ++ uint32_t major = _pio_major_instr_bits(instr_bits); ++ if (major == pio_instr_bits_in || major == pio_instr_bits_out) { ++ assert(arg2 && arg2 <= 32); ++ } else { ++ assert(arg2 <= 31); ++ } ++#endif ++ return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); ++} ++ ++static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { ++ return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); ++} ++ ++/*! \brief Encode just the delay slot bits of an instruction ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay ++ * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt ++ * as they share the same bits within the instruction encoding. ++ * ++ * \param cycles the number of cycles 0-31 (or less if side set is being used) ++ * \return the delay slot bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_delay(uint cycles) { ++ // note that the maximum cycles will be smaller if sideset_bit_count > 0 ++ valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); ++ return cycles << 8u; ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in non optional side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return value << (13u - sideset_bit_count); ++} ++ ++/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) ++ * \ingroup pio_instructions ++ * ++ * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits ++ * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when ++ * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits ++ * within the instruction encoding. ++ * ++ * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm ++ * \param value the value to sideset on the pins ++ * \return the side set bits to be ORed with an instruction encoding ++ */ ++static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); ++ valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); ++ return 0x1000u | value << (12u - sideset_bit_count); ++} ++ ++/*! \brief Encode an unconditional JMP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !X ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_x(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y zero instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP Y-- ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_y_dec(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); ++} ++ ++/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP X!=Y ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_x_ne_y(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); ++} ++ ++/*! \brief Encode a conditional JMP if input pin high instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP PIN ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_pin(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); ++} ++ ++/*! \brief Encode a conditional JMP if output shift register not empty instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `JMP !OSRE ` ++ * ++ * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_jmp_not_osre(uint addr) { ++ return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); ++} ++ ++static inline uint _pio_encode_irq(bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return (relative ? 0x10u : 0x0u) | irq; ++} ++ ++/*! \brief Encode a WAIT for GPIO pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT GPIO ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param gpio The real GPIO number 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); ++} ++ ++/*! \brief Encode a WAIT for pin instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT PIN ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param pin The pin number 0-31 relative to the executing SM's input pin mapping ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_pin(bool polarity, uint pin) { ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); ++} ++ ++/*! \brief Encode a WAIT for IRQ instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `WAIT IRQ ` ++ * ++ * \param polarity true for `WAIT 1`, false for `WAIT 0` ++ * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { ++ valid_params_if(PIO_INSTRUCTIONS, irq <= 7); ++ return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode an IN instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IN , ` ++ * ++ * \param src The source to take data from ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_in(enum pio_src_dest src, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); ++} ++ ++/*! \brief Encode an OUT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `OUT , ` ++ * ++ * \param dest The destination to write data to ++ * \param count The number of bits 1-32 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); ++} ++ ++/*! \brief Encode a PUSH instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PUSH , ` ++ * ++ * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` ++ * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_push(bool if_full, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a PULL instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `PULL , ` ++ * ++ * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` ++ * \param block true for `PULL ... BLOCK`, false for `PULL ...` ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_pull(bool if_empty, bool block) { ++ return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); ++} ++ ++/*! \brief Encode a MOV instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ` ++ * ++ * \param dest The destination to write data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); ++} ++ ++/*! \brief Encode a MOV instruction with bit invert ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ~` ++ * ++ * \param dest The destination to write inverted data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a MOV instruction with bit reverse ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `MOV , ::` ++ * ++ * \param dest The destination to write bit reversed data to ++ * \param src The source to take data from ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); ++ valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); ++} ++ ++/*! \brief Encode a IRQ SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ SET ` ++ * ++ * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_set(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ WAIT instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ WAIT ` ++ * ++ * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_wait(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a IRQ CLEAR instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `IRQ CLEAR ` ++ * ++ * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` ++ * \param irq the irq number 0-7 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_irq_clear(bool relative, uint irq) { ++ return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); ++} ++ ++/*! \brief Encode a SET instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `SET , ` ++ * ++ * \param dest The destination to apply the value to ++ * \param value The value 0-31 ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { ++ valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); ++ return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); ++} ++ ++/*! \brief Encode a NOP instruction ++ * \ingroup pio_instructions ++ * ++ * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` ++ * ++ * \return The instruction encoding with 0 delay and no side set value ++ * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt ++ */ ++static inline uint pio_encode_nop(void) { ++ return pio_encode_mov(pio_y, pio_y); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/pio_rp1.h +@@ -0,0 +1,873 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2024 Raspberry Pi Ltd. ++ * All rights reserved. ++ */ ++ ++#ifndef _PIO_RP1_H ++#define _PIO_RP1_H ++ ++#include ++ ++#define PARAM_WARNINGS_ENABLED 1 ++ ++#ifdef DEBUG ++#define PARAM_WARNINGS_ENABLED 1 ++#endif ++ ++#ifndef PARAM_WARNINGS_ENABLED ++#define PARAM_WARNINGS_ENABLED 0 ++#endif ++ ++#define bad_params_if(client, test) \ ++ ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ ++ if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ ++ f; }) ++ ++#ifndef PARAM_ASSERTIONS_ENABLE_ALL ++#define PARAM_ASSERTIONS_ENABLE_ALL 0 ++#endif ++ ++#ifndef PARAM_ASSERTIONS_DISABLE_ALL ++#define PARAM_ASSERTIONS_DISABLE_ALL 0 ++#endif ++ ++#define PARAM_ASSERTIONS_ENABLED(x) \ ++ ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && \ ++ !PARAM_ASSERTIONS_DISABLE_ALL) ++#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) WARN_ON(test); }) ++ ++#include ++ ++#define NUM_PIO_STATE_MACHINES 4 ++#define PIO_INSTRUCTION_COUNT 32 ++#define PIO_ORIGIN_ANY ((uint)(~0)) ++#define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) ++ ++#define PICO_NO_HARDWARE 0 ++ ++#define pio0 pio_open_helper(0) ++ ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS 0x0000001f ++#define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB 0 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS 0x03f00000 ++#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB 20 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS 0x000003e0 ++#define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB 5 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS 0x1c000000 ++#define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB 26 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS 0x000f8000 ++#define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB 15 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS 0x00007c00 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB 10 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS 0xe0000000 ++#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB 29 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS 0x40000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB 30 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS 0x20000000 ++#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB 29 ++#define PROC_PIO_SM0_CLKDIV_INT_LSB 16 ++#define PROC_PIO_SM0_CLKDIV_FRAC_LSB 8 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS 0x0001f000 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB 12 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS 0x00000f80 ++#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB 7 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS 0x1f000000 ++#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB 24 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS 0x00040000 ++#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB 18 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS 0x00020000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB 17 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS 0x00010000 ++#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB 16 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS 0x01f00000 ++#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB 20 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS 0x00080000 ++#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB 19 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS 0x3e000000 ++#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB 25 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS 0x40000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB 30 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS 0x80000000 ++#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB 31 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS 0x00020000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB 17 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS 0x00040000 ++#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB 18 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS 0x00f80000 ++#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB 19 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS 0x00000020 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB 5 ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS 0x0000001f ++#define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB 0 ++ ++enum pio_fifo_join { ++ PIO_FIFO_JOIN_NONE = 0, ++ PIO_FIFO_JOIN_TX = 1, ++ PIO_FIFO_JOIN_RX = 2, ++}; ++ ++enum pio_mov_status_type { ++ STATUS_TX_LESSTHAN = 0, ++ STATUS_RX_LESSTHAN = 1 ++}; ++ ++enum pio_xfer_dir { ++ PIO_DIR_TO_SM, ++ PIO_DIR_FROM_SM, ++ PIO_DIR_COUNT ++}; ++ ++enum clock_index { ++ clk_sys = 5 ++}; ++ ++typedef struct pio_program { ++ const uint16_t *instructions; ++ uint8_t length; ++ int8_t origin; // required instruction memory origin or -1 ++} pio_program_t; ++ ++enum gpio_function { ++ GPIO_FUNC_FSEL0 = 0, ++ GPIO_FUNC_FSEL1 = 1, ++ GPIO_FUNC_FSEL2 = 2, ++ GPIO_FUNC_FSEL3 = 3, ++ GPIO_FUNC_FSEL4 = 4, ++ GPIO_FUNC_FSEL5 = 5, ++ GPIO_FUNC_FSEL6 = 6, ++ GPIO_FUNC_FSEL7 = 7, ++ GPIO_FUNC_FSEL8 = 8, ++ GPIO_FUNC_NULL = 0x1f, ++ ++ // Name a few ++ GPIO_FUNC_SYS_RIO = 5, ++ GPIO_FUNC_PROC_RIO = 6, ++ GPIO_FUNC_PIO = 7, ++}; ++ ++enum gpio_irq_level { ++ GPIO_IRQ_LEVEL_LOW = 0x1u, ++ GPIO_IRQ_LEVEL_HIGH = 0x2u, ++ GPIO_IRQ_EDGE_FALL = 0x4u, ++ GPIO_IRQ_EDGE_RISE = 0x8u, ++}; ++ ++enum gpio_override { ++ GPIO_OVERRIDE_NORMAL = 0, ++ GPIO_OVERRIDE_INVERT = 1, ++ GPIO_OVERRIDE_LOW = 2, ++ GPIO_OVERRIDE_HIGH = 3, ++}; ++enum gpio_slew_rate { ++ GPIO_SLEW_RATE_SLOW = 0, ++ GPIO_SLEW_RATE_FAST = 1 ++}; ++ ++enum gpio_drive_strength { ++ GPIO_DRIVE_STRENGTH_2MA = 0, ++ GPIO_DRIVE_STRENGTH_4MA = 1, ++ GPIO_DRIVE_STRENGTH_8MA = 2, ++ GPIO_DRIVE_STRENGTH_12MA = 3 ++}; ++ ++typedef rp1_pio_sm_config pio_sm_config; ++ ++typedef struct rp1_pio_client *PIO; ++ ++void pio_set_error(struct rp1_pio_client *client, int err); ++int pio_get_error(struct rp1_pio_client *client); ++void pio_clear_error(struct rp1_pio_client *client); ++ ++int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_add_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_remove_program(struct rp1_pio_client *client, void *param); ++int rp1_pio_clear_instr_mem(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_claim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_unclaim(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_is_claimed(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_config(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_exec(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clear_fifos(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_clkdiv(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pins(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_pindirs(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_clkdiv_restart(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_enable_sync(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_outover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_inover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_oeover(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); ++int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); ++ ++int pio_init(void); ++PIO pio_open(void); ++void pio_close(PIO pio); ++ ++int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); ++int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data); ++ ++static inline bool pio_can_add_program(struct rp1_pio_client *client, ++ const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_can_add_program(client, &args); ++} ++ ++static inline bool pio_can_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return false; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return !rp1_pio_can_add_program(client, &args); ++} ++ ++uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) ++{ ++ struct rp1_pio_add_program_args args; ++ int offset; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT)) ++ return PIO_ORIGIN_ANY; ++ args.origin = (program->origin == -1) ? PIO_ORIGIN_ANY : program->origin; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ offset = rp1_pio_add_program(client, &args); ++ return (offset >= 0) ? offset : PIO_ORIGIN_ANY; ++} ++ ++static inline int pio_add_program_at_offset(struct rp1_pio_client *client, ++ const pio_program_t *program, uint offset) ++{ ++ struct rp1_pio_add_program_args args; ++ ++ if (bad_params_if(client, program->length > PIO_INSTRUCTION_COUNT || ++ offset >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ args.origin = offset; ++ args.num_instrs = program->length; ++ ++ memcpy(args.instrs, program->instructions, args.num_instrs * sizeof(args.instrs[0])); ++ return rp1_pio_add_program(client, &args); ++} ++ ++static inline int pio_remove_program(struct rp1_pio_client *client, const pio_program_t *program, ++ uint loaded_offset) ++{ ++ struct rp1_pio_remove_program_args args; ++ ++ args.origin = loaded_offset; ++ args.num_instrs = program->length; ++ ++ return rp1_pio_remove_program(client, &args); ++} ++ ++static inline int pio_clear_instruction_memory(struct rp1_pio_client *client) ++{ ++ return rp1_pio_clear_instr_mem(client, NULL); ++} ++ ++static inline int pio_sm_claim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_claim_sm_mask(struct rp1_pio_client *client, uint mask) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_sm_unclaim(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 1 << sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_claim(client, &args); ++} ++ ++static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = 0 }; ++ int sm; ++ ++ sm = rp1_pio_sm_claim(client, &args); ++ if (sm < 0 && required) ++ WARN_ON("No PIO state machines are available"); ++ return sm; ++} ++ ++static inline bool pio_sm_is_claimed(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return true; ++ return rp1_pio_sm_is_claimed(client, &args); ++} ++ ++static inline int pio_sm_init(struct rp1_pio_client *client, uint sm, uint initial_pc, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, ++ .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ initial_pc >= PIO_INSTRUCTION_COUNT)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_init(client, &args); ++} ++ ++static inline int pio_sm_set_config(struct rp1_pio_client *client, uint sm, ++ const pio_sm_config *config) ++{ ++ struct rp1_pio_sm_init_args args = { .sm = sm, .config = *config }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_set_config(client, &args); ++} ++ ++int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) ++{ ++ struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || instr > (uint16_t)~0)) ++ return -EINVAL; ++ ++ return rp1_pio_sm_exec(client, &args); ++} ++ ++static inline int pio_sm_clear_fifos(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clear_fifos(client, &args); ++} ++ ++static inline bool pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, ++ uint8_t *div_frac) ++{ ++ if (bad_params_if(NULL, div < 1 || div > 65536)) ++ return false; ++ *div_int = (uint16_t)div; ++ if (*div_int == 0) ++ *div_frac = 0; ++ else ++ *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); ++ return true; ++} ++ ++static inline int pio_sm_set_clkdiv_int_frac(struct rp1_pio_client *client, uint sm, ++ uint16_t div_int, uint8_t div_frac) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, ++ .div_frac = div_frac }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (div_int == 0 && div_frac != 0))) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, float div) ++{ ++ struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm }; ++ ++ if (!pio_calculate_clkdiv_from_float(div, &args.div_int, &args.div_frac)) ++ return -EINVAL; ++ return rp1_pio_sm_set_clkdiv(client, &args); ++} ++ ++static inline int pio_sm_set_pins(struct rp1_pio_client *client, uint sm, uint32_t pin_values) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = GPIOS_MASK }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pins_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_values, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pins(client, &args); ++} ++ ++static inline int pio_sm_set_pindirs_with_mask(struct rp1_pio_client *client, uint sm, ++ uint32_t pin_dirs, uint32_t pin_mask) ++{ ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, ++ .mask = pin_mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ (pin_dirs & GPIOS_MASK) != pin_dirs || ++ (pin_mask & pin_mask) != pin_mask)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_consecutive_pindirs(struct rp1_pio_client *client, uint sm, ++ uint pin_base, uint pin_count, bool is_out) ++{ ++ uint32_t mask = ((1 << pin_count) - 1) << pin_base; ++ struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, ++ .mask = mask }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES || ++ pin_base >= RP1_PIO_GPIO_COUNT || ++ pin_count > RP1_PIO_GPIO_COUNT || ++ (pin_base + pin_count) > RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_sm_set_pindirs(client, &args); ++} ++ ++static inline int pio_sm_set_enabled(struct rp1_pio_client *client, uint sm, bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_set_sm_mask_enabled(struct rp1_pio_client *client, uint32_t mask, ++ bool enabled) ++{ ++ struct rp1_pio_sm_set_enabled_args args = { .mask = mask, .enable = enabled }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_set_enabled(client, &args); ++} ++ ++static inline int pio_sm_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_restart(client, &args); ++} ++ ++static inline int pio_sm_clkdiv_restart(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_clkdiv_restart_sm_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_clkdiv_restart(client, &args); ++} ++ ++static inline int pio_enable_sm_in_sync_mask(struct rp1_pio_client *client, uint32_t mask) ++{ ++ struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; ++ ++ if (bad_params_if(client, mask >= (1 << NUM_PIO_STATE_MACHINES))) ++ return -EINVAL; ++ return rp1_pio_sm_enable_sync(client, &args); ++} ++ ++static inline int pio_sm_set_dmactrl(struct rp1_pio_client *client, uint sm, bool is_tx, ++ uint32_t ctrl) ++{ ++ struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_set_dmactrl(client, &args); ++}; ++ ++static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline int pio_sm_put_blocking(struct rp1_pio_client *client, uint sm, uint32_t data) ++{ ++ struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = true, .data = data }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_put(client, &args); ++} ++ ++static inline uint32_t pio_sm_get(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = false }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline uint32_t pio_sm_get_blocking(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = true }; ++ ++ if (!bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ rp1_pio_sm_get(client, &args); ++ return args.data; ++} ++ ++static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) ++{ ++ if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || ++ out_count > RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | ++ (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | ++ (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); ++} ++ ++static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) ++{ ++ if (bad_params_if(NULL, set_base >= RP1_PIO_GPIO_COUNT || ++ set_count > 5)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | ++ PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | ++ (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | ++ (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); ++} ++ ++ ++static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) ++{ ++ if (bad_params_if(NULL, in_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | ++ (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) ++{ ++ if (bad_params_if(NULL, sideset_base >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | ++ (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); ++} ++ ++static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, ++ bool pindirs) ++{ ++ if (bad_params_if(NULL, bit_count > 5 || ++ (optional && (bit_count == 0)))) ++ return; ++ c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | ++ (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | ++ (optional << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | ++ (pindirs << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); ++} ++ ++static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, ++ uint8_t div_frac) ++{ ++ if (bad_params_if(NULL, div_int == 0 && div_frac != 0)) ++ return; ++ ++ c->clkdiv = ++ (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | ++ (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); ++} ++ ++static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) ++{ ++ uint16_t div_int; ++ uint8_t div_frac; ++ ++ pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); ++ sm_config_set_clkdiv_int_frac(c, div_int, div_frac); ++} ++ ++static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) ++{ ++ if (bad_params_if(NULL, wrap >= PIO_INSTRUCTION_COUNT || ++ wrap_target >= PIO_INSTRUCTION_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | ++ PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | ++ (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | ++ (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); ++} ++ ++static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) ++{ ++ if (bad_params_if(NULL, pin >= RP1_PIO_GPIO_COUNT)) ++ return; ++ ++ c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | ++ (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); ++} ++ ++static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, ++ uint push_threshold) ++{ ++ if (bad_params_if(NULL, push_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | ++ (autopush << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | ++ ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); ++} ++ ++static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, ++ uint pull_threshold) ++{ ++ if (bad_params_if(NULL, pull_threshold > 32)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & ++ ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | ++ (shift_right << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | ++ (autopull << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | ++ ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); ++} ++ ++static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) ++{ ++ if (bad_params_if(NULL, join != PIO_FIFO_JOIN_NONE && ++ join != PIO_FIFO_JOIN_TX && ++ join != PIO_FIFO_JOIN_RX)) ++ return; ++ ++ c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | ++ PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | ++ (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); ++} ++ ++static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, ++ uint enable_pin_index) ++{ ++ c->execctrl = (c->execctrl & ++ (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | ++ PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | ++ (sticky << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | ++ (has_enable_pin << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | ++ ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); ++} ++ ++static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, ++ uint status_n) ++{ ++ if (bad_params_if(NULL, status_sel != STATUS_TX_LESSTHAN && ++ status_sel != STATUS_RX_LESSTHAN)) ++ return; ++ ++ c->execctrl = (c->execctrl ++ & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) ++ | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) ++ | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & ++ PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); ++} ++ ++static inline pio_sm_config pio_get_default_sm_config(void) ++{ ++ pio_sm_config c = { 0 }; ++ ++ sm_config_set_clkdiv_int_frac(&c, 1, 0); ++ sm_config_set_wrap(&c, 0, 31); ++ sm_config_set_in_shift(&c, true, false, 32); ++ sm_config_set_out_shift(&c, true, false, 32); ++ return c; ++} ++ ++static inline uint32_t clock_get_hz(enum clock_index clk_index) ++{ ++ const uint32_t MHZ = 1000000; ++ ++ if (bad_params_if(NULL, clk_index != clk_sys)) ++ return 0; ++ return 200 * MHZ; ++} ++ ++static inline int pio_gpio_set_function(struct rp1_pio_client *client, uint gpio, ++ enum gpio_function fn) ++{ ++ struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_function(client, &args); ++} ++ ++static inline int pio_gpio_init(struct rp1_pio_client *client, uint gpio) ++{ ++ struct rp1_gpio_init_args args = { .gpio = gpio }; ++ int ret; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ ret = rp1_pio_gpio_init(client, &args); ++ if (ret) ++ return ret; ++ return pio_gpio_set_function(client, gpio, RP1_GPIO_FUNC_PIO); ++} ++ ++static inline int pio_gpio_set_pulls(struct rp1_pio_client *client, uint gpio, bool up, bool down) ++{ ++ struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_pulls(client, &args); ++} ++ ++static inline int pio_gpio_set_outover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_outover(client, &args); ++} ++ ++static inline int pio_gpio_set_inover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_inover(client, &args); ++} ++ ++static inline int pio_gpio_set_oeover(struct rp1_pio_client *client, uint gpio, uint value) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_oeover(client, &args); ++} ++ ++static inline int pio_gpio_set_input_enabled(struct rp1_pio_client *client, uint gpio, ++ bool enabled) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_input_enabled(client, &args); ++} ++ ++static inline int pio_gpio_set_drive_strength(struct rp1_pio_client *client, uint gpio, ++ enum gpio_drive_strength drive) ++{ ++ struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; ++ ++ if (bad_params_if(client, gpio >= RP1_PIO_GPIO_COUNT)) ++ return -EINVAL; ++ return rp1_pio_gpio_set_drive_strength(client, &args); ++} ++ ++static inline int pio_gpio_pull_up(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, true, false); ++} ++ ++static inline int pio_gpio_pull_down(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, true); ++} ++ ++static inline int pio_gpio_disable_pulls(struct rp1_pio_client *client, uint gpio) ++{ ++ return pio_gpio_set_pulls(client, gpio, false, false); ++} ++ ++#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch new file mode 100644 index 000000000..9eed5f568 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1389-pwm-Add-pwm-pio-rp1-driver.patch @@ -0,0 +1,299 @@ +From 4d20aadc3188ecfb62b309a9924ee9696a94fc33 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 8 Nov 2024 09:37:58 +0000 +Subject: [PATCH] pwm: Add pwm-pio-rp1 driver + +Use the PIO hardware on RP1 to implement a PWM interface. + +Signed-off-by: Phil Elwell +--- + drivers/pwm/Kconfig | 11 ++ + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-pio-rp1.c | 251 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 263 insertions(+) + create mode 100644 drivers/pwm/pwm-pio-rp1.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -465,6 +465,17 @@ config PWM_PCA9685 + To compile this driver as a module, choose M here: the module + will be called pwm-pca9685. + ++config PWM_PIO_RP1 ++ tristate "RP1 PIO PWM support" ++ depends on FIRMWARE_RP1 || COMPILE_TEST ++ help ++ This is a PWM framework driver for Raspberry Pi 5, using the PIO ++ hardware of RP1 to provide PWM functionality. Supports up to 4 ++ instances on GPIOs in bank 0. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-pio-rp1. ++ + config PWM_PXA + tristate "PXA PWM support" + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -42,6 +42,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o + obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o ++obj-$(CONFIG_PWM_PIO_RP1) += pwm-pio-rp1.o + obj-$(CONFIG_PWM_PXA) += pwm-pxa.o + obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o + obj-$(CONFIG_PWM_RP1) += pwm-rp1.o +--- /dev/null ++++ b/drivers/pwm/pwm-pio-rp1.c +@@ -0,0 +1,251 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Raspberry Pi PIO PWM. ++ * ++ * Copyright (C) 2024 Raspberry Pi Ltd. ++ * ++ * Author: Phil Elwell (phil@raspberrypi.com) ++ * ++ * Based on the pwm-rp1 driver by: ++ * Naushir Patuck ++ * and on the pwm-gpio driver by: ++ * Vincent Whitchurch ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct pwm_pio_rp1 { ++ struct pwm_chip chip; ++ struct device *dev; ++ struct gpio_desc *gpiod; ++ struct mutex mutex; ++ PIO pio; ++ uint sm; ++ uint offset; ++ uint gpio; ++ uint32_t period; /* In SM cycles */ ++ uint32_t duty_cycle; /* In SM cycles */ ++ enum pwm_polarity polarity; ++ bool enabled; ++}; ++ ++/* Generated from pwm.pio by pioasm */ ++#define pwm_wrap_target 0 ++#define pwm_wrap 6 ++#define pwm_loop_ticks 3 ++ ++static const uint16_t pwm_program_instructions[] = { ++ // .wrap_target ++ 0x9080, // 0: pull noblock side 0 ++ 0xa027, // 1: mov x, osr ++ 0xa046, // 2: mov y, isr ++ 0x00a5, // 3: jmp x != y, 5 ++ 0x1806, // 4: jmp 6 side 1 ++ 0xa042, // 5: nop ++ 0x0083, // 6: jmp y--, 3 ++ // .wrap ++}; ++ ++static const struct pio_program pwm_program = { ++ .instructions = pwm_program_instructions, ++ .length = 7, ++ .origin = -1, ++}; ++ ++static unsigned int pwm_pio_resolution __read_mostly; ++ ++static inline pio_sm_config pwm_program_get_default_config(uint offset) ++{ ++ pio_sm_config c = pio_get_default_sm_config(); ++ ++ sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); ++ sm_config_set_sideset(&c, 2, true, false); ++ return c; ++} ++ ++static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) ++{ ++ pio_gpio_init(pio, pin); ++ ++ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); ++ pio_sm_config c = pwm_program_get_default_config(offset); ++ ++ sm_config_set_sideset_pins(&c, pin); ++ pio_sm_init(pio, sm, offset, &c); ++} ++ ++/* Write `period` to the input shift register - must be disabled */ ++static void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) ++{ ++ pio_sm_put_blocking(pio, sm, period); ++ pio_sm_exec(pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); ++} ++ ++/* Write `level` to TX FIFO. State machine will copy this into X. */ ++static void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) ++{ ++ pio_sm_put_blocking(pio, sm, level); ++} ++ ++static int pwm_pio_rp1_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct pwm_pio_rp1 *ppwm = container_of(chip, struct pwm_pio_rp1, chip); ++ uint32_t new_duty_cycle; ++ uint32_t new_period; ++ ++ if (state->duty_cycle && state->duty_cycle < pwm_pio_resolution) ++ return -EINVAL; ++ ++ if (state->duty_cycle != state->period && ++ (state->period - state->duty_cycle < pwm_pio_resolution)) ++ return -EINVAL; ++ ++ new_period = state->period / pwm_pio_resolution; ++ new_duty_cycle = state->duty_cycle / pwm_pio_resolution; ++ ++ mutex_lock(&ppwm->mutex); ++ ++ if ((ppwm->enabled && !state->enabled) || new_period != ppwm->period) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, false); ++ ppwm->enabled = false; ++ } ++ ++ if (new_period != ppwm->period) { ++ pio_pwm_set_period(ppwm->pio, ppwm->sm, new_period); ++ ppwm->period = new_period; ++ } ++ ++ if (state->enabled && new_duty_cycle != ppwm->duty_cycle) { ++ pio_pwm_set_level(ppwm->pio, ppwm->sm, new_duty_cycle); ++ ppwm->duty_cycle = new_duty_cycle; ++ } ++ ++ if (state->polarity != ppwm->polarity) { ++ pio_gpio_set_outover(ppwm->pio, ppwm->gpio, ++ (state->polarity == PWM_POLARITY_INVERSED) ? ++ GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); ++ ppwm->polarity = state->polarity; ++ } ++ ++ if (!ppwm->enabled && state->enabled) { ++ pio_sm_set_enabled(ppwm->pio, ppwm->sm, true); ++ ppwm->enabled = true; ++ } ++ ++ mutex_unlock(&ppwm->mutex); ++ ++ return 0; ++} ++ ++static const struct pwm_ops pwm_pio_rp1_ops = { ++ .apply = pwm_pio_rp1_apply, ++}; ++ ++static int pwm_pio_rp1_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct of_phandle_args of_args = { 0 }; ++ struct device *dev = &pdev->dev; ++ struct pwm_pio_rp1 *ppwm; ++ struct pwm_chip *chip; ++ bool is_rp1; ++ ++ ppwm = devm_kzalloc(dev, sizeof(*ppwm), GFP_KERNEL); ++ if (IS_ERR(ppwm)) ++ return PTR_ERR(ppwm); ++ ++ chip = &ppwm->chip; ++ ++ mutex_init(&ppwm->mutex); ++ ++ ppwm->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); ++ /* Need to check that this is an RP1 GPIO in the first bank, and retrieve the offset */ ++ /* Unfortunately I think this has to be done by parsing the gpios property */ ++ if (IS_ERR(ppwm->gpiod)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->gpiod), ++ "could not get a gpio\n"); ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(np, "gpios", "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, ++ "can't find gpio declaration\n"); ++ ++ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); ++ of_node_put(of_args.np); ++ if (!is_rp1 || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, ++ "not an RP1 gpio\n"); ++ ++ ppwm->gpio = of_args.args[0]; ++ ++ ppwm->pio = pio_open(); ++ if (IS_ERR(ppwm->pio)) ++ return dev_err_probe(dev, PTR_ERR(ppwm->pio), ++ "%pfw: could not open PIO\n", ++ dev_fwnode(dev)); ++ ++ ppwm->sm = pio_claim_unused_sm(ppwm->pio, false); ++ if ((int)ppwm->sm < 0) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: no free PIO SM\n", ++ dev_fwnode(dev)); ++ } ++ ++ ppwm->offset = pio_add_program(ppwm->pio, &pwm_program); ++ if (ppwm->offset == PIO_ORIGIN_ANY) { ++ pio_close(ppwm->pio); ++ return dev_err_probe(dev, -EBUSY, ++ "%pfw: not enough PIO program space\n", ++ dev_fwnode(dev)); ++ } ++ ++ pwm_program_init(ppwm->pio, ppwm->sm, ppwm->offset, ppwm->gpio); ++ ++ pwm_pio_resolution = (1000u * 1000 * 1000 * pwm_loop_ticks) / clock_get_hz(clk_sys); ++ ++ chip->dev = dev; ++ chip->ops = &pwm_pio_rp1_ops; ++ chip->atomic = true; ++ chip->npwm = 1; ++ ++ platform_set_drvdata(pdev, ppwm); ++ ++ return devm_pwmchip_add(dev, chip); ++} ++ ++static void pwm_pio_rp1_remove(struct platform_device *pdev) ++{ ++ struct pwm_pio_rp1 *ppwm = platform_get_drvdata(pdev); ++ ++ pio_close(ppwm->pio); ++} ++ ++static const struct of_device_id pwm_pio_rp1_dt_ids[] = { ++ { .compatible = "raspberrypi,pwm-pio-rp1" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, pwm_pio_rp1_dt_ids); ++ ++static struct platform_driver pwm_pio_rp1_driver = { ++ .driver = { ++ .name = "pwm-pio-rp1", ++ .of_match_table = pwm_pio_rp1_dt_ids, ++ }, ++ .probe = pwm_pio_rp1_probe, ++ .remove_new = pwm_pio_rp1_remove, ++}; ++module_platform_driver(pwm_pio_rp1_driver); ++ ++MODULE_DESCRIPTION("PWM PIO RP1 driver"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch new file mode 100644 index 000000000..bd6d63331 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1391-overlays-Add-pwm-pio-overlay.patch @@ -0,0 +1,99 @@ +From ba7e2e3d03a432acbc338c6c03e46dcd97cfa1b3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 7 Nov 2024 11:41:33 +0000 +Subject: [PATCH] overlays: Add pwm-pio overlay + +Add an overlay to enable a single-channel PIO-assisted PWM interface on any +header pin. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 ++++ + arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++ + .../arm/boot/dts/overlays/pwm-pio-overlay.dts | 39 +++++++++++++++++++ + 4 files changed, 52 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pwm-pio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -219,6 +219,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + pwm-2chan.dtbo \ + pwm-gpio.dtbo \ + pwm-ir-tx.dtbo \ ++ pwm-pio.dtbo \ + pwm1.dtbo \ + qca7000.dtbo \ + qca7000-uart0.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3926,6 +3926,14 @@ Params: gpio_pin Output G + func Pin function (default 2 = Alt5) + + ++Name: pwm-pio ++Info: Configures a GPIO pin as PIO-assisted PWM output. Unlike hardware PWM, ++ this can be used on any RP1 GPIO in bank 0 (0-27). Up to 4 are ++ supported, assuming nothing else is using PIO. Pi 5 only. ++Load: dtoverlay=pwm-pio,= ++Params: gpio Output GPIO (0-27, default 4) ++ ++ + Name: pwm1 + Info: Configures one or two PWM channel on PWM1 (BCM2711 only) + N.B.: +--- a/arch/arm/boot/dts/overlays/overlay_map.dts ++++ b/arch/arm/boot/dts/overlays/overlay_map.dts +@@ -240,6 +240,10 @@ + bcm2712; + }; + ++ pwm-pio { ++ bcm2712; ++ }; ++ + pwm1 { + bcm2711; + }; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Device tree overlay for RP1 PIO PWM. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2712"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_pio_pins: pwm_pio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ function = "pio"; ++ bias-disable; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_pio: pwm_pio@4 { ++ compatible = "raspberrypi,pwm-pio-rp1"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pio_pins>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ gpio = <&pwm_pio>,"gpios:4", ++ <&pwm_pio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&pwm_pio>,"reg:0", ++ <&pwm_pio_pins>,"reg:0"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch new file mode 100644 index 000000000..cd197f7a7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1392-fixup-misc-Add-RP1-PIO-driver.patch @@ -0,0 +1,29 @@ +From 1b5acd42281ad102b79f4e1794f0a0cccdafda05 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Sat, 16 Nov 2024 16:53:31 +0000 +Subject: [PATCH] fixup! misc: Add RP1 PIO driver + +Signed-off-by: Phil Elwell +--- + include/uapi/misc/rp1_pio_if.h | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -1,4 +1,4 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ ++/* SPDX-License-Identifier: GPL-2.0 + WITH Linux-syscall-note */ + /* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. +@@ -169,10 +169,6 @@ struct rp1_access_hw_args { + #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) + #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) + +-#ifdef CONFIG_COMPAT +-//XXX #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct pio_sm_xfer_data_args) +-#endif +- + #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) + #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch b/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch new file mode 100644 index 000000000..6862a74e2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1394-misc-rp1-pio-Add-compat_ioctl-method.patch @@ -0,0 +1,91 @@ +From b4472d09b1ffdafd8132803ffbec62596e559fd8 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 18 Nov 2024 09:10:52 +0000 +Subject: [PATCH] misc: rp1-pio: Add compat_ioctl method + +Provide a compat_ioctl method, to support running a 64-bit kernel with +a 32-bit userland. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 64 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 64 insertions(+) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -996,11 +996,75 @@ static long rp1_pio_ioctl(struct file *f + return ret; + } + ++#ifdef CONFIG_COMPAT ++ ++struct rp1_pio_sm_xfer_data_args_compat { ++ uint16_t sm; ++ uint16_t dir; ++ uint16_t data_bytes; ++ compat_uptr_t data; ++}; ++ ++struct rp1_access_hw_args_compat { ++ uint32_t addr; ++ uint32_t len; ++ compat_uptr_t data; ++}; ++ ++#define PIO_IOC_SM_XFER_DATA_COMPAT _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args_compat) ++#define PIO_IOC_READ_HW_COMPAT _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args_compat) ++#define PIO_IOC_WRITE_HW_COMPAT _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args_compat) ++ ++static long rp1_pio_compat_ioctl(struct file *filp, unsigned int ioctl_num, ++ unsigned long ioctl_param) ++{ ++ struct rp1_pio_client *client = filp->private_data; ++ ++ switch (ioctl_num) { ++ case PIO_IOC_SM_XFER_DATA_COMPAT: ++ { ++ struct rp1_pio_sm_xfer_data_args_compat compat_param; ++ struct rp1_pio_sm_xfer_data_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.sm = compat_param.sm; ++ param.dir = compat_param.dir; ++ param.data_bytes = compat_param.data_bytes; ++ param.data = compat_ptr(compat_param.data); ++ return rp1_pio_sm_xfer_data(client, ¶m); ++ } ++ ++ case PIO_IOC_READ_HW_COMPAT: ++ case PIO_IOC_WRITE_HW_COMPAT: ++ { ++ struct rp1_access_hw_args_compat compat_param; ++ struct rp1_access_hw_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.addr = compat_param.addr; ++ param.len = compat_param.len; ++ param.data = compat_ptr(compat_param.data); ++ if (ioctl_num == PIO_IOC_READ_HW_COMPAT) ++ return rp1_pio_read_hw(client, ¶m); ++ else ++ return rp1_pio_write_hw(client, ¶m); ++ } ++ default: ++ return rp1_pio_ioctl(filp, ioctl_num, ioctl_param); ++ } ++} ++#else ++#define rp1_pio_compat_ioctl NULL ++#endif ++ + const struct file_operations rp1_pio_fops = { + .owner = THIS_MODULE, + .open = rp1_pio_open, + .release = rp1_pio_release, + .unlocked_ioctl = rp1_pio_ioctl, ++ .compat_ioctl = rp1_pio_compat_ioctl, + }; + + static int rp1_pio_probe(struct platform_device *pdev) diff --git a/target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch b/target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch similarity index 93% rename from target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch rename to target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch index 691484d46..bc20db253 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0998-i2c-designware-Add-support-for-bus-clear-feature.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1396-i2c-designware-Add-support-for-bus-clear-feature.patch @@ -1,7 +1,7 @@ -From 24cb07b0c0724a22e474d12e7c2d5b834bf3b076 Mon Sep 17 00:00:00 2001 +From 0e4968617aad7d0f88e0a630499202eaae407a19 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 26 Mar 2024 15:57:46 +0000 -Subject: [PATCH 0998/1085] i2c: designware: Add support for bus clear feature +Subject: [PATCH] i2c: designware: Add support for bus clear feature Newer versions of the DesignWare I2C block support the detection of stuck signals, and a mechanism to recover from them. Add the required @@ -108,7 +108,7 @@ Signed-off-by: Phil Elwell DW_IC_TX_ABRT_10ADDR1_NOACK | \ --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c -@@ -212,6 +212,7 @@ static int i2c_dw_set_timings_master(str +@@ -215,6 +215,7 @@ static int i2c_dw_set_timings_master(str */ static int i2c_dw_init_master(struct dw_i2c_dev *dev) { @@ -116,7 +116,7 @@ Signed-off-by: Phil Elwell int ret; ret = i2c_dw_acquire_lock(dev); -@@ -235,6 +236,17 @@ static int i2c_dw_init_master(struct dw_ +@@ -238,6 +239,17 @@ static int i2c_dw_init_master(struct dw_ regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt); } @@ -134,7 +134,7 @@ Signed-off-by: Phil Elwell /* Write SDA hold time if supported */ if (dev->sda_hold_time) regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time); -@@ -1071,6 +1083,7 @@ int i2c_dw_probe_master(struct dw_i2c_de +@@ -1074,6 +1086,7 @@ int i2c_dw_probe_master(struct dw_i2c_de struct i2c_adapter *adap = &dev->adapter; unsigned long irq_flags; unsigned int ic_con; @@ -142,7 +142,7 @@ Signed-off-by: Phil Elwell int ret; init_completion(&dev->cmd_complete); -@@ -1106,7 +1119,11 @@ int i2c_dw_probe_master(struct dw_i2c_de +@@ -1109,7 +1122,11 @@ int i2c_dw_probe_master(struct dw_i2c_de if (ret) return ret; diff --git a/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch b/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch new file mode 100644 index 000000000..3ad28d688 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1397-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch @@ -0,0 +1,3592 @@ +From 32511f035b086bca254d8adab234cef3541492b4 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Thu, 17 Oct 2024 11:37:29 +0100 +Subject: [PATCH] drivers: media: pci: Update Hailo accelerator device driver + to v4.19 + +Sourced from https://github.com/hailo-ai/hailort-drivers/ + +Signed-off-by: Naushir Patuck +--- + drivers/media/pci/hailo/Makefile | 4 +- + drivers/media/pci/hailo/common/fw_operation.c | 50 ++- + drivers/media/pci/hailo/common/fw_operation.h | 8 +- + .../media/pci/hailo/common/fw_validation.c | 10 +- + .../media/pci/hailo/common/fw_validation.h | 7 +- + .../pci/hailo/common/hailo_ioctl_common.h | 28 +- + .../media/pci/hailo/common/hailo_resource.c | 23 +- + .../media/pci/hailo/common/hailo_resource.h | 2 +- + drivers/media/pci/hailo/common/pcie_common.c | 380 +++++++++--------- + drivers/media/pci/hailo/common/pcie_common.h | 38 +- + drivers/media/pci/hailo/common/soc_structs.h | 79 ++++ + drivers/media/pci/hailo/common/utils.h | 23 +- + drivers/media/pci/hailo/common/vdma_common.c | 93 +++-- + drivers/media/pci/hailo/common/vdma_common.h | 22 +- + drivers/media/pci/hailo/src/fops.c | 284 ++----------- + drivers/media/pci/hailo/src/fops.h | 5 +- + drivers/media/pci/hailo/src/nnc.c | 299 ++++++++++++++ + drivers/media/pci/hailo/src/nnc.h | 22 + + drivers/media/pci/hailo/src/pci_soc_ioctl.c | 155 ------- + drivers/media/pci/hailo/src/pcie.c | 166 +++----- + drivers/media/pci/hailo/src/pcie.h | 26 +- + drivers/media/pci/hailo/src/soc.c | 244 +++++++++++ + .../pci/hailo/src/{pci_soc_ioctl.h => soc.h} | 13 +- + drivers/media/pci/hailo/src/sysfs.c | 2 +- + drivers/media/pci/hailo/src/sysfs.h | 2 +- + drivers/media/pci/hailo/src/utils.c | 26 -- + drivers/media/pci/hailo/utils/compact.h | 2 +- + drivers/media/pci/hailo/utils/fw_common.h | 2 +- + .../pci/hailo/utils/integrated_nnc_utils.c | 10 +- + .../pci/hailo/utils/integrated_nnc_utils.h | 2 +- + drivers/media/pci/hailo/utils/logs.c | 2 +- + drivers/media/pci/hailo/utils/logs.h | 2 +- + drivers/media/pci/hailo/vdma/ioctl.c | 18 +- + drivers/media/pci/hailo/vdma/ioctl.h | 6 +- + drivers/media/pci/hailo/vdma/memory.c | 12 +- + drivers/media/pci/hailo/vdma/memory.h | 2 +- + drivers/media/pci/hailo/vdma/vdma.c | 39 +- + drivers/media/pci/hailo/vdma/vdma.h | 5 +- + 38 files changed, 1224 insertions(+), 889 deletions(-) + create mode 100644 drivers/media/pci/hailo/common/soc_structs.h + create mode 100644 drivers/media/pci/hailo/src/nnc.c + create mode 100644 drivers/media/pci/hailo/src/nnc.h + delete mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c + create mode 100644 drivers/media/pci/hailo/src/soc.c + rename drivers/media/pci/hailo/src/{pci_soc_ioctl.h => soc.h} (53%) + mode change 100755 => 100644 + delete mode 100644 drivers/media/pci/hailo/src/utils.c + +--- a/drivers/media/pci/hailo/Makefile ++++ b/drivers/media/pci/hailo/Makefile +@@ -8,9 +8,9 @@ obj-$(CONFIG_MEDIA_PCI_HAILO) := hailo_p + + hailo_pci-objs += src/pcie.o + hailo_pci-objs += src/fops.o +-hailo_pci-objs += src/utils.o + hailo_pci-objs += src/sysfs.o +-hailo_pci-objs += src/pci_soc_ioctl.o ++hailo_pci-objs += src/nnc.o ++hailo_pci-objs += src/soc.o + + hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o + hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o +--- a/drivers/media/pci/hailo/common/fw_operation.c ++++ b/drivers/media/pci/hailo/common/fw_operation.c +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. +-**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + + #include "fw_operation.h" + +@@ -15,7 +15,10 @@ typedef struct { + u32 chip_offset; + } FW_DEBUG_BUFFER_HEADER_t; + +-#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t)) ++#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) ++#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) ++#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) + + int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification) + { +@@ -35,6 +38,21 @@ int hailo_read_firmware_notification(str + return 0; + } + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, ++ struct hailo_d2h_notification *notification) ++{ ++ struct hailo_resource notification_resource; ++ ++ if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) { ++ return -EINVAL; ++ } ++ ++ notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, ++ notification_resource.size = sizeof(struct hailo_d2h_notification); ++ ++ return hailo_read_firmware_notification(¬ification_resource, notification); ++} ++ + static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header) + { + size_t ready_to_read = 0; +@@ -100,4 +118,30 @@ long hailo_read_firmware_log(struct hail + + params->read_bytes = ready_to_read; + return 0; ++} ++ ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params) ++{ ++ long err = 0; ++ struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE}; ++ ++ if (HAILO_CPU_ID_CPU0 == params->cpu_id) { ++ log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; ++ } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { ++ log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; ++ } else { ++ return -EINVAL; ++ } ++ ++ if (0 == params->buffer_size) { ++ params->read_bytes = 0; ++ return 0; ++ } ++ ++ err = hailo_read_firmware_log(&log_resource, params); ++ if (0 != err) { ++ return err; ++ } ++ ++ return 0; + } +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/fw_operation.h ++++ b/drivers/media/pci/hailo/common/fw_operation.h +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved. +-**/ ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ + + #ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_ + #define _HAILO_COMMON_FIRMWARE_OPERATION_H_ +@@ -16,8 +16,12 @@ extern "C" { + + int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); + ++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification); ++ + long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params); + ++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params); ++ + #ifdef __cplusplus + } + #endif +--- a/drivers/media/pci/hailo/common/fw_validation.c ++++ b/drivers/media/pci/hailo/common/fw_validation.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "fw_validation.h" +@@ -85,15 +85,15 @@ exit: + } + + int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, +- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert) ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert) + { + +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = *outer_consumed_firmware_offset; + +- firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset); +- CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), -EINVAL); ++ firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset); ++ CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL); + + if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) || + (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) { +--- a/drivers/media/pci/hailo/common/fw_validation.h ++++ b/drivers/media/pci/hailo/common/fw_validation.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_ +@@ -44,8 +44,7 @@ typedef struct { + typedef struct { + u32 key_size; + u32 content_size; +- u8 certificates_data[0]; +-} secure_boot_certificate_t; ++} secure_boot_certificate_header_t; + + #ifdef _MSC_VER + #pragma warning(pop) +@@ -60,6 +59,6 @@ int FW_VALIDATION__validate_fw_header(ui + firmware_header_t **out_firmware_header, enum hailo_board_type board_type); + + int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, +- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert); ++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert); + + #endif +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h ++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h +@@ -1,13 +1,13 @@ + // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_IOCTL_COMMON_H_ + #define _HAILO_IOCTL_COMMON_H_ + + #define HAILO_DRV_VER_MAJOR 4 +-#define HAILO_DRV_VER_MINOR 18 ++#define HAILO_DRV_VER_MINOR 19 + #define HAILO_DRV_VER_REVISION 0 + + #define _STRINGIFY_EXPANDED( x ) #x +@@ -17,10 +17,11 @@ + + // This value is not easily changeable. + // For example: the channel interrupts ioctls assume we have up to 32 channels +-#define MAX_VDMA_CHANNELS_PER_ENGINE (32) +-#define MAX_VDMA_ENGINES (3) +-#define SIZE_OF_VDMA_DESCRIPTOR (16) +-#define VDMA_DEST_CHANNELS_START (16) ++#define MAX_VDMA_CHANNELS_PER_ENGINE (32) ++#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION (16) ++#define MAX_VDMA_ENGINES (3) ++#define SIZE_OF_VDMA_DESCRIPTOR (16) ++#define VDMA_DEST_CHANNELS_START (16) + + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128) + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1) +@@ -37,8 +38,8 @@ + #define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT) + #define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT (2) + #define FW_ACCESS_DRIVER_SHUTDOWN_MASK (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT) +-#define FW_ACCESS_SOC_CONNECT_SHIFT (3) +-#define FW_ACCESS_SOC_CONNECT_MASK (1 << FW_ACCESS_SOC_CONNECT_SHIFT) ++#define FW_ACCESS_SOC_CONTROL_SHIFT (3) ++#define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT) + + #define INVALID_VDMA_CHANNEL (0xff) + +@@ -245,6 +246,12 @@ struct hailo_desc_list_release_params { + uintptr_t desc_handle; // in + }; + ++struct hailo_write_action_list_params { ++ uint8_t *data; // in ++ size_t size; // in ++ uint64_t dma_address; // out ++}; ++ + /* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */ + struct hailo_desc_list_program_params { + size_t buffer_handle; // in +@@ -508,6 +515,7 @@ struct hailo_vdma_launch_transfer_params + + /* structure used in ioctl HAILO_SOC_CONNECT */ + struct hailo_soc_connect_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in +@@ -522,6 +530,7 @@ struct hailo_soc_close_params { + + /* structure used in ioctl HAILO_PCI_EP_ACCEPT */ + struct hailo_pci_ep_accept_params { ++ uint16_t port_number; // in + uint8_t input_channel_index; // out + uint8_t output_channel_index; // out + uintptr_t input_desc_handle; // in +@@ -562,6 +571,7 @@ struct tCompatibleHailoIoctlData + struct hailo_soc_close_params SocCloseParams; + struct hailo_pci_ep_accept_params AcceptParams; + struct hailo_pci_ep_close_params PciEpCloseParams; ++ struct hailo_write_action_list_params WriteActionListParams; + } Buffer; + }; + #endif // _MSC_VER +@@ -632,6 +642,7 @@ enum hailo_nnc_ioctl_code { + HAILO_DISABLE_NOTIFICATION_CODE, + HAILO_READ_LOG_CODE, + HAILO_RESET_NN_CORE_CODE, ++ HAILO_WRITE_ACTION_LIST_CODE, + + // Must be last + HAILO_NNC_IOCTL_MAX_NR +@@ -642,6 +653,7 @@ enum hailo_nnc_ioctl_code { + #define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE) + #define HAILO_READ_LOG _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params) + #define HAILO_RESET_NN_CORE _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE) ++#define HAILO_WRITE_ACTION_LIST _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_WRITE_ACTION_LIST_CODE, struct hailo_write_action_list_params) + + enum hailo_soc_ioctl_code { + HAILO_SOC_IOCTL_CONNECT_CODE, +--- a/drivers/media/pci/hailo/common/hailo_resource.c ++++ b/drivers/media/pci/hailo/common/hailo_resource.c +@@ -1,24 +1,31 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "hailo_resource.h" + ++#include "utils.h" ++ + #include + #include + #include + #include + ++#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3))) + + u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset) + { +- return ioread8((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val); + } + + u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset) + { +- return ioread16((u8*)resource->address + offset); ++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val); + } + + u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset) +@@ -28,12 +35,18 @@ u32 hailo_resource_read32(struct hailo_r + + void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value) + { +- iowrite8(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); + } + + void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value) + { +- iowrite16(value, (u8*)resource->address + offset); ++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset)); ++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset)); ++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value), ++ (u8*)ALIGN_TO_32_BIT(resource->address + offset)); + } + + void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value) +--- a/drivers/media/pci/hailo/common/hailo_resource.h ++++ b/drivers/media/pci/hailo/common/hailo_resource.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_HAILO_RESOURCE_H_ +--- a/drivers/media/pci/hailo/common/pcie_common.c ++++ b/drivers/media/pci/hailo/common/pcie_common.c +@@ -1,10 +1,11 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "pcie_common.h" + #include "fw_operation.h" ++#include "soc_structs.h" + + #include + #include +@@ -35,10 +36,6 @@ + #define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100) + #define FIRMWARE_LOAD_SLEEP_MS (50) + +-#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024) +-#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE) +- +-#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640) + #define PCIE_REQUEST_SIZE_OFFSET (0x640) + + #define PCIE_CONFIG_VENDOR_OFFSET (0x0098) +@@ -59,7 +56,6 @@ struct hailo_fw_addresses { + u32 app_fw_code_ram_base; + u32 boot_key_cert; + u32 boot_cont_cert; +- u32 boot_fw_trigger; + u32 core_code_ram_base; + u32 core_fw_header; + u32 atr0_trsl_addr1; +@@ -69,13 +65,11 @@ struct hailo_fw_addresses { + + struct loading_stage { + const struct hailo_file_batch *batch; ++ u32 trigger_address; + }; + + struct hailo_board_compatibility { + struct hailo_fw_addresses fw_addresses; +- const char *fw_filename; +- const struct hailo_config_constants board_cfg; +- const struct hailo_config_constants fw_cfg; + const struct loading_stage stages[MAX_LOADING_STAGES]; + }; + +@@ -85,28 +79,32 @@ static const struct hailo_file_batch hai + .address = 0xA0000, + .max_size = 0x8004, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot.dtb.signed", + .address = 0xA8004, + .max_size = 0x20000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/scu_fw.bin", + .address = 0x20000, + .max_size = 0x40000, + .is_mandatory = true, +- .has_header = true ++ .has_header = true, ++ .has_core = false + }, + { + .filename = NULL, + .address = 0x00, + .max_size = 0x00, + .is_mandatory = false, +- .has_header = false ++ .has_header = false, ++ .has_core = false + } + }; + +@@ -116,36 +114,140 @@ static const struct hailo_file_batch hai + .address = 0x85000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/u-boot-tfa.itb", + .address = 0x86000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/fitImage", + .address = 0x87000000, + .max_size = 0x1000000, + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + { + .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz", + .address = 0x88000000, + .max_size = 0x20000000, // Max size 512MB + .is_mandatory = true, +- .has_header = false ++ .has_header = false, ++ .has_core = false + }, + }; + ++// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb) ++static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = { ++ { ++ .filename = "hailo/hailo10h/u-boot-spl.bin", ++ .address = 0x85000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo10h/u-boot-tfa.itb", ++ .address = 0x86000000, ++ .max_size = 0x1000000, ++ .is_mandatory = true, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++}; ++ ++static const struct hailo_file_batch hailo8_files_stg1[] = { ++ { ++ .filename = "hailo/hailo8_fw.4.19.0.bin", ++ .address = 0x20000, ++ .max_size = 0x50000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = "hailo/hailo8_board_cfg.bin", ++ .address = 0x60001000, ++ .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = "hailo/hailo8_fw_cfg.bin", ++ .address = 0x60001500, ++ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = { ++ { ++ .filename = "hailo/hailo15_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ ++static const struct hailo_file_batch pluto_files_stg1[] = { ++ { ++ .filename = "hailo/pluto_fw.bin", ++ .address = 0x20000, ++ .max_size = 0x100000, ++ .is_mandatory = true, ++ .has_header = true, ++ .has_core = true ++ }, ++ { ++ .filename = NULL, ++ .address = 0x00, ++ .max_size = 0x00, ++ .is_mandatory = false, ++ .has_header = false, ++ .has_core = false ++ } ++}; ++ + static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = { + [HAILO_BOARD_TYPE_HAILO8] = { + .fw_addresses = { + .boot_fw_header = 0xE0030, +- .boot_fw_trigger = 0xE0980, + .boot_key_cert = 0xE0048, + .boot_cont_cert = 0xE0390, + .app_fw_code_ram_base = 0x60000, +@@ -155,22 +257,16 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1684, + .boot_status = 0xe0000, + }, +- .fw_filename = "hailo/hailo8_fw.bin", +- .board_cfg = { +- .filename = "hailo/hailo8_board_cfg.bin", +- .address = 0x60001000, +- .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE, +- }, +- .fw_cfg = { +- .filename = "hailo/hailo8_fw_cfg.bin", +- .address = 0x60001500, +- .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE, ++ .stages = { ++ { ++ .batch = hailo8_files_stg1, ++ .trigger_address = 0xE0980 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -180,22 +276,16 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, +- .fw_filename = "hailo/hailo15_fw.bin", +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, ++ .stages = { ++ { ++ .batch = hailo10h_legacy_files_stg1, ++ .trigger_address = 0x88c98 ++ }, + }, + }, + [HAILO_BOARD_TYPE_HAILO10H] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -205,23 +295,18 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, +- .fw_filename = NULL, +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, + .stages = { + { + .batch = hailo10h_files_stg1, ++ .trigger_address = 0x88c98 + }, + { + .batch = hailo10h_files_stg2, ++ .trigger_address = 0x84000000 ++ }, ++ { ++ .batch = hailo10h_files_stg2_linux_in_emmc, ++ .trigger_address = 0x84000000 + }, + }, + }, +@@ -230,7 +315,6 @@ static const struct hailo_board_compatib + [HAILO_BOARD_TYPE_PLUTO] = { + .fw_addresses = { + .boot_fw_header = 0x88000, +- .boot_fw_trigger = 0x88c98, + .boot_key_cert = 0x88018, + .boot_cont_cert = 0x886a8, + .app_fw_code_ram_base = 0x20000, +@@ -241,16 +325,11 @@ static const struct hailo_board_compatib + .raise_ready_offset = 0x174c, + .boot_status = 0x80000, + }, +- .fw_filename = "hailo/pluto_fw.bin", +- .board_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, +- }, +- .fw_cfg = { +- .filename = NULL, +- .address = 0, +- .max_size = 0, ++ .stages = { ++ { ++ .batch = pluto_files_stg1, ++ .trigger_address = 0x88c98 ++ }, + }, + } + }; +@@ -340,21 +419,6 @@ void hailo_pcie_write_firmware_driver_sh + hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); + } + +-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, +- struct hailo_d2h_notification *notification) +-{ +- struct hailo_resource notification_resource; +- +- if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resources->fw_access.size) { +- return -EINVAL; +- } +- +- notification_resource.address = resources->fw_access.address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET, +- notification_resource.size = sizeof(struct hailo_d2h_notification); +- +- return hailo_read_firmware_notification(¬ification_resource, notification); +-} +- + int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index) + { + size_t offset = 0; +@@ -388,7 +452,7 @@ static void write_memory_chunk(struct ha + u32 ATR_INDEX = 0; + BUG_ON(dest_offset + len > (u32)resources->fw_access.size); + +- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX); + (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src); + } + +@@ -398,13 +462,13 @@ static void read_memory_chunk( + u32 ATR_INDEX = 0; + BUG_ON(src_offset + len > (u32)resources->fw_access.size); + +- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)src, ATR_INDEX); ++ (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX); + (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest); + } + + // Note: this function modify the device ATR table (that is also used by the firmware for control and vdma). + // Use with caution, and restore the original atr if needed. +-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) ++static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len) + { + struct hailo_atr_config previous_atr = {0}; + hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK); +@@ -417,8 +481,8 @@ void write_memory(struct hailo_pcie_reso + + if (base_address != dest) { + // Data is not aligned, write the first chunk +- chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len); +- write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len); ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len); ++ write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len); + offset += chunk_len; + } + +@@ -447,8 +511,8 @@ static void read_memory(struct hailo_pci + + if (base_address != src) { + // Data is not aligned, write the first chunk +- chunk_len = min(base_address + ATR_TABLE_SIZE - src, len); +- read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len); ++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len); ++ read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len); + offset += chunk_len; + } + +@@ -463,12 +527,12 @@ static void read_memory(struct hailo_pci + } + + static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header, +- secure_boot_certificate_t *fw_cert) ++ secure_boot_certificate_header_t *fw_cert) + { + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); +- void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t)); +- void *key_data = &fw_cert->certificates_data[0]; +- void *content_data = &fw_cert->certificates_data[fw_cert->key_size]; ++ u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t)); ++ u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t)); ++ u8 *content_data = key_data + fw_cert->key_size; + + write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t)); + +@@ -487,13 +551,11 @@ static void hailo_write_core_firmware(st + write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t)); + } + +-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources) ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address) + { +- const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + u32 pcie_finished = 1; + +- write_memory(resources, fw_addresses->boot_fw_trigger, +- (void*)&pcie_finished, sizeof(pcie_finished)); ++ write_memory(resources, address, (void*)&pcie_finished, sizeof(pcie_finished)); + } + + u32 hailo_get_boot_status(struct hailo_pcie_resources *resources) +@@ -501,8 +563,7 @@ u32 hailo_get_boot_status(struct hailo_p + u32 boot_status = 0; + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); + +- read_memory(resources, fw_addresses->boot_status, +- &boot_status, sizeof(boot_status)); ++ read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status)); + + return boot_status; + } +@@ -517,11 +578,11 @@ u32 hailo_get_boot_status(struct hailo_p + */ + static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size, + firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header, +- secure_boot_certificate_t **out_firmware_cert, enum hailo_board_type board_type) ++ secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type) + { + firmware_header_t *app_firmware_header = NULL; + firmware_header_t *core_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + int err = -EINVAL; + u32 consumed_firmware_offset = 0; + +@@ -571,25 +632,25 @@ exit: + return err; + } + +-static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev) ++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev) + { + const struct firmware *firmware = NULL; + firmware_header_t *app_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; ++ secure_boot_certificate_header_t *firmware_cert = NULL; + firmware_header_t *core_firmware_header = NULL; + int err = 0; + +- err = request_firmware_direct(&firmware, files_batch->filename, dev); ++ err = request_firmware_direct(&firmware, file_info->filename, dev); + if (err < 0) { + return err; + } + +- if (firmware->size > files_batch->max_size) { ++ if (firmware->size > file_info->max_size) { + release_firmware(firmware); + return -EFBIG; + } + +- if (files_batch->has_header) { ++ if (file_info->has_header) { + err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size, + &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); + if (err < 0) { +@@ -598,8 +659,11 @@ static int write_single_file(struct hail + } + + hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); ++ if (file_info->has_core) { ++ hailo_write_core_firmware(resources, core_firmware_header); ++ } + } else { +- write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size); ++ write_memory(resources, file_info->address, (void*)firmware->data, firmware->size); + } + + release_firmware(firmware); +@@ -632,31 +696,13 @@ int hailo_pcie_write_firmware_batch(stru + dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename); + } + +- return 0; +-} +- +-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size) +-{ +- firmware_header_t *app_firmware_header = NULL; +- secure_boot_certificate_t *firmware_cert = NULL; +- firmware_header_t *core_firmware_header = NULL; +- +- int err = FW_VALIDATION__validate_fw_headers((uintptr_t)fw_data, fw_size, +- &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type); +- if (err < 0) { +- return err; +- } +- +- hailo_write_app_firmware(resources, app_firmware_header, firmware_cert); +- hailo_write_core_firmware(resources, core_firmware_header); +- +- hailo_trigger_firmware_boot(resources); ++ hailo_trigger_firmware_boot(resources, compat[resources->board_type].stages[stage].trigger_address); + + return 0; + } + + // TODO: HRT-14147 - remove this function +-bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources) ++static bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources) + { + return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED; + } +@@ -691,32 +737,6 @@ bool hailo_pcie_wait_for_firmware(struct + return false; + } + +-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, +- const size_t config_size, const struct hailo_config_constants *config_consts) +-{ +- if (config_size > config_consts->max_size) { +- return -EINVAL; +- } +- +- write_memory(resources, config_consts->address, config_data, (u32)config_size); +- return 0; +-} +- +-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return &compat[board_type].board_cfg; +-} +- +-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return &compat[board_type].fw_cfg; +-} +- +-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type) { +- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0); +- return compat[board_type].fw_filename; +-} +- + void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap) + { + size_t i = 0; +@@ -745,7 +765,7 @@ void hailo_pcie_enable_interrupts(struct + hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF); + + mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION | +- BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED); ++ BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED | BCS_ISTATUS_SOC_CLOSED_IRQ); + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); + } + +@@ -754,45 +774,15 @@ void hailo_pcie_disable_interrupts(struc + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0); + } + +-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params) +-{ +- long err = 0; +- struct hailo_resource log_resource = {resources->fw_access.address, DEBUG_BUFFER_TOTAL_SIZE}; +- +- if (HAILO_CPU_ID_CPU0 == params->cpu_id) { +- log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET; +- } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) { +- log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET; +- } else { +- return -EINVAL; +- } +- +- if (0 == params->buffer_size) { +- params->read_bytes = 0; +- return 0; +- } +- +- err = hailo_read_firmware_log(&log_resource, params); +- if (0 != err) { +- return err; +- } +- +- return 0; +-} +- + static int direct_memory_transfer(struct hailo_pcie_resources *resources, + struct hailo_memory_transfer_params *params) + { +- if (params->address > U32_MAX) { +- return -EFAULT; +- } +- + switch (params->transfer_direction) { + case TRANSFER_READ: +- read_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ read_memory(resources, params->address, params->buffer, (u32)params->count); + break; + case TRANSFER_WRITE: +- write_memory(resources, (u32)params->address, params->buffer, (u32)params->count); ++ write_memory(resources, params->address, params->buffer, (u32)params->count); + break; + default: + return -EINVAL; +@@ -845,16 +835,18 @@ int hailo_set_device_type(struct hailo_p + return 0; + } + +-// On PCIe, just return the address +-static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id) ++// On PCIe, just return the start address ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id) + { + (void)channel_id; +- return (u64)dma_address; ++ (void)dma_address_end; ++ (void)step; ++ return (u64)dma_address_start; + } + + struct hailo_vdma_hw hailo_pcie_vdma_hw = { + .hw_ops = { +- .encode_desc_dma_address = encode_dma_address ++ .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range, + }, + .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID, + .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK, +@@ -862,11 +854,19 @@ struct hailo_vdma_hw hailo_pcie_vdma_hw + .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK, + }; + +-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources) ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request) + { + const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); +- const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK; ++ BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); + +- // Write shutdown flag to FW +- hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, soc_connect_value); +-} +\ No newline at end of file ++ hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request); ++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK); ++} ++ ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response) ++{ ++ BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes"); ++ hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response); ++} +--- a/drivers/media/pci/hailo/common/pcie_common.h ++++ b/drivers/media/pci/hailo/common/pcie_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_PCIE_COMMON_H_ +@@ -12,6 +12,7 @@ + #include "fw_operation.h" + #include "utils.h" + #include "vdma_common.h" ++#include "soc_structs.h" + + #include + #include +@@ -21,6 +22,7 @@ + #define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000) + #define BCS_ISTATUS_HOST_DRIVER_DOWN (0x08000000) + #define BCS_ISTATUS_SOC_CONNECT_ACCEPTED (0x10000000) ++#define BCS_ISTATUS_SOC_CLOSED_IRQ (0x20000000) + #define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF) + #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00) + +@@ -42,7 +44,7 @@ + #define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4 + #define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2 + +-typedef u32 hailo_ptr_t; ++typedef u64 hailo_ptr_t; + + struct hailo_pcie_resources { + struct hailo_resource config; // BAR0 +@@ -63,7 +65,8 @@ struct hailo_atr_config { + enum loading_stages { + FIRST_STAGE = 0, + SECOND_STAGE = 1, +- MAX_LOADING_STAGES = 2 ++ SECOND_STAGE_LINUX_IN_EMMC = 2, ++ MAX_LOADING_STAGES = 3 + }; + + enum hailo_pcie_interrupt_masks { +@@ -71,6 +74,7 @@ enum hailo_pcie_interrupt_masks { + FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION, + DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN, + SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED, ++ SOC_CLOSED_IRQ = BCS_ISTATUS_SOC_CLOSED_IRQ, + VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK, + VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK + }; +@@ -80,18 +84,13 @@ struct hailo_pcie_interrupt_source { + u32 vdma_channels_bitmap; + }; + +-struct hailo_config_constants { +- const char *filename; +- u32 address; +- size_t max_size; +-}; +- + struct hailo_file_batch { + const char *filename; + u32 address; + size_t max_size; + bool is_mandatory; + bool has_header; ++ bool has_core; + }; + + // TODO: HRT-6144 - Align Windows/Linux to QNX +@@ -130,27 +129,15 @@ void hailo_pcie_disable_interrupts(struc + int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command); + int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command); + +-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size); + int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage); + bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources); + bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources); + +-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources, +- struct hailo_d2h_notification *notification); +- +-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data, +- const size_t config_size, const struct hailo_config_constants *config_consts); +-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type); +-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type); +-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type); +- +-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params); + int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params); + + bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources); + void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources); +-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len); +-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources); ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address); + + int hailo_set_device_type(struct hailo_pcie_resources *resources); + +@@ -159,7 +146,12 @@ u32 hailo_get_boot_status(struct hailo_p + int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index); + void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index); + +-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources); ++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); ++ ++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources, ++ const struct hailo_pcie_soc_request *request); ++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources, ++ struct hailo_pcie_soc_response *response); + + #ifdef __cplusplus + } +--- /dev/null ++++ b/drivers/media/pci/hailo/common/soc_structs.h +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: MIT ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * Contains definitions for pcie soc to pcie ep communication ++ */ ++ ++#ifndef __HAILO_COMMON_SOC_STRUCTS__ ++#define __HAILO_COMMON_SOC_STRUCTS__ ++ ++#include ++ ++#pragma pack(push, 1) ++ ++struct hailo_pcie_soc_connect_request { ++ u16 port; ++}; ++ ++struct hailo_pcie_soc_connect_response { ++ u8 input_channel_index; ++ u8 output_channel_index; ++}; ++ ++ ++struct hailo_pcie_soc_close_request { ++ u32 channels_bitmap; ++}; ++ ++struct hailo_pcie_soc_close_response { ++ u8 reserved; ++}; ++ ++enum hailo_pcie_soc_control_code { ++ // Start from big initial value to ensure the right code was used (using 0 ++ // as initiale may cause confusion if the code was not set correctly). ++ HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100, ++ HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ HAILO_PCIE_SOC_CONTROL_CODE_INVALID, ++}; ++ ++#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES (16) ++#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16) ++ ++// IRQ to signal the PCIe that the EP was closed/released ++#define PCI_EP_SOC_CLOSED_IRQ (0x00000020) ++#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010) ++ ++struct hailo_pcie_soc_request { ++ u32 control_code; ++ union { ++ struct hailo_pcie_soc_connect_request connect; ++ struct hailo_pcie_soc_close_request close; ++ u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES]; ++ }; ++}; ++ ++struct hailo_pcie_soc_response { ++ u32 control_code; ++ s32 status; ++ union { ++ struct hailo_pcie_soc_connect_response connect; ++ struct hailo_pcie_soc_close_response close; ++ u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES]; ++ }; ++}; ++ ++#pragma pack(pop) ++ ++// Compile time validate function. Don't need to call it. ++static inline void __validate_soc_struct_sizes(void) ++{ ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) != ++ sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size"); ++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) != ++ sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size"); ++} ++ ++#endif /* __HAILO_COMMON_SOC_STRUCTS__ */ +\ No newline at end of file +--- a/drivers/media/pci/hailo/common/utils.h ++++ b/drivers/media/pci/hailo/common/utils.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_DRIVER_UTILS_H_ +@@ -8,6 +8,11 @@ + + #include + ++#define DWORD_SIZE (4) ++#define WORD_SIZE (2) ++#define BYTE_SIZE (1) ++#define BITS_IN_BYTE (8) ++ + #define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); } + #define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos))) + +@@ -50,6 +55,22 @@ static inline uint8_t ceil_log2(uint32_t + return result; + } + ++// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned. ++#define MAX_POWER_OF_2_VALUE (0x80000000) ++#define POWER_OF_2_ERROR ((uint32_t)-1) ++static inline uint32_t get_nearest_powerof_2(uint32_t value) ++{ ++ uint32_t power_of_2 = 1; ++ if (value > MAX_POWER_OF_2_VALUE) { ++ return POWER_OF_2_ERROR; ++ } ++ ++ while (value > power_of_2) { ++ power_of_2 <<= 1; ++ } ++ return power_of_2; ++} ++ + #ifndef DIV_ROUND_UP + #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + #endif +--- a/drivers/media/pci/hailo/common/vdma_common.c ++++ b/drivers/media/pci/hailo/common/vdma_common.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "vdma_common.h" +@@ -62,11 +62,6 @@ + #define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1) + #define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK + +-#define DWORD_SIZE (4) +-#define WORD_SIZE (2) +-#define BYTE_SIZE (1) +-#define BITS_IN_BYTE (8) +- + #define TIMESTAMPS_CIRC_SPACE(timestamp_list) \ + CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE) + #define TIMESTAMPS_CIRC_CNT(timestamp_list) \ +@@ -150,7 +145,7 @@ static bool validate_last_desc_status(st + return true; + } + +-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, ++static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, + u8 data_id) + { + descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) + +@@ -174,33 +169,45 @@ static int program_descriptors_in_chunk( + u32 max_desc_index, + u8 channel_id) + { +- const u32 desc_per_chunk = DIV_ROUND_UP(chunk_size, desc_list->desc_page_size); ++ const u16 page_size = desc_list->desc_page_size; ++ const u8 ddr_data_id = vdma_hw->ddr_data_id; ++ const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size); ++ const u32 starting_desc_index = desc_index; ++ const u32 residue_size = chunk_size % page_size; + struct hailo_vdma_descriptor *dma_desc = NULL; +- u16 size_to_program = 0; +- u32 index = 0; + u64 encoded_addr = 0; + +- for (index = 0; index < desc_per_chunk; index++) { +- if (desc_index > max_desc_index) { +- return -ERANGE; +- } +- +- encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address(chunk_addr, channel_id); +- if (INVALID_VDMA_ADDRESS == encoded_addr) { +- return -EFAULT; +- } ++ if (descs_to_program == 0) { ++ // Nothing to program ++ return 0; ++ } + +- dma_desc = &desc_list->desc_list[desc_index % desc_list->desc_count]; +- size_to_program = chunk_size > desc_list->desc_page_size ? +- desc_list->desc_page_size : (u16)chunk_size; +- hailo_vdma_program_descriptor(dma_desc, encoded_addr, size_to_program, vdma_hw->ddr_data_id); ++ // We iterate through descriptors [desc_index, desc_index + descs_to_program) ++ if (desc_index + descs_to_program > max_desc_index + 1) { ++ return -ERANGE; ++ } + +- chunk_addr += size_to_program; +- chunk_size -= size_to_program; +- desc_index++; ++ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, channel_id); ++ if (INVALID_VDMA_ADDRESS == encoded_addr) { ++ return -EFAULT; + } + +- return (int)desc_per_chunk; ++ // Program all descriptors except the last one ++ for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) { ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ hailo_vdma_program_descriptor( ++ &desc_list->desc_list[desc_index & desc_list->desc_count_mask], ++ encoded_addr, page_size, ddr_data_id); ++ encoded_addr += page_size; ++ } ++ ++ // Handle the last descriptor outside of the loop ++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. ++ dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask]; ++ hailo_vdma_program_descriptor(dma_desc, encoded_addr, ++ (residue_size == 0) ? page_size : (u16)residue_size, ddr_data_id); ++ ++ return (int)descs_to_program; + } + + static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw, +@@ -236,11 +243,11 @@ static int bind_and_program_descriptors_ + { + const u8 channel_id = get_channel_id(channel_index); + int desc_programmed = 0; ++ int descs_programmed_in_chunk = 0; + u32 max_desc_index = 0; + u32 chunk_size = 0; + struct scatterlist *sg_entry = NULL; + unsigned int i = 0; +- int ret = 0; + size_t buffer_current_offset = 0; + dma_addr_t chunk_start_addr = 0; + u32 program_size = buffer->size; +@@ -272,14 +279,14 @@ static int bind_and_program_descriptors_ + (u32)(sg_dma_len(sg_entry)); + chunk_size = min((u32)program_size, chunk_size); + +- ret = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, ++ descs_programmed_in_chunk = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, + starting_desc, max_desc_index, channel_id); +- if (ret < 0) { +- return ret; ++ if (descs_programmed_in_chunk < 0) { ++ return descs_programmed_in_chunk; + } + +- desc_programmed += ret; +- starting_desc = starting_desc + ret; ++ desc_programmed += descs_programmed_in_chunk; ++ starting_desc = starting_desc + descs_programmed_in_chunk; + program_size -= chunk_size; + buffer_current_offset += sg_dma_len(sg_entry); + } +@@ -583,21 +590,23 @@ void hailo_vdma_engine_disable_channels( + engine->enabled_channels &= ~bitmap; + + for_each_vdma_channel(engine, channel, channel_index) { +- channel_state_init(&channel->state); ++ if (hailo_test_bit(channel_index, &bitmap)) { ++ channel_state_init(&channel->state); + +- while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { +- struct hailo_ongoing_transfer transfer; +- ongoing_transfer_pop(channel, &transfer); +- +- if (channel->last_desc_list == NULL) { +- pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); +- continue; ++ while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { ++ struct hailo_ongoing_transfer transfer; ++ ongoing_transfer_pop(channel, &transfer); ++ ++ if (channel->last_desc_list == NULL) { ++ pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index); ++ continue; ++ } ++ ++ clear_dirty_descs(channel, &transfer); + } + +- clear_dirty_descs(channel, &transfer); ++ channel->last_desc_list = NULL; + } +- +- channel->last_desc_list = NULL; + } + } + +--- a/drivers/media/pci/hailo/common/vdma_common.h ++++ b/drivers/media/pci/hailo/common/vdma_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: MIT + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_COMMON_VDMA_COMMON_H_ +@@ -30,7 +30,13 @@ struct hailo_vdma_descriptor { + + struct hailo_vdma_descriptors_list { + struct hailo_vdma_descriptor *desc_list; +- u32 desc_count; // Must be power of 2 if is_circular is set. ++ // Must be power of 2 if is_circular is set. ++ u32 desc_count; ++ // The nearest power of 2 to desc_count (including desc_count), minus 1. ++ // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo. ++ // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask' ++ // will return the same value. ++ u32 desc_count_mask; + u16 desc_page_size; + bool is_circular; + }; +@@ -113,9 +119,10 @@ struct hailo_vdma_engine { + }; + + struct hailo_vdma_hw_ops { +- // Accepts some dma_addr_t mapped to the device and encodes it using +- // hw specific encode. returns INVALID_VDMA_ADDRESS on failure. +- u64 (*encode_desc_dma_address)(dma_addr_t dma_address, u8 channel_id); ++ // Accepts start, end and step of an address range (of type dma_addr_t). ++ // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid. ++ // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid. ++ u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id); + }; + + struct hailo_vdma_hw { +@@ -136,12 +143,9 @@ struct hailo_vdma_hw { + for (index = 0, element = &array[index]; index < size; index++, element = &array[index]) + + #define for_each_vdma_channel(engine, channel, channel_index) \ +- _for_each_element_array(engine->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ ++ _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \ + channel, channel_index) + +-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size, +- u8 data_id); +- + /** + * Program the given descriptors list to map the given buffer. + * +--- a/drivers/media/pci/hailo/src/fops.c ++++ b/drivers/media/pci/hailo/src/fops.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include +@@ -19,14 +19,14 @@ + #include + #endif + +-#include "utils.h" + #include "fops.h" + #include "vdma_common.h" + #include "utils/logs.h" + #include "vdma/memory.h" + #include "vdma/ioctl.h" + #include "utils/compact.h" +-#include "pci_soc_ioctl.h" ++#include "nnc.h" ++#include "soc.h" + + + #if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 ) +@@ -48,13 +48,6 @@ + // On pcie driver there is only one dma engine + #define DEFAULT_VDMA_ENGINE_INDEX (0) + +-#if !defined(HAILO_EMULATOR) +-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) +-#else /* !defined(HAILO_EMULATOR) */ +-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) +-#endif /* !defined(HAILO_EMULATOR) */ +- +-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp); + + static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp) + { +@@ -124,7 +117,7 @@ int hailo_pcie_fops_open(struct inode *i + + previous_power_state = pBoard->pDev->current_state; + if (PCI_D0 != previous_power_state) { +- hailo_info(pBoard, "Waking up board"); ++ hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state); + err = pci_set_power_state(pBoard->pDev, PCI_D0); + if (err < 0) { + hailo_err(pBoard, "Failed waking up board %d", err); +@@ -148,7 +141,11 @@ int hailo_pcie_fops_open(struct inode *i + interrupts_enabled_by_filp = true; + } + +- err = hailo_add_notification_wait(pBoard, filp); ++ if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ err = hailo_nnc_file_context_init(pBoard, context); ++ } else { ++ err = hailo_soc_file_context_init(pBoard, context); ++ } + if (err < 0) { + goto l_release_irq; + } +@@ -166,6 +163,7 @@ l_release_irq: + + l_revert_power_state: + if (pBoard->pDev->current_state != previous_power_state) { ++ hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state); + if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) { + hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state); + } +@@ -180,34 +178,6 @@ l_exit: + return err; + } + +-int hailo_pcie_driver_down(struct hailo_pcie_board *board) +-{ +- long completion_result = 0; +- int err = 0; +- +- reinit_completion(&board->driver_down.reset_completed); +- +- hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); +- +- // Wait for response +- completion_result = +- wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); +- if (completion_result <= 0) { +- if (0 == completion_result) { +- hailo_err(board, "hailo_pcie_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); +- err = -ETIMEDOUT; +- } else { +- hailo_info(board, "hailo_pcie_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", +- completion_result); +- err = completion_result; +- } +- goto l_exit; +- } +- +-l_exit: +- return err; +-} +- + int hailo_pcie_fops_release(struct inode *inode, struct file *filp) + { + struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data; +@@ -234,12 +204,10 @@ int hailo_pcie_fops_release(struct inode + hailo_err(board, "Invalid file context\n"); + } + +- hailo_pcie_clear_notification_wait_list(board, filp); +- +- if (filp == board->vdma.used_by_filp) { +- if (hailo_pcie_driver_down(board)) { +- hailo_err(board, "Failed sending FW shutdown event"); +- } ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ hailo_nnc_file_context_finalize(board, context); ++ } else { ++ hailo_soc_file_context_finalize(board, context); + } + + hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp); +@@ -250,6 +218,7 @@ int hailo_pcie_fops_release(struct inode + hailo_disable_interrupts(board); + + if (power_mode_enabled()) { ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) { + hailo_err(board, "Failed setting power state to D3hot"); + } +@@ -301,44 +270,23 @@ static long hailo_memory_transfer_ioctl( + return err; + } + +-static long hailo_read_log_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg) +-{ +- long err = 0; +- struct hailo_read_log_params params; +- +- if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { +- hailo_err(pBoard, "HAILO_READ_LOG, copy_from_user fail\n"); +- return -ENOMEM; +- } +- +- if (0 > (err = hailo_pcie_read_firmware_log(&pBoard->pcie_resources, ¶ms))) { +- hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); +- return err; +- } +- +- if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { +- return -ENOMEM; +- } +- +- return 0; +-} +- + static void firmware_notification_irq_handler(struct hailo_pcie_board *board) + { + struct hailo_notification_wait *notif_wait_cursor = NULL; + int err = 0; + unsigned long irq_saved_flags = 0; + +- spin_lock_irqsave(&board->notification_read_spinlock, irq_saved_flags); +- err = hailo_pcie_read_firmware_notification(&board->pcie_resources, &board->notification_cache); +- spin_unlock_irqrestore(&board->notification_read_spinlock, irq_saved_flags); ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); + + if (err < 0) { + hailo_err(board, "Failed reading firmware notification"); + } + else { ++ // TODO: HRT-14502 move interrupt handling to nnc + rcu_read_lock(); +- list_for_each_entry_rcu(notif_wait_cursor, &board->notification_wait_list, notification_wait_list) ++ list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list) + { + complete(¬if_wait_cursor->notification_completion); + } +@@ -374,7 +322,7 @@ irqreturn_t hailo_irqhandler(int irq, vo + + // wake fw_control if needed + if (irq_source.interrupt_bitmask & FW_CONTROL) { +- complete(&board->fw_control.completion); ++ complete(&board->nnc.fw_control.completion); + } + + // wake driver_down if needed +@@ -392,7 +340,14 @@ irqreturn_t hailo_irqhandler(int irq, vo + } + + if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) { +- complete_all(&board->soc_connect_accepted); ++ complete_all(&board->soc.control_resp_ready); ++ } ++ ++ if (irq_source.interrupt_bitmask & SOC_CLOSED_IRQ) { ++ hailo_info(board, "hailo_irqhandler - SOC_CLOSED_IRQ\n"); ++ // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. ++ hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], ++ 0xFFFFFFFF); + } + + if (0 != irq_source.vdma_channels_bitmap) { +@@ -404,170 +359,11 @@ irqreturn_t hailo_irqhandler(int irq, vo + return return_value; + } + +-static long hailo_get_notification_wait_thread(struct hailo_pcie_board *pBoard, struct file *filp, +- struct hailo_notification_wait **current_waiting_thread) +-{ +- struct hailo_notification_wait *cursor = NULL; +- // note: safe to access without rcu because the notification_wait_list is closed only on file release +- list_for_each_entry(cursor, &pBoard->notification_wait_list, notification_wait_list) +- { +- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { +- *current_waiting_thread = cursor; +- return 0; +- } +- } +- +- return -EFAULT; +-} +- +-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp) +-{ +- struct hailo_notification_wait *new_notification_wait = NULL; +- if (!(new_notification_wait = kmalloc(sizeof(*new_notification_wait), GFP_KERNEL))) { +- hailo_err(board, "Failed to allocate notification wait structure.\n"); +- return -ENOMEM; +- } +- new_notification_wait->tgid = current->tgid; +- new_notification_wait->filp = filp; +- new_notification_wait->is_disabled = false; +- init_completion(&new_notification_wait->notification_completion); +- list_add_rcu(&new_notification_wait->notification_wait_list, &board->notification_wait_list); +- return 0; +-} +- +-static long hailo_read_notification_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg, struct file *filp, +- bool* should_up_board_mutex) +-{ +- long err = 0; +- struct hailo_notification_wait *current_waiting_thread = NULL; +- struct hailo_d2h_notification *notification = &pBoard->notification_to_user; +- unsigned long irq_saved_flags; +- +- err = hailo_get_notification_wait_thread(pBoard, filp, ¤t_waiting_thread); +- if (0 != err) { +- goto l_exit; +- } +- up(&pBoard->mutex); +- +- if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { +- hailo_info(pBoard, +- "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", +- err, current_waiting_thread->tgid); +- *should_up_board_mutex = false; +- goto l_exit; +- } +- +- if (down_interruptible(&pBoard->mutex)) { +- hailo_info(pBoard, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); +- *should_up_board_mutex = false; +- err = -ERESTARTSYS; +- goto l_exit; +- } +- +- // Check if was disabled +- if (current_waiting_thread->is_disabled) { +- hailo_info(pBoard, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); +- err = -EINVAL; +- goto l_exit; +- } +- +- reinit_completion(¤t_waiting_thread->notification_completion); +- +- spin_lock_irqsave(&pBoard->notification_read_spinlock, irq_saved_flags); +- notification->buffer_len = pBoard->notification_cache.buffer_len; +- memcpy(notification->buffer, pBoard->notification_cache.buffer, notification->buffer_len); +- spin_unlock_irqrestore(&pBoard->notification_read_spinlock, irq_saved_flags); +- +- if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { +- hailo_err(pBoard, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +-l_exit: +- return err; +-} +- +-static long hailo_disable_notification(struct hailo_pcie_board *pBoard, struct file *filp) +-{ +- struct hailo_notification_wait *cursor = NULL; +- +- hailo_info(pBoard, "HAILO_DISABLE_NOTIFICATION: disable notification"); +- rcu_read_lock(); +- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { +- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { +- cursor->is_disabled = true; +- complete(&cursor->notification_completion); +- break; +- } +- } +- rcu_read_unlock(); +- +- return 0; +-} +- +-static int hailo_fw_control(struct hailo_pcie_board *pBoard, unsigned long arg, bool* should_up_board_mutex) +-{ +- struct hailo_fw_control *command = &pBoard->fw_control.command; +- long completion_result = 0; +- int err = 0; +- +- up(&pBoard->mutex); +- *should_up_board_mutex = false; +- +- if (down_interruptible(&pBoard->fw_control.mutex)) { +- hailo_info(pBoard, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); +- return -ERESTARTSYS; +- } +- +- if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { +- hailo_err(pBoard, "hailo_fw_control, copy_from_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +- reinit_completion(&pBoard->fw_control.completion); +- +- err = hailo_pcie_write_firmware_control(&pBoard->pcie_resources, command); +- if (err < 0) { +- hailo_err(pBoard, "Failed writing fw control to pcie\n"); +- goto l_exit; +- } +- +- // Wait for response +- completion_result = wait_for_completion_interruptible_timeout(&pBoard->fw_control.completion, msecs_to_jiffies(command->timeout_ms)); +- if (completion_result <= 0) { +- if (0 == completion_result) { +- hailo_err(pBoard, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); +- err = -ETIMEDOUT; +- } else { +- hailo_info(pBoard, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); +- err = -EINTR; +- } +- goto l_exit; +- } +- +- err = hailo_pcie_read_firmware_control(&pBoard->pcie_resources, command); +- if (err < 0) { +- hailo_err(pBoard, "Failed reading fw control from pcie\n"); +- goto l_exit; +- } +- +- if (copy_to_user((void __user*)arg, command, sizeof(*command))) { +- hailo_err(pBoard, "hailo_fw_control, copy_to_user fail\n"); +- err = -ENOMEM; +- goto l_exit; +- } +- +-l_exit: +- up(&pBoard->fw_control.mutex); +- return err; +-} +- + static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg) + { + struct hailo_device_properties props = { + .desc_max_page_size = board->desc_max_page_size, ++ .board_type = board->pcie_resources.board_type, + .allocation_mode = board->allocation_mode, + .dma_type = HAILO_DMA_TYPE_PCIE, + .dma_engines_count = board->vdma.vdma_engines_count, +@@ -618,24 +414,6 @@ static long hailo_general_ioctl(struct h + } + } + +-static long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, +- struct file *filp, bool *should_up_board_mutex) +-{ +- switch (cmd) { +- case HAILO_FW_CONTROL: +- return hailo_fw_control(board, arg, should_up_board_mutex); +- case HAILO_READ_NOTIFICATION: +- return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); +- case HAILO_DISABLE_NOTIFICATION: +- return hailo_disable_notification(board, filp); +- case HAILO_READ_LOG: +- return hailo_read_log_ioctl(board, arg); +- default: +- hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); +- return -ENOTTY; +- } +-} +- + long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg) + { + long err = 0; +@@ -694,7 +472,7 @@ long hailo_pcie_fops_unlockedioctl(struc + hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd)); + err = -EINVAL; + } else { +- err = hailo_soc_ioctl(board, &context->vdma_context, &board->vdma, cmd, arg); ++ err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg); + } + break; + case HAILO_NNC_IOCTL_MAGIC: +--- a/drivers/media/pci/hailo/src/fops.h ++++ b/drivers/media/pci/hailo/src/fops.h +@@ -1,16 +1,17 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_FOPS_H_ + #define _HAILO_PCI_FOPS_H_ + ++#include "pcie.h" ++ + int hailo_pcie_fops_open(struct inode* inode, struct file* filp); + int hailo_pcie_fops_release(struct inode* inode, struct file* filp); + long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg); + int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma); +-int hailo_pcie_driver_down(struct hailo_pcie_board *board); + void hailo_pcie_ep_init(struct hailo_pcie_board *board); + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +--- /dev/null ++++ b/drivers/media/pci/hailo/src/nnc.c +@@ -0,0 +1,299 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW. ++ * The device supports sending controls, receiving notification and reading the FW log. ++ */ ++ ++#include "nnc.h" ++#include "hailo_ioctl_common.h" ++ ++#include "utils/logs.h" ++#include "utils/compact.h" ++ ++#include ++ ++#if !defined(HAILO_EMULATOR) ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5) ++#else /* !defined(HAILO_EMULATOR) */ ++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000) ++#endif /* !defined(HAILO_EMULATOR) */ ++ ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc) ++{ ++ sema_init(&nnc->fw_control.mutex, 1); ++ spin_lock_init(&nnc->notification_read_spinlock); ++ init_completion(&nnc->fw_control.completion); ++ INIT_LIST_HEAD(&nnc->notification_wait_list); ++ memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache)); ++} ++ ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); ++ } ++ rcu_read_unlock(); ++} ++ ++static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex) ++{ ++ struct hailo_fw_control *command = &board->nnc.fw_control.command; ++ long completion_result = 0; ++ int err = 0; ++ ++ up(&board->mutex); ++ *should_up_board_mutex = false; ++ ++ if (down_interruptible(&board->nnc.fw_control.mutex)) { ++ hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid); ++ return -ERESTARTSYS; ++ } ++ ++ if (copy_from_user(command, (void __user*)arg, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_from_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++ reinit_completion(&board->nnc.fw_control.completion); ++ ++ err = hailo_pcie_write_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed writing fw control to pcie\n"); ++ goto l_exit; ++ } ++ ++ // Wait for response ++ completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms)); ++ if (completion_result <= 0) { ++ if (0 == completion_result) { ++ hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms); ++ err = -ETIMEDOUT; ++ } else { ++ hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result); ++ err = -EINTR; ++ } ++ goto l_exit; ++ } ++ ++ err = hailo_pcie_read_firmware_control(&board->pcie_resources, command); ++ if (err < 0) { ++ hailo_err(board, "Failed reading fw control from pcie\n"); ++ goto l_exit; ++ } ++ ++ if (copy_to_user((void __user*)arg, command, sizeof(*command))) { ++ hailo_err(board, "hailo_fw_control, copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++l_exit: ++ up(&board->nnc.fw_control.mutex); ++ return err; ++} ++ ++static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp, ++ struct hailo_notification_wait **current_waiting_thread) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ // note: safe to access without rcu because the notification_wait_list is closed only on file release ++ list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list) ++ { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ *current_waiting_thread = cursor; ++ return 0; ++ } ++ } ++ ++ return -EFAULT; ++} ++ ++static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp, ++ bool* should_up_board_mutex) ++{ ++ long err = 0; ++ struct hailo_notification_wait *current_waiting_thread = NULL; ++ struct hailo_d2h_notification *notification = &board->nnc.notification_to_user; ++ unsigned long irq_saved_flags; ++ ++ err = hailo_get_notification_wait_thread(board, filp, ¤t_waiting_thread); ++ if (0 != err) { ++ goto l_exit; ++ } ++ up(&board->mutex); ++ ++ if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) { ++ hailo_info(board, ++ "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n", ++ err, current_waiting_thread->tgid); ++ *should_up_board_mutex = false; ++ goto l_exit; ++ } ++ ++ if (down_interruptible(&board->mutex)) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n"); ++ *should_up_board_mutex = false; ++ err = -ERESTARTSYS; ++ goto l_exit; ++ } ++ ++ // Check if was disabled ++ if (current_waiting_thread->is_disabled) { ++ hailo_info(board, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); ++ err = -EINVAL; ++ goto l_exit; ++ } ++ ++ reinit_completion(¤t_waiting_thread->notification_completion); ++ ++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ notification->buffer_len = board->nnc.notification_cache.buffer_len; ++ memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len); ++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags); ++ ++ if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) { ++ hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n"); ++ err = -ENOMEM; ++ goto l_exit; ++ } ++ ++l_exit: ++ return err; ++} ++ ++static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cursor = NULL; ++ ++ hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification"); ++ rcu_read_lock(); ++ list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) { ++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) { ++ cursor->is_disabled = true; ++ complete(&cursor->notification_completion); ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg) ++{ ++ long err = 0; ++ struct hailo_read_log_params params; ++ ++ if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) { ++ hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, ¶ms))) { ++ hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err); ++ return err; ++ } ++ ++ if (copy_to_user((void*)arg, ¶ms, sizeof(params))) { ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex) ++{ ++ switch (cmd) { ++ case HAILO_FW_CONTROL: ++ return hailo_fw_control(board, arg, should_up_board_mutex); ++ case HAILO_READ_NOTIFICATION: ++ return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex); ++ case HAILO_DISABLE_NOTIFICATION: ++ return hailo_disable_notification(board, filp); ++ case HAILO_READ_LOG: ++ return hailo_read_log_ioctl(board, arg); ++ default: ++ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; ++ } ++} ++ ++ ++static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL); ++ if (!wait) { ++ hailo_err(board, "Failed to allocate notification wait structure.\n"); ++ return -ENOMEM; ++ } ++ wait->tgid = current->tgid; ++ wait->filp = filp; ++ wait->is_disabled = false; ++ init_completion(&wait->notification_completion); ++ list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list); ++ return 0; ++} ++ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ return add_notification_wait(board, context->filp); ++} ++ ++static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp) ++{ ++ struct hailo_notification_wait *cur = NULL, *next = NULL; ++ list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) { ++ if (cur->filp == filp) { ++ list_del_rcu(&cur->notification_wait_list); ++ synchronize_rcu(); ++ kfree(cur); ++ } ++ } ++} ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board) ++{ ++ long completion_result = 0; ++ int err = 0; ++ ++ reinit_completion(&board->driver_down.reset_completed); ++ ++ hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources); ++ ++ // Wait for response ++ completion_result = ++ wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS)); ++ if (completion_result <= 0) { ++ if (0 == completion_result) { ++ hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS); ++ err = -ETIMEDOUT; ++ } else { ++ hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n", ++ completion_result); ++ err = completion_result; ++ } ++ goto l_exit; ++ } ++ ++l_exit: ++ return err; ++} ++ ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ clear_notification_wait_list(board, context->filp); ++ ++ if (context->filp == board->vdma.used_by_filp) { ++ hailo_nnc_driver_down(board); ++ } ++} +\ No newline at end of file +--- /dev/null ++++ b/drivers/media/pci/hailo/src/nnc.h +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++ ++#ifndef _HAILO_PCI_NNC_H_ ++#define _HAILO_PCI_NNC_H_ ++ ++#include "pcie.h" ++ ++void hailo_nnc_init(struct hailo_pcie_nnc *nnc); ++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc); ++ ++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg, ++ struct file *filp, bool *should_up_board_mutex); ++ ++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_nnc_driver_down(struct hailo_pcie_board *board); ++ ++#endif /* _HAILO_PCI_NNC_H_ */ +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.c ++++ /dev/null +@@ -1,155 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. +- **/ +-#include "pci_soc_ioctl.h" +- +-#include "utils.h" +-#include "vdma_common.h" +-#include "utils/logs.h" +-#include "vdma/memory.h" +- +-#define PCI_SOC_VDMA_ENGINE_INDEX (0) +-#define PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS (10000) +- +-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) +-{ +- switch (cmd) { +- case HAILO_SOC_CONNECT: +- return hailo_soc_connect_ioctl(board, context, controller, arg); +- case HAILO_SOC_CLOSE: +- return hailo_soc_close_ioctl(board, controller, arg); +- default: +- hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); +- return -ENOTTY; +- } +-} +- +-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned long arg) +-{ +- struct hailo_soc_connect_params params; +- struct hailo_vdma_channel *input_channel = NULL; +- struct hailo_vdma_channel *output_channel = NULL; +- struct hailo_vdma_engine *vdma_engine = NULL; +- struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; +- struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; +- uint8_t depth = 0; +- int err = 0; +- long completion_result = 0; +- +- if (copy_from_user(¶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 +--- a/drivers/media/pci/hailo/src/pcie.c ++++ b/drivers/media/pci/hailo/src/pcie.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include +@@ -22,6 +22,8 @@ + + #include "hailo_ioctl_common.h" + #include "pcie.h" ++#include "nnc.h" ++#include "soc.h" + #include "fops.h" + #include "sysfs.h" + #include "utils/logs.h" +@@ -40,11 +42,12 @@ enum hailo_allocate_driver_buffer_driver + HAILO_FORCE_BUFFER_FROM_DRIVER = 2, + }; + +-//Debug flag ++// Debug flag + static int force_desc_page_size = 0; + static bool g_is_power_mode_enabled = true; + static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER; + static bool force_hailo15_legacy_mode = false; ++static bool force_boot_linux_from_eemc = false; + + #define DEVICE_NODE_NAME "hailo" + static int char_major = 0; +@@ -206,7 +209,7 @@ static int hailo_pcie_disable_aspm(struc + /* Double-check ASPM control. If not disabled by the above, the + * BIOS is preventing that from happening (or CONFIG_PCIEASPM is + * not enabled); override by writing PCI config space directly. +- */ ++ */ + err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc); + if (err < 0) { + hailo_err(board, "Couldn't read LNKCTL capability\n"); +@@ -288,101 +291,59 @@ static void hailo_pcie_remove_board(stru + up(&g_hailo_add_board_mutex); + } + +-static int hailo_write_config(struct hailo_pcie_resources *resources, struct device *dev, +- const struct hailo_config_constants *config_consts) +-{ +- const struct firmware *config = NULL; +- int err = 0; +- +- if (NULL == config_consts->filename) { +- // Config not supported for platform +- return 0; +- } +- +- err = request_firmware_direct(&config, config_consts->filename, dev); +- if (err < 0) { +- hailo_dev_info(dev, "Config %s not found\n", config_consts->filename); +- return 0; +- } +- +- hailo_dev_notice(dev, "Writing config %s\n", config_consts->filename); +- +- err = hailo_pcie_write_config_common(resources, config->data, config->size, config_consts); +- if (err < 0) { +- if (-EINVAL == err) { +- hailo_dev_warn(dev, "Config size %zu is bigger than max %zu\n", config->size, config_consts->max_size); +- } +- release_firmware(config); +- return err; +- } +- +- release_firmware(config); +- return 0; +-} +- + static bool wait_for_firmware_completion(struct completion *fw_load_completion) + { + return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS))); + } + +-static int hailo_load_firmware(struct hailo_pcie_resources *resources, ++static int hailo_load_soc_firmware(struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) + { +- const struct firmware *firmware = NULL; +- int err = 0; + u32 boot_status = 0; ++ int err = 0; ++ u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE; + + if (hailo_pcie_is_firmware_loaded(resources)) { +- hailo_dev_warn(dev, "Firmware was already loaded\n"); ++ hailo_dev_warn(dev, "Firmware batch was already loaded\n"); + return 0; + } + +- reinit_completion(fw_load_completion); +- +- err = hailo_write_config(resources, dev, hailo_pcie_get_board_config_constants(resources->board_type)); +- if (err < 0) { +- hailo_dev_err(dev, "Failed writing board config"); +- return err; +- } ++ init_completion(fw_load_completion); + +- err = hailo_write_config(resources, dev, hailo_pcie_get_user_config_constants(resources->board_type)); ++ err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing fw config"); ++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); + return err; + } + +- // read firmware file +- err = request_firmware_direct(&firmware, hailo_pcie_get_fw_filename(resources->board_type), dev); +- if (err < 0) { +- hailo_dev_warn(dev, "Firmware file not found (/lib/firmware/%s), please upload the firmware manually \n", +- hailo_pcie_get_fw_filename(resources->board_type)); +- return 0; ++ if (!wait_for_firmware_completion(fw_load_completion)) { ++ boot_status = hailo_get_boot_status(resources); ++ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ return -ETIMEDOUT; + } ++ reinit_completion(fw_load_completion); + +- err = hailo_pcie_write_firmware(resources, firmware->data, firmware->size); ++ err = hailo_pcie_write_firmware_batch(dev, resources, second_stage); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware. err %d\n", err); +- release_firmware(firmware); ++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); + return err; + } + +- release_firmware(firmware); +- + if (!wait_for_firmware_completion(fw_load_completion)) { + boot_status = hailo_get_boot_status(resources); + hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } + +- hailo_dev_notice(dev, "Firmware was loaded successfully\n"); ++ hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ + return 0; + } + +-static int hailo_load_firmware_batch(struct hailo_pcie_resources *resources, ++static int hailo_load_nnc_firmware(struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) + { + u32 boot_status = 0; +- u32 pcie_finished = 1; + int err = 0; + + if (hailo_pcie_is_firmware_loaded(resources)) { +@@ -398,31 +359,13 @@ static int hailo_load_firmware_batch(str + return err; + } + +- hailo_trigger_firmware_boot(resources); +- + if (!wait_for_firmware_completion(fw_load_completion)) { + boot_status = hailo_get_boot_status(resources); + hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } +- reinit_completion(fw_load_completion); + +- err = hailo_pcie_write_firmware_batch(dev, resources, SECOND_STAGE); +- if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); +- return err; +- } +- +- // TODO: HRT-13838 - Remove, move address to compat, make write_memory static +- write_memory(resources, 0x84000000, (void*)&pcie_finished, sizeof(pcie_finished)); +- +- if (!wait_for_firmware_completion(fw_load_completion)) { +- boot_status = hailo_get_boot_status(resources); +- hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); +- return -ETIMEDOUT; +- } +- +- hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ hailo_dev_notice(dev, "Firmware loaded successfully\n"); + + return 0; + } +@@ -439,15 +382,13 @@ static int hailo_activate_board(struct h + return err; + } + +- switch (board->pcie_resources.board_type) { +- case HAILO_BOARD_TYPE_HAILO10H: +- err = hailo_load_firmware_batch(&board->pcie_resources, &board->pDev->dev, ++ switch (board->pcie_resources.accelerator_type) { ++ case HAILO_ACCELERATOR_TYPE_SOC: ++ err = hailo_load_soc_firmware(&board->pcie_resources, &board->pDev->dev, + &board->fw_loaded_completion); + break; +- case HAILO_BOARD_TYPE_HAILO10H_LEGACY: +- case HAILO_BOARD_TYPE_PLUTO: +- case HAILO_BOARD_TYPE_HAILO8: +- err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev, ++ case HAILO_ACCELERATOR_TYPE_NNC: ++ err = hailo_load_nnc_firmware(&board->pcie_resources, &board->pDev->dev, + &board->fw_loaded_completion); + break; + default: +@@ -464,6 +405,7 @@ static int hailo_activate_board(struct h + + if (power_mode_enabled()) { + // Setting the device to low power state, until the user opens the device ++ hailo_info(board, "Power change state to PCI_D3hot\n"); + err = pci_set_power_state(board->pDev, PCI_D3hot); + if (err < 0) { + hailo_err(board, "Set power state failed %d\n", err); +@@ -755,21 +697,17 @@ static int hailo_pcie_probe(struct pci_d + + pBoard->interrupts_enabled = false; + init_completion(&pBoard->fw_loaded_completion); +- init_completion(&pBoard->soc_connect_accepted); + + sema_init(&pBoard->mutex, 1); + atomic_set(&pBoard->ref_count, 0); + INIT_LIST_HEAD(&pBoard->open_files_list); + +- sema_init(&pBoard->fw_control.mutex, 1); +- spin_lock_init(&pBoard->notification_read_spinlock); +- init_completion(&pBoard->fw_control.completion); ++ // Init both soc and nnc, since the interrupts are shared. ++ hailo_nnc_init(&pBoard->nnc); ++ hailo_soc_init(&pBoard->soc); + + init_completion(&pBoard->driver_down.reset_completed); + +- INIT_LIST_HEAD(&pBoard->notification_wait_list); +- +- memset(&pBoard->notification_cache, 0, sizeof(pBoard->notification_cache)); + memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params)); + + err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev, +@@ -832,7 +770,6 @@ probe_exit: + static void hailo_pcie_remove(struct pci_dev* pDev) + { + struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev); +- struct hailo_notification_wait *cursor = NULL; + + pci_notice(pDev, "Remove: Releasing board\n"); + +@@ -864,13 +801,7 @@ static void hailo_pcie_remove(struct pci + + pci_set_drvdata(pDev, NULL); + +- // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed +- rcu_read_lock(); +- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) { +- cursor->is_disabled = true; +- complete(&cursor->notification_completion); +- } +- rcu_read_unlock(); ++ hailo_nnc_finalize(&pBoard->nnc); + + up(&pBoard->mutex); + +@@ -889,6 +820,15 @@ static void hailo_pcie_remove(struct pci + + } + ++inline int driver_down(struct hailo_pcie_board *board) ++{ ++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) { ++ return hailo_nnc_driver_down(board); ++ } else { ++ return hailo_soc_driver_down(board); ++ } ++} ++ + #ifdef CONFIG_PM_SLEEP + static int hailo_pcie_suspend(struct device *dev) + { +@@ -899,17 +839,16 @@ static int hailo_pcie_suspend(struct dev + // lock board to wait for any pending operations + down(&board->mutex); + +- // Disable all interrupts. All interrupts from Hailo chip would be masked. +- hailo_disable_interrupts(board); +- +- // Close all vDMA channels + if (board->vdma.used_by_filp != NULL) { +- err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + dev_notice(dev, "Error while trying to call FW to close vdma channels\n"); + } + } + ++ // Disable all interrupts. All interrupts from Hailo chip would be masked. ++ hailo_disable_interrupts(board); ++ + // Un validate all activae file contexts so every new action would return error to the user. + list_for_each_entry(cur, &board->open_files_list, open_files_list) { + cur->is_valid = false; +@@ -919,8 +858,8 @@ static int hailo_pcie_suspend(struct dev + up(&board->mutex); + + dev_notice(dev, "PM's suspend\n"); +- // Continue system suspend +- return err; ++ // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly) ++ return 0; + } + + static int hailo_pcie_resume(struct device *dev) +@@ -930,10 +869,10 @@ static int hailo_pcie_resume(struct devi + + if ((err = hailo_activate_board(board)) < 0) { + dev_err(dev, "Failed activating board %d\n", err); +- return err; + } + + dev_notice(dev, "PM's resume\n"); ++ // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly) + return 0; + } + #endif /* CONFIG_PM_SLEEP */ +@@ -954,7 +893,7 @@ static void hailo_pci_reset_prepare(stru + down(&board->mutex); + if (board->vdma.used_by_filp != NULL) { + // Try to close all vDMA channels before reset +- err = hailo_pcie_driver_down(board); ++ err = driver_down(board); + if (err < 0) { + pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err); + } +@@ -1088,6 +1027,9 @@ MODULE_PARM_DESC(force_desc_page_size, " + module_param(force_hailo15_legacy_mode, bool, S_IRUGO); + MODULE_PARM_DESC(force_hailo15_legacy_mode, "Forces work with Hailo15 in legacy mode(relevant for emulators)"); + ++module_param(force_boot_linux_from_eemc, bool, S_IRUGO); ++MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)"); ++ + MODULE_AUTHOR("Hailo Technologies Ltd."); + MODULE_DESCRIPTION("Hailo PCIe driver"); + MODULE_LICENSE("GPL v2"); +--- a/drivers/media/pci/hailo/src/pcie.h ++++ b/drivers/media/pci/hailo/src/pcie.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_PCIE_H_ +@@ -41,6 +41,19 @@ struct hailo_fw_boot { + }; + + ++struct hailo_pcie_nnc { ++ struct hailo_fw_control_info fw_control; ++ ++ spinlock_t notification_read_spinlock; ++ struct list_head notification_wait_list; ++ struct hailo_d2h_notification notification_cache; ++ struct hailo_d2h_notification notification_to_user; ++}; ++ ++struct hailo_pcie_soc { ++ struct completion control_resp_ready; ++}; ++ + // Context for each open file handle + // TODO: store board and use as actual context + struct hailo_file_context { +@@ -48,6 +61,7 @@ struct hailo_file_context { + struct file *filp; + struct hailo_vdma_file_context vdma_context; + bool is_valid; ++ u32 soc_used_channels_bitmap; + }; + + struct hailo_pcie_board { +@@ -57,21 +71,17 @@ struct hailo_pcie_board { + atomic_t ref_count; + struct list_head open_files_list; + struct hailo_pcie_resources pcie_resources; +- struct hailo_fw_control_info fw_control; ++ struct hailo_pcie_nnc nnc; ++ struct hailo_pcie_soc soc; + struct hailo_pcie_driver_down_info driver_down; + struct semaphore mutex; + struct hailo_vdma_controller vdma; +- spinlock_t notification_read_spinlock; +- struct list_head notification_wait_list; +- struct hailo_d2h_notification notification_cache; +- struct hailo_d2h_notification notification_to_user; ++ + struct hailo_memory_transfer_params memory_transfer_params; + u32 desc_max_page_size; + enum hailo_allocation_mode allocation_mode; + struct completion fw_loaded_completion; + bool interrupts_enabled; +- // Only needed in accelerator type soc +- struct completion soc_connect_accepted; + }; + + bool power_mode_enabled(void); +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.c +@@ -0,0 +1,244 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++/** ++ * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and ++ * some application processor (pci_ep). ++ */ ++ ++#include "soc.h" ++ ++#include "vdma_common.h" ++#include "utils/logs.h" ++#include "vdma/memory.h" ++ ++#include ++ ++#define PCI_SOC_VDMA_ENGINE_INDEX (0) ++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000) ++#define PCI_SOC_INPUT_CHANNEL_BITMASK (0x000000FF) ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc) ++{ ++ init_completion(&soc->control_resp_ready); ++} ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case HAILO_SOC_CONNECT: ++ return hailo_soc_connect_ioctl(board, context, controller, arg); ++ case HAILO_SOC_CLOSE: ++ return hailo_soc_close_ioctl(board, controller, context, arg); ++ default: ++ hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd)); ++ return -ENOTTY; ++ } ++} ++ ++static int soc_control(struct hailo_pcie_board *board, ++ const struct hailo_pcie_soc_request *request, ++ struct hailo_pcie_soc_response *response) ++{ ++ int ret = 0; ++ reinit_completion(&board->soc.control_resp_ready); ++ ++ hailo_pcie_soc_write_request(&board->pcie_resources, request); ++ ++ ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready, ++ msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS)); ++ if (ret <= 0) { ++ if (0 == ret) { ++ hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS); ++ return -ETIMEDOUT; ++ } else { ++ hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ hailo_pcie_soc_read_response(&board->pcie_resources, response); ++ ++ if (response->status < 0) { ++ hailo_err(board, "soc control failed with status=%d\n", response->status); ++ return response->status; ++ } ++ ++ if (response->control_code != request->control_code) { ++ hailo_err(board, "Invalid response control code %d (expected %d)\n", ++ response->control_code, request->control_code); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_soc_connect_params params; ++ struct hailo_vdma_channel *input_channel = NULL; ++ struct hailo_vdma_channel *output_channel = NULL; ++ struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; ++ struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; ++ uint8_t depth = 0; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_err(board, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT, ++ .connect = { ++ .port = params.port_number ++ } ++ }; ++ err = soc_control(board, &request, &response); ++ if (err < 0) { ++ return err; ++ } ++ ++ params.input_channel_index = response.connect.input_channel_index; ++ params.output_channel_index = response.connect.output_channel_index; ++ ++ if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index); ++ return -EINVAL; ++ } ++ ++ input_channel = &vdma_engine->channels[params.input_channel_index]; ++ output_channel = &vdma_engine->channels[params.output_channel_index]; ++ ++ input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle); ++ output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle); ++ if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) { ++ hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n"); ++ return -EINVAL; ++ } ++ ++ if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) || ++ !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) { ++ hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n"); ++ return -EINVAL; ++ } ++ ++ // configure and start input channel ++ depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count); ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index); ++ return -EINVAL; ++ } ++ ++ // Store the input channels state in bitmap (open) ++ hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ ++ // configure and start output channel ++ depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count); ++ // DMA Direction is only to get channel index - so ++ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth, ++ board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index); ++ // Close input channel ++ hailo_vdma_stop_channel(input_channel->host_regs); ++ return -EINVAL; ++ } ++ ++ // Store the output channels state in bitmap (open) ++ hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap) ++{ ++ struct hailo_pcie_soc_request request = {0}; ++ struct hailo_pcie_soc_response response = {0}; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_vdma_channel *channel = NULL; ++ u8 channel_index = 0; ++ ++ hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap); ++ for_each_vdma_channel(engine, channel, channel_index) { ++ if (hailo_test_bit(channel_index, &channels_bitmap)) { ++ hailo_vdma_stop_channel(channel->host_regs); ++ } ++ } ++ ++ request = (struct hailo_pcie_soc_request) { ++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE, ++ .close = { ++ .channels_bitmap = channels_bitmap ++ } ++ }; ++ return soc_control(board, &request, &response); ++} ++ ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, ++ struct hailo_file_context *context, unsigned long arg) ++{ ++ struct hailo_soc_close_params params; ++ u32 channels_bitmap = 0; ++ int err = 0; ++ ++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { ++ hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n"); ++ return -ENOMEM; ++ } ++ ++ // TOOD: check channels are connected ++ ++ channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index); ++ ++ err = close_channels(board, channels_bitmap); ++ if (0 != err) { ++ hailo_dev_err(&board->pDev->dev, "Error closing channels\n"); ++ return err; ++ } ++ ++ // Store the channel state in bitmap (closed) ++ hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap); ++ hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap); ++ ++ return err; ++} ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // Nothing to init yet ++ return 0; ++} ++ ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context) ++{ ++ // close only channels connected by this (by bitmap) ++ if (context->soc_used_channels_bitmap != 0) { ++ close_channels(board, context->soc_used_channels_bitmap); ++ } ++} ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board) ++{ ++ return close_channels(board, 0xFFFFFFFF); ++} +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.h ++++ /dev/null +@@ -1,19 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. +- **/ +- +-#ifndef _HAILO_PCI_SOC_IOCTL_H_ +-#define _HAILO_PCI_SOC_IOCTL_H_ +- +-#include "vdma/ioctl.h" +-#include "pcie.h" +- +- +-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); +-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context, +- struct hailo_vdma_controller *controller, unsigned long arg); +-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg); +- +-#endif // _HAILO_PCI_SOC_IOCTL_H_ +\ No newline at end of file +--- /dev/null ++++ b/drivers/media/pci/hailo/src/soc.h +@@ -0,0 +1,26 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/** ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. ++ **/ ++ ++#ifndef _HAILO_PCI_SOC_IOCTL_H_ ++#define _HAILO_PCI_SOC_IOCTL_H_ ++ ++#include "vdma/ioctl.h" ++#include "pcie.h" ++ ++ ++void hailo_soc_init(struct hailo_pcie_soc *soc); ++ ++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg); ++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context, ++ struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg); ++ ++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context); ++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context); ++ ++int hailo_soc_driver_down(struct hailo_pcie_board *board); ++ ++#endif // _HAILO_PCI_SOC_IOCTL_H_ +\ No newline at end of file +--- a/drivers/media/pci/hailo/src/sysfs.c ++++ b/drivers/media/pci/hailo/src/sysfs.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "sysfs.h" +--- a/drivers/media/pci/hailo/src/sysfs.h ++++ b/drivers/media/pci/hailo/src/sysfs.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_SYSFS_H_ +--- a/drivers/media/pci/hailo/src/utils.c ++++ /dev/null +@@ -1,26 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. +- **/ +- +-#include +-#include +-#include +-#include +- +-#include "pcie.h" +-#include "utils.h" +-#include "utils/logs.h" +- +- +-void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp) +-{ +- struct hailo_notification_wait *cur = NULL, *next = NULL; +- list_for_each_entry_safe(cur, next, &pBoard->notification_wait_list, notification_wait_list) { +- if (cur->filp == filp) { +- list_del_rcu(&cur->notification_wait_list); +- synchronize_rcu(); +- kfree(cur); +- } +- } +-} +--- a/drivers/media/pci/hailo/utils/compact.h ++++ b/drivers/media/pci/hailo/utils/compact.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_PCI_COMPACT_H_ +--- a/drivers/media/pci/hailo/utils/fw_common.h ++++ b/drivers/media/pci/hailo/utils/fw_common.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_LINUX_COMMON_H_ +--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c ++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "integrated_nnc_utils.h" +@@ -43,11 +43,19 @@ int hailo_ioremap_shmem(struct platform_ + void __iomem * remap_ptr; + + shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index); ++ if (!shmem) { ++ hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index); ++ return -ENODEV; ++ } ++ + ret = of_address_to_resource(shmem, 0, &res); + if (ret) { + hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index); ++ of_node_put(shmem); + return ret; + } ++ ++ // Decrement the refcount of the node + of_node_put(shmem); + + remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); +--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h ++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _INTEGRATED_NNC_UTILS_H_ +--- a/drivers/media/pci/hailo/utils/logs.c ++++ b/drivers/media/pci/hailo/utils/logs.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "logs.h" +--- a/drivers/media/pci/hailo/utils/logs.h ++++ b/drivers/media/pci/hailo/utils/logs.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _COMMON_LOGS_H_ +--- a/drivers/media/pci/hailo/vdma/ioctl.c ++++ b/drivers/media/pci/hailo/vdma/ioctl.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #include "ioctl.h" +@@ -12,7 +12,7 @@ + #include + + +-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) + { + struct hailo_vdma_enable_channels_params input; + struct hailo_vdma_engine *engine = NULL; +@@ -40,12 +40,15 @@ long hailo_vdma_enable_channels_ioctl(st + hailo_vdma_update_interrupts_mask(controller, engine_index); + hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the enabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] |= channels_bitmap; + } + + return 0; + } + +-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg) ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context) + { + struct hailo_vdma_disable_channels_params input; + struct hailo_vdma_engine *engine = NULL; +@@ -77,6 +80,9 @@ long hailo_vdma_disable_channels_ioctl(s + + hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n", + engine_index, channels_bitmap); ++ ++ // Update the context with the disabled channels bitmap ++ context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap; + } + + // Wake up threads waiting +@@ -204,7 +210,7 @@ long hailo_vdma_buffer_map_ioctl(struct + return -EFAULT; + } + +- hailo_dev_info(controller->dev, "address %lx tgid %d size: %zu\n", ++ hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n", + buf_info.user_address, current->tgid, buf_info.size); + + direction = get_dma_direction(buf_info.data_direction); +@@ -231,7 +237,7 @@ long hailo_vdma_buffer_map_ioctl(struct + } + + list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list); +- hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n", ++ hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n", + buf_info.user_address, buf_info.mapped_handle); + return 0; + } +@@ -247,7 +253,7 @@ long hailo_vdma_buffer_unmap_ioctl(struc + return -EFAULT; + } + +- hailo_dev_info(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); ++ hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle); + + mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle); + if (mapped_buffer == NULL) { +--- a/drivers/media/pci/hailo/vdma/ioctl.h ++++ b/drivers/media/pci/hailo/vdma/ioctl.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #ifndef _HAILO_VDMA_IOCTL_H_ +@@ -8,8 +8,8 @@ + + #include "vdma/vdma.h" + +-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); +-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg); ++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); ++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context); + long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, + struct semaphore *mutex, bool *should_up_board_mutex); + +--- a/drivers/media/pci/hailo/vdma/memory.c ++++ b/drivers/media/pci/hailo/vdma/memory.c +@@ -1,11 +1,12 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #define pr_fmt(fmt) "hailo: " fmt + + #include "memory.h" ++#include "utils.h" + #include "utils/compact.h" + + #include +@@ -316,6 +317,11 @@ int hailo_desc_list_create(struct device + size_t buffer_size = 0; + const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB (from the VDMA registers documentation) + ++ if (MAX_POWER_OF_2_VALUE < descriptors_count) { ++ dev_err(dev, "Invalid descriptors count %u\n", descriptors_count); ++ return -EINVAL; ++ } ++ + buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor); + buffer_size = ALIGN(buffer_size, align); + +@@ -323,7 +329,7 @@ int hailo_desc_list_create(struct device + &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO); + if (descriptors->kernel_address == NULL) { + dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory " +- "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", ++ "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n", + descriptors_count, buffer_size); + return -ENOMEM; + } +@@ -333,6 +339,8 @@ int hailo_desc_list_create(struct device + + descriptors->desc_list.desc_list = descriptors->kernel_address; + descriptors->desc_list.desc_count = descriptors_count; ++ // No need to check the return value of get_nearest_powerof_2 because we already checked the input ++ descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1); + descriptors->desc_list.desc_page_size = desc_page_size; + descriptors->desc_list.is_circular = is_circular; + +--- a/drivers/media/pci/hailo/vdma/memory.h ++++ b/drivers/media/pci/hailo/vdma/memory.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + /** + * vDMA memory utility (including allocation and mappings) +--- a/drivers/media/pci/hailo/vdma/vdma.c ++++ b/drivers/media/pci/hailo/vdma/vdma.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + + #define pr_fmt(fmt) "hailo: " fmt +@@ -105,6 +105,9 @@ void hailo_vdma_file_context_init(struct + INIT_LIST_HEAD(&context->descriptors_buffer_list); + INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list); + INIT_LIST_HEAD(&context->continuous_buffer_list); ++ ++ BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE, ++ "Unexpected amount of VDMA channels per engine"); + } + + void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller, +@@ -119,21 +122,22 @@ void hailo_vdma_file_context_finalize(st + { + size_t engine_index = 0; + struct hailo_vdma_engine *engine = NULL; +- const u32 channels_bitmap = 0xFFFFFFFF; // disable all channel interrupts + unsigned long irq_saved_flags = 0; + // In case of FLR, the vdma registers will be NULL + const bool is_device_up = (NULL != controller->dev); + +- if (filp == controller->used_by_filp) { +- for_each_vdma_engine(controller, engine, engine_index) { +- hailo_vdma_engine_disable_channels(engine, channels_bitmap); ++ for_each_vdma_engine(controller, engine, engine_index) { ++ if (context->enabled_channels_bitmap[engine_index]) { ++ hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index, ++ context->enabled_channels_bitmap[engine_index]); ++ hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]); + + if (is_device_up) { + hailo_vdma_update_interrupts_mask(controller, engine_index); + } + + spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); +- hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap); ++ hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]); + spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); + } + } +@@ -148,10 +152,21 @@ void hailo_vdma_file_context_finalize(st + } + } + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap) ++{ ++ unsigned long irq_saved_flags = 0; ++ ++ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); ++ hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); ++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); ++ ++ wake_up_interruptible_all(&controller->interrupts_wq); ++} ++ + void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, + size_t engine_index, u32 channels_bitmap) + { +- unsigned long irq_saved_flags = 0; + struct hailo_vdma_engine *engine = NULL; + + BUG_ON(engine_index >= controller->vdma_engines_count); +@@ -159,11 +174,7 @@ void hailo_vdma_irq_handler(struct hailo + + hailo_vdma_engine_push_timestamps(engine, channels_bitmap); + +- spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags); +- hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap); +- spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags); +- +- wake_up_interruptible_all(&controller->interrupts_wq); ++ hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap); + } + + long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller, +@@ -171,9 +182,9 @@ long hailo_vdma_ioctl(struct hailo_vdma_ + { + switch (cmd) { + case HAILO_VDMA_ENABLE_CHANNELS: +- return hailo_vdma_enable_channels_ioctl(controller, arg); ++ return hailo_vdma_enable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_DISABLE_CHANNELS: +- return hailo_vdma_disable_channels_ioctl(controller, arg); ++ return hailo_vdma_disable_channels_ioctl(controller, arg, context); + case HAILO_VDMA_INTERRUPTS_WAIT: + return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex); + case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS: +--- a/drivers/media/pci/hailo/vdma/vdma.h ++++ b/drivers/media/pci/hailo/vdma/vdma.h +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /** +- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved. ++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved. + **/ + /** + * Hailo vdma engine definitions +@@ -130,6 +130,7 @@ struct hailo_vdma_file_context { + struct list_head descriptors_buffer_list; + struct list_head vdma_low_memory_buffer_list; + struct list_head continuous_buffer_list; ++ u32 enabled_channels_bitmap[MAX_VDMA_ENGINES]; + }; + + +@@ -145,6 +146,8 @@ void hailo_vdma_file_context_init(struct + void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context, + struct hailo_vdma_controller *controller, struct file *filp); + ++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine, ++ u32 channels_bitmap); + void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index, + u32 channels_bitmap); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch b/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch new file mode 100644 index 000000000..ecd26a78b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1398-dtoverlays-enable-SPI-CS-active-high.patch @@ -0,0 +1,108 @@ +From dbf12796d1368286672529d7b03f81066a8c36f3 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Mon, 18 Nov 2024 10:55:33 +0100 +Subject: [PATCH] dtoverlays: enable SPI CS active-high + +The documentation isn't very clear explaining how to enable SPI CS +active-high and it takes a long time to understand it. Adding a specific +overlay as a simple example on how to invert this signal can help +understand the solution. + +Link: https://forums.raspberrypi.com/viewtopic.php?t=378222 +Signed-off-by: Iker Pedrosa +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 +++ + .../overlays/spi0-1cs-inverted-overlay.dts | 59 +++++++++++++++++++ + 3 files changed, 68 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -259,6 +259,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + spi-rtc.dtbo \ + spi0-0cs.dtbo \ + spi0-1cs.dtbo \ ++ spi0-1cs-inverted.dtbo \ + spi0-2cs.dtbo \ + spi1-1cs.dtbo \ + spi1-2cs.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4438,6 +4438,14 @@ Params: cs0_pin GPIO pin + it for other uses. + + ++Name: spi0-1cs-inverted ++Info: Only use one CS pin for SPI0 and set to active-high ++Load: dtoverlay=spi0-1cs-inverted,= ++Params: cs0_pin GPIO pin for CS0 (default 8) ++ no_miso Don't claim and use the MISO pin (9), freeing ++ it for other uses. ++ ++ + Name: spi0-2cs + Info: Change the CS pins for SPI0 + Load: dtoverlay=spi0-2cs,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts +@@ -0,0 +1,59 @@ ++/dts-v1/; ++/plugin/; ++ ++/* ++ * There are some devices that need an inverted Chip Select (CS) to select the ++ * device signal, as an example the AZDelivery 12864 display. That means that ++ * the CS polarity is active-high. To invert the CS signal the DT needs to set ++ * the cs-gpio to GPIO_ACTIVE_HIGH (0) in the controller and set the ++ * spi-cs-high in the peripheral property. On top of that, since this is a ++ * display the DT also needs to specify the write-only property. ++*/ ++ ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&spi0_cs_pins>; ++ frag0: __overlay__ { ++ brcm,pins = <8>; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&spi0>; ++ frag1: __overlay__ { ++ cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spidev1>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&spi0_pins>; ++ __dormant__ { ++ brcm,pins = <10 11>; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&spidev0>; ++ __overlay__ { ++ spi-cs-high; ++ }; ++ }; ++ ++ __overrides__ { ++ cs0_pin = <&frag0>,"brcm,pins:0", ++ <&frag1>,"cs-gpios:4"; ++ no_miso = <0>,"=3"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch b/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch new file mode 100644 index 000000000..8f8af00c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1399-drm-vc4-hvs-Defer-updating-the-enable_bg_fill-until-.patch @@ -0,0 +1,64 @@ +From 57b528e557890f25e010b6bc7356b5a716c79db2 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 12 Nov 2024 17:58:52 +0000 +Subject: [PATCH] drm/vc4: hvs: Defer updating the enable_bg_fill until vblank + +The register to enable/disable background fill was being set +from atomic flush, however that will be applied immediately and +can be a while before the vblank. If it was required for the +current frame but not for the next one, that can result in +corruption for part of the current frame. + +Store the state in vc4_hvs, and update it on vblank. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ + drivers/gpu/drm/vc4/vc4_hvs.c | 18 ++++++++++-------- + 2 files changed, 12 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -339,6 +339,8 @@ struct vc4_hvs { + unsigned int enabled: 1; + } eof_irq[HVS_NUM_CHANNELS]; + ++ bool bg_fill[HVS_NUM_CHANNELS]; ++ + unsigned long max_core_rate; + + /* Memory manager for CRTCs to allocate space in the display +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -1470,14 +1470,7 @@ void vc4_hvs_atomic_flush(struct drm_crt + /* This sets a black background color fill, as is the case + * with other DRM drivers. + */ +- if (enable_bg_fill) +- HVS_WRITE(SCALER6_DISPX_CTRL1(channel), +- HVS_READ(SCALER6_DISPX_CTRL1(channel)) | +- SCALER6(DISPX_CTRL1_BGENB)); +- else +- HVS_WRITE(SCALER6_DISPX_CTRL1(channel), +- HVS_READ(SCALER6_DISPX_CTRL1(channel)) & +- ~SCALER6(DISPX_CTRL1_BGENB)); ++ hvs->bg_fill[channel] = enable_bg_fill; + } else { + /* we can actually run with a lower core clock when background + * fill is enabled on VC4_GEN_5 so leave it enabled always. +@@ -1662,6 +1655,15 @@ static irqreturn_t vc6_hvs_eof_irq_handl + if (hvs->eof_irq[i].desc != irq) + continue; + ++ if (hvs->bg_fill[i]) ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) | ++ SCALER6(DISPX_CTRL1_BGENB)); ++ else ++ HVS_WRITE(SCALER6_DISPX_CTRL1(i), ++ HVS_READ(SCALER6_DISPX_CTRL1(i)) & ++ ~SCALER6(DISPX_CTRL1_BGENB)); ++ + vc4_hvs_schedule_dlist_sweep(hvs, i); + return IRQ_HANDLED; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch b/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch new file mode 100644 index 000000000..da21526c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1400-misc-rp1-pio-Add-FIFO-related-methods.patch @@ -0,0 +1,215 @@ +From dd2394360860d15146c96635796a75b05bb32b61 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 19 Nov 2024 09:25:34 +0000 +Subject: [PATCH] misc: rp1-pio: Add FIFO-related methods + +Add support for querying the FIFO status and clearing the TX FIFO. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-fw-pio.h | 3 ++ + drivers/misc/rp1-pio.c | 24 +++++++++ + include/linux/pio_rp1.h | 89 ++++++++++++++++++++++++++++++++++ + include/uapi/misc/rp1_pio_if.h | 13 ++++- + 4 files changed, 128 insertions(+), 1 deletion(-) + +--- a/drivers/misc/rp1-fw-pio.h ++++ b/drivers/misc/rp1-fw-pio.h +@@ -47,6 +47,9 @@ enum rp1_pio_ops { + READ_HW, // src address, len -> data bytes + WRITE_HW, // dst address, data + ++ PIO_SM_FIFO_STATE, // u16 sm, u8 tx -> u16 level, u8 empty, u8 full ++ PIO_SM_DRAIN_TX, // u16 sm ++ + PIO_COUNT + }; + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -476,6 +476,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi + } + EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl); + ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_fifo_state_args *args = param; ++ const int level_offset = offsetof(struct rp1_pio_sm_fifo_state_args, level); ++ int ret; ++ ++ ret = rp1_pio_message_resp(client->pio, PIO_SM_FIFO_STATE, args, sizeof(*args), ++ &args->level, NULL, sizeof(*args) - level_offset); ++ if (ret >= 0) ++ return level_offset + ret; ++ return ret; ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_fifo_state); ++ ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_clear_fifos_args *args = param; ++ ++ return rp1_pio_message(client->pio, PIO_SM_DRAIN_TX, args, sizeof(*args)); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_drain_tx); ++ + int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param) + { + struct rp1_gpio_init_args *args = param; +@@ -848,6 +870,8 @@ struct handler_info { + HANDLER(SM_PUT, sm_put), + HANDLER(SM_GET, sm_get), + HANDLER(SM_SET_DMACTRL, sm_set_dmactrl), ++ HANDLER(SM_FIFO_STATE, sm_fifo_state), ++ HANDLER(SM_DRAIN_TX, sm_drain_tx), + + HANDLER(GPIO_INIT, gpio_init), + HANDLER(GPIO_SET_FUNCTION, gpio_set_function), +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -200,6 +200,8 @@ int rp1_pio_sm_enable_sync(struct rp1_pi + int rp1_pio_sm_put(struct rp1_pio_client *client, void *param); + int rp1_pio_sm_get(struct rp1_pio_client *client, void *param); + int rp1_pio_sm_set_dmactrl(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_fifo_state(struct rp1_pio_client *client, void *param); ++int rp1_pio_sm_drain_tx(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_set_function(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_set_pulls(struct rp1_pio_client *client, void *param); +@@ -551,6 +553,15 @@ static inline int pio_sm_set_dmactrl(str + return rp1_pio_sm_set_dmactrl(client, &args); + }; + ++static inline int pio_sm_drain_tx_fifo(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ return rp1_pio_sm_drain_tx(client, &args); ++}; ++ + static inline int pio_sm_put(struct rp1_pio_client *client, uint sm, uint32_t data) + { + struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = false, .data = data }; +@@ -587,6 +598,84 @@ static inline uint32_t pio_sm_get_blocki + return args.data; + } + ++static inline int pio_sm_is_rx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_rx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_rx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_empty(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.empty; ++ return ret; ++}; ++ ++static inline int pio_sm_is_tx_fifo_full(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.full; ++ return ret; ++}; ++ ++static inline int pio_sm_tx_fifo_level(struct rp1_pio_client *client, uint sm) ++{ ++ struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; ++ int ret; ++ ++ if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) ++ return -EINVAL; ++ ret = rp1_pio_sm_fifo_state(client, &args); ++ if (ret == sizeof(args)) ++ ret = args.level; ++ return ret; ++}; ++ + static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) + { + if (bad_params_if(NULL, out_base >= RP1_PIO_GPIO_COUNT || +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -114,7 +114,7 @@ struct rp1_pio_sm_get_args { + uint16_t sm; + uint8_t blocking; + uint8_t rsvd; +- uint32_t data; /* IN/OUT */ ++ uint32_t data; /* OUT */ + }; + + struct rp1_pio_sm_set_dmactrl_args { +@@ -124,6 +124,15 @@ struct rp1_pio_sm_set_dmactrl_args { + uint32_t ctrl; + }; + ++struct rp1_pio_sm_fifo_state_args { ++ uint16_t sm; ++ uint8_t tx; ++ uint8_t rsvd; ++ uint16_t level; /* OUT */ ++ uint8_t empty; /* OUT */ ++ uint8_t full; /* OUT */ ++}; ++ + struct rp1_gpio_init_args { + uint16_t gpio; + }; +@@ -195,6 +204,8 @@ struct rp1_access_hw_args { + #define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) + #define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) + #define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) ++#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) ++#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) + + #define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) + #define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) diff --git a/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch b/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch new file mode 100644 index 000000000..1a709577e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1401-overlays-Enable-Raspberry-Touch-2-rotation-with-over.patch @@ -0,0 +1,31 @@ +From fa6ad4bcad4e8db18493a4af640b4b5c95434e70 Mon Sep 17 00:00:00 2001 +From: Just a nerd <157698061+foonerd@users.noreply.github.com> +Date: Wed, 20 Nov 2024 14:08:48 +0000 +Subject: [PATCH] overlays: Enable Raspberry Touch 2 rotation with overlay + +See: https://github.com/raspberrypi/linux/pull/6480 +Signed-off-by: foonerd +--- + arch/arm/boot/dts/overlays/README | 1 + + arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5249,6 +5249,7 @@ Params: sizex Touchscr + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis + disable_touch Disables the touch screen overlay driver ++ rotation Display rotation {0,90,180,270} (default 0) + dsi0 Use DSI0 and i2c_csi_dsi0 (rather than + the default DSI1 and i2c_csi_dsi). + +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts +@@ -118,5 +118,6 @@ + invy = <0>, "+11"; + swapxy = <>911>,"touchscreen-swapped-x-y?"; + disable_touch = <>911>, "status=disabled"; ++ rotation = <&dsi_panel>, "rotation:0"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch b/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch new file mode 100644 index 000000000..643e83b31 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1402-rp1-pio-Add-missing-static-inline-s.patch @@ -0,0 +1,42 @@ +From df8a2f6dc114b2c5c7685a069f717f2b06186b74 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 20 Nov 2024 16:23:06 +0000 +Subject: [PATCH] rp1-pio: Add missing 'static inline's + +Avoid some duplicate symbol errors by adding some missing +'static inline' decorations. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -247,7 +247,7 @@ static inline bool pio_can_add_program_a + return !rp1_pio_can_add_program(client, &args); + } + +-uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) ++static inline uint pio_add_program(struct rp1_pio_client *client, const pio_program_t *program) + { + struct rp1_pio_add_program_args args; + int offset; +@@ -367,7 +367,7 @@ static inline int pio_sm_set_config(stru + return rp1_pio_sm_set_config(client, &args); + } + +-int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) ++static inline int pio_sm_exec(struct rp1_pio_client *client, uint sm, uint instr) + { + struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false }; + +@@ -377,7 +377,7 @@ int pio_sm_exec(struct rp1_pio_client *c + return rp1_pio_sm_exec(client, &args); + } + +-int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) ++static inline int pio_sm_exec_wait_blocking(struct rp1_pio_client *client, uint sm, uint instr) + { + struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = true }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch b/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch new file mode 100644 index 000000000..92bde521d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1403-misc-rp1-pio-Back-port-some-6.11-build-fixes.patch @@ -0,0 +1,39 @@ +From d1f0c94e974a5f26d210b1d13a6ef9543bee4984 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 21 Nov 2024 11:11:48 +0000 +Subject: [PATCH] misc: rp1-pio: Back-port some 6.11 build fixes + +Porting rp1-pio to rpi-6.11.y uncovered a few missing #includes and a +difference of const-ness. Although not needed here, back-porting the +resulting changes makes the driver more "correct" and may prevent a +future merge conflict. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 3 +++ + include/linux/pio_rp1.h | 2 +- + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -22,6 +22,9 @@ + #include + #include + #include ++#include ++#include ++#include + #include + #include + #include +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -176,7 +176,7 @@ typedef rp1_pio_sm_config pio_sm_config; + typedef struct rp1_pio_client *PIO; + + void pio_set_error(struct rp1_pio_client *client, int err); +-int pio_get_error(struct rp1_pio_client *client); ++int pio_get_error(const struct rp1_pio_client *client); + void pio_clear_error(struct rp1_pio_client *client); + + int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); diff --git a/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch b/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch new file mode 100644 index 000000000..fd46f25c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1404-Adding-Pimidi-kernel-module.patch @@ -0,0 +1,1167 @@ +From 8a6f640708627ac8ebf79f88793038933f169198 Mon Sep 17 00:00:00 2001 +From: Giedrius +Date: Thu, 21 Nov 2024 08:04:02 +0000 +Subject: [PATCH] Adding Pimidi kernel module. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Giedrius Trainavičius +--- + sound/drivers/Kconfig | 10 + + sound/drivers/Makefile | 2 + + sound/drivers/pimidi.c | 1113 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1125 insertions(+) + create mode 100644 sound/drivers/pimidi.c + +--- a/sound/drivers/Kconfig ++++ b/sound/drivers/Kconfig +@@ -263,4 +263,14 @@ config SND_AC97_POWER_SAVE_DEFAULT + + See SND_AC97_POWER_SAVE for more details. + ++config SND_PIMIDI ++ tristate "Pimidi driver" ++ depends on SND_SEQUENCER && CRC8 ++ select SND_RAWMIDI ++ help ++ Say Y here to include support for Blokas Pimidi. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called snd-pimidi. ++ + endif # SND_DRIVERS +--- a/sound/drivers/Makefile ++++ b/sound/drivers/Makefile +@@ -9,6 +9,7 @@ snd-aloop-objs := aloop.o + snd-mtpav-objs := mtpav.o + snd-mts64-objs := mts64.o + snd-pcmtest-objs := pcmtest.o ++snd-pimidi-objs := pimidi.o + snd-portman2x4-objs := portman2x4.o + snd-serial-u16550-objs := serial-u16550.o + snd-serial-generic-objs := serial-generic.o +@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-s + obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o + obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o + obj-$(CONFIG_SND_MTS64) += snd-mts64.o ++obj-$(CONFIG_SND_PIMIDI) += snd-pimidi.o + obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o + + obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ +--- /dev/null ++++ b/sound/drivers/pimidi.c +@@ -0,0 +1,1113 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define PIMIDI_LOG_IMPL(instance, log_func, msg, ...) log_func("pimidi(%s)[%c]: " msg "\n", \ ++ __func__, (instance) ? (instance)->d + '0' : 'G', ## __VA_ARGS__) ++ ++#ifdef PIMIDI_DEBUG ++# define printd(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert, __VA_ARGS__) ++# define printd_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert_ratelimited, __VA_ARGS__) ++# define printd_g(...) printd((struct pimidi_instance *)NULL, __VA_ARGS__) ++#else ++# define printd(instance, ...) do {} while (0) ++# define printd_rl(instance, ...) do {} while (0) ++# define printd_g(...) do {} while (0) ++#endif ++ ++#define printe(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err, __VA_ARGS__) ++#define printe_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err_ratelimited, __VA_ARGS__) ++#define printi(instance, ...) PIMIDI_LOG_IMPL(instance, pr_info, __VA_ARGS__) ++#define printw(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn, __VA_ARGS__) ++#define printw_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn_ratelimited, __VA_ARGS__) ++ ++#define printe_g(...) printe((struct pimidi_instance *)NULL, __VA_ARGS__) ++#define printi_g(...) printi((struct pimidi_instance *)NULL, __VA_ARGS__) ++ ++DECLARE_CRC8_TABLE(pimidi_crc8_table); ++enum { PIMIDI_CRC8_POLYNOMIAL = 0x83 }; ++enum { PIMIDI_MAX_DEVICES = 4 }; ++enum { PIMIDI_MAX_PACKET_SIZE = 17 }; ++enum { PIMIDI_PORTS = 2 }; ++ ++struct pimidi_shared { ++ // lock protects the shared reset_gpio and devices list. ++ struct mutex lock; ++ struct gpio_desc *reset_gpio; ++ struct workqueue_struct *work_queue; ++ struct list_head devices; ++}; ++ ++static struct pimidi_shared pimidi_global = { ++ .devices = LIST_HEAD_INIT(pimidi_global.devices), ++}; ++ ++struct pimidi_version_t { ++ u8 hwrev; ++ u8 major; ++ u8 minor; ++ u8 build; ++}; ++ ++enum { PIMIDI_IN_FIFO_SIZE = 4096 }; ++ ++struct pimidi_midi_port { ++ // in_lock protects the input substream. ++ struct mutex in_lock; ++ // out_lock protects the output substream. ++ struct mutex out_lock; ++ DECLARE_KFIFO(in_fifo, uint8_t, PIMIDI_IN_FIFO_SIZE); ++ unsigned int last_output_at; ++ unsigned int output_buffer_used_in_millibytes; ++ struct work_struct in_handler; ++ struct delayed_work out_handler; ++ unsigned long enabled_streams; ++ unsigned int tx_cnt; ++ unsigned int rx_cnt; ++}; ++ ++struct pimidi_instance { ++ struct list_head list; ++ struct i2c_client *i2c_client; ++ struct pimidi_version_t version; ++ char serial[11]; ++ char d; ++ struct gpio_desc *data_ready_gpio; ++ ++ struct work_struct drdy_handler; ++ ++ // comm_lock serializes I2C communication. ++ struct mutex comm_lock; ++ char *rx_buf; ++ size_t rx_len; ++ int rx_status; ++ struct completion *rx_completion; ++ ++ struct snd_rawmidi *rawmidi; ++ struct pimidi_midi_port midi_port[PIMIDI_PORTS]; ++ bool stopping; ++}; ++ ++static struct snd_rawmidi_substream *pimidi_find_substream(struct snd_rawmidi *rawmidi, ++ int stream, ++ int number ++ ) ++{ ++ struct snd_rawmidi_substream *substream; ++ ++ list_for_each_entry(substream, &rawmidi->streams[stream].substreams, list) { ++ if (substream->number == number) ++ return substream; ++ } ++ return NULL; ++} ++ ++static void pimidi_midi_in_handler(struct pimidi_instance *instance, int port) ++{ ++ int i, n, err; ++ ++ printd(instance, "(%d)", port); ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_INPUT, &midi_port->enabled_streams)) { ++ printd(instance, "Input not enabled for %d", port); ++ return; ++ } ++ ++ u8 data[512]; ++ ++ n = kfifo_out_peek(&midi_port->in_fifo, data, sizeof(data)); ++ printd(instance, "Peeked %d MIDI bytes", n); ++ ++ mutex_lock(&midi_port->in_lock); ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, ++ SNDRV_RAWMIDI_STREAM_INPUT, ++ port); ++ ++ err = snd_rawmidi_receive(substream, data, n); ++ if (err > 0) ++ midi_port->rx_cnt += err; ++ mutex_unlock(&midi_port->in_lock); ++ ++ for (i = 0; i < err; ++i) ++ kfifo_skip(&midi_port->in_fifo); ++ ++ if (n != err) ++ printw_rl(instance, ++ "Not all MIDI data consumed for port %d: %d / %d", port, err, n); ++ ++ if (!kfifo_is_empty(&midi_port->in_fifo) && !instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_in_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[0].in_handler), ++ 0); ++} ++ ++static void pimidi_midi_in_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[1].in_handler), ++ 1); ++} ++ ++static void pimidi_midi_out_handler(struct pimidi_instance *instance, int port) ++{ ++ printd(instance, "(%d)", port); ++ if (!test_bit(SNDRV_RAWMIDI_STREAM_OUTPUT, &instance->midi_port[port].enabled_streams)) { ++ printd(instance, "Output not enabled for %d", port); ++ return; ++ } ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ struct snd_rawmidi_substream *substream = ++ pimidi_find_substream(instance->rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, port); ++ ++ mutex_lock(&midi_port->out_lock); ++ ++ enum { MIDI_MILLI_BYTES_PER_JIFFY = 3125000 / HZ }; ++ enum { MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES = ++ (512 - PIMIDI_MAX_PACKET_SIZE - 1) * 1000 }; ++ ++ unsigned int now = jiffies; ++ unsigned int millibytes_became_available = ++ (MIDI_MILLI_BYTES_PER_JIFFY) * (now - midi_port->last_output_at); ++ ++ midi_port->output_buffer_used_in_millibytes = ++ midi_port->output_buffer_used_in_millibytes <= ++ millibytes_became_available ? 0 : midi_port->output_buffer_used_in_millibytes - ++ millibytes_became_available; ++ ++ unsigned int output_buffer_available = ++ (MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES ++ - midi_port->output_buffer_used_in_millibytes) ++ / 1000; ++ ++ u8 buffer[PIMIDI_MAX_PACKET_SIZE]; ++ int n, batch, err; ++ ++ for (batch = 0; batch < 3; ++batch) { ++ if (output_buffer_available == 0) ++ printd(instance, "Buffer full"); ++ ++ printd(instance, "Buffer available: %u (%u +%u, %u -> %u, dt %u) (%u) @ %u", ++ output_buffer_available, midi_port->output_buffer_used_in_millibytes, ++ millibytes_became_available, midi_port->last_output_at, now, ++ now - midi_port->last_output_at, midi_port->tx_cnt, HZ); ++ midi_port->last_output_at = now; ++ ++ n = output_buffer_available ++ ? snd_rawmidi_transmit_peek(substream, buffer + 1, ++ min(output_buffer_available, ++ sizeof(buffer) - 2)) ++ : 0; ++ if (n > 0) { ++ printd(instance, "Peeked: %d", n); ++ snd_rawmidi_transmit_ack(substream, n); ++ ++ buffer[0] = (port << 4) | n; ++ buffer[n + 1] = ~crc8(pimidi_crc8_table, buffer, n + 1, CRC8_INIT_VALUE); ++ ++#ifdef PIMIDI_DEBUG ++ pr_debug("%s[%d]: Sending %d bytes:", __func__, instance->d, n + 2); ++ int i; ++ ++ for (i = 0; i < n + 2; ++i) ++ pr_cont(" %02x", buffer[i]); ++ ++ pr_cont("\n"); ++#endif ++ mutex_lock(&instance->comm_lock); ++ err = i2c_master_send(instance->i2c_client, buffer, n + 2); ++ mutex_unlock(&instance->comm_lock); ++ ++ if (err < 0) { ++ printe(instance, ++ "Error occurred when sending MIDI data over I2C! (%d)", ++ err); ++ goto cleanup; ++ } ++ ++ midi_port->tx_cnt += n; ++ midi_port->output_buffer_used_in_millibytes += n * 1000; ++ output_buffer_available -= n; ++ } else if (n < 0) { ++ err = n; ++ printe(instance, "snd_rawmidi_transmit_peek returned error %d!", err); ++ goto cleanup; ++ } else { ++ break; ++ } ++ } ++ ++ printd(instance, "Checking if empty %p", substream); ++ if (!snd_rawmidi_transmit_empty(substream) && !instance->stopping) { ++ unsigned int delay = 1; ++ ++ if (output_buffer_available == 0) ++ delay = 125000 / MIDI_MILLI_BYTES_PER_JIFFY; ++ printd(instance, "Queue more work after %u jiffies", delay); ++ mod_delayed_work(pimidi_global.work_queue, &midi_port->out_handler, delay); ++ } ++ ++cleanup: ++ mutex_unlock(&midi_port->out_lock); ++ printd(instance, "Done"); ++} ++ ++static void pimidi_midi_out_handler_0(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[0].out_handler.work), 0); ++} ++ ++static void pimidi_midi_out_handler_1(struct work_struct *work) ++{ ++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance, ++ midi_port[1].out_handler.work), 1); ++} ++ ++static void pimidi_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!delayed_work_pending(&instance->midi_port[substream->number].out_handler)) { ++ printd(instance, "Queueing work"); ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ } ++ } ++} ++ ++static void pimidi_midi_output_drain(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d)", substream->stream, substream->number); ++ ++ printd(instance, "Begin draining!"); ++ ++ queue_delayed_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].out_handler, 0); ++ ++ unsigned long deadline = jiffies + 5 * HZ; ++ ++ do { ++ printd(instance, "Before flush"); ++ while (delayed_work_pending(&instance->midi_port[substream->number].out_handler)) ++ flush_delayed_work(&instance->midi_port[substream->number].out_handler); ++ printd(instance, "Flushed"); ++ } while (!snd_rawmidi_transmit_empty(substream) && time_before(jiffies, deadline)); ++ ++ printd(instance, "Done!"); ++} ++ ++static int pimidi_midi_output_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->out_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->out_lock); ++ return 0; ++} ++ ++static int pimidi_midi_input_close(struct snd_rawmidi_substream *substream) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number]; ++ ++ mutex_lock(&midi_port->in_lock); ++ clear_bit(substream->stream, &midi_port->enabled_streams); ++ mutex_unlock(&midi_port->in_lock); ++ return 0; ++} ++ ++static void pimidi_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) ++{ ++ struct pimidi_instance *instance = substream->rmidi->private_data; ++ ++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up); ++ ++ if (up == 0) { ++ clear_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ cancel_work_sync(&instance->midi_port[substream->number].in_handler); ++ } else { ++ set_bit(substream->stream, ++ &instance->midi_port[substream->number].enabled_streams); ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, ++ &instance->midi_port[substream->number].in_handler); ++ } ++} ++ ++static void pimidi_get_port_info(struct snd_rawmidi *rmidi, int number, ++ struct snd_seq_port_info *seq_port_info) ++{ ++ printd_g("%p, %d, %p", rmidi, number, seq_port_info); ++ seq_port_info->type = ++ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | ++ SNDRV_SEQ_PORT_TYPE_HARDWARE | ++ SNDRV_SEQ_PORT_TYPE_PORT; ++ strscpy(seq_port_info->name, number == 0 ? "a" : "b", ++ sizeof(seq_port_info->name)); ++ seq_port_info->midi_voices = 0; ++} ++ ++static const struct snd_rawmidi_global_ops pimidi_midi_ops = { ++ .get_port_info = pimidi_get_port_info, ++}; ++ ++static int pimidi_midi_open(struct snd_rawmidi_substream *substream) ++{ ++ printd_g("(%p) stream=%d number=%d", substream, substream->stream, substream->number); ++ return 0; ++} ++ ++static const struct snd_rawmidi_ops pimidi_midi_output_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_output_close, ++ .trigger = pimidi_midi_output_trigger, ++ .drain = pimidi_midi_output_drain, ++}; ++ ++static const struct snd_rawmidi_ops pimidi_midi_input_ops = { ++ .open = pimidi_midi_open, ++ .close = pimidi_midi_input_close, ++ .trigger = pimidi_midi_input_trigger, ++}; ++ ++static int pimidi_register(struct pimidi_instance *instance) ++{ ++ int err = 0; ++ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Registering..."); ++ if (!pimidi_global.reset_gpio) { ++ printd_g("Getting reset pin."); ++ pimidi_global.reset_gpio = gpiod_get(&instance->i2c_client->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(pimidi_global.reset_gpio)) { ++ err = PTR_ERR(pimidi_global.reset_gpio); ++ printe_g("gpiod_get failed: %d", err); ++ pimidi_global.reset_gpio = NULL; ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++ } ++ } ++ list_add_tail(&instance->list, &pimidi_global.devices); ++ mutex_unlock(&pimidi_global.lock); ++ return err; ++} ++ ++static void pimidi_unregister(struct pimidi_instance *instance) ++{ ++ mutex_lock(&pimidi_global.lock); ++ printd(instance, "Unregistering..."); ++ list_del(&instance->list); ++ if (list_empty(&pimidi_global.devices)) { ++ printd_g("Releasing reset pin"); ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static void pimidi_perform_reset(void) ++{ ++ mutex_lock(&pimidi_global.lock); ++ ++ printd_g("Performing reset."); ++ ++ struct list_head *p; ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ printd(instance, "Pausing..."); ++ instance->stopping = true; ++ disable_irq(instance->i2c_client->irq); ++ cancel_work(&instance->drdy_handler); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work(&instance->midi_port[i].in_handler); ++ cancel_delayed_work(&instance->midi_port[i].out_handler); ++ } ++ ++ drain_workqueue(pimidi_global.work_queue); ++ } ++ ++ printd_g("Reset = low"); ++ gpiod_set_value(pimidi_global.reset_gpio, 1); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ gpiod_direction_output(instance->data_ready_gpio, 1); ++ printd(instance, "DRDY high"); ++ } ++ ++ usleep_range(1000, 5000); ++ printd_g("Reset = high"); ++ gpiod_set_value(pimidi_global.reset_gpio, 0); ++ msleep(30); ++ ++ int i; ++ ++ for (i = 0; i < PIMIDI_MAX_DEVICES; ++i) { ++ usleep_range(1000, 3000); ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, ++ list); ++ ++ if (instance->d < i) ++ continue; ++ printd(instance, "DRDY -> %d", !gpiod_get_value(instance->data_ready_gpio)); ++ gpiod_set_value(instance->data_ready_gpio, ++ !gpiod_get_value(instance->data_ready_gpio)); ++ } ++ } ++ usleep_range(16000, 20000); ++ ++ list_for_each(p, &pimidi_global.devices) { ++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list); ++ ++ if (!gpiod_is_active_low(instance->data_ready_gpio)) ++ gpiod_toggle_active_low(instance->data_ready_gpio); ++ ++ printd(instance, "DRDY input"); ++ gpiod_direction_input(instance->data_ready_gpio); ++ ++ printd(instance, "Resume..."); ++ instance->stopping = false; ++ enable_irq(instance->i2c_client->irq); ++ } ++ ++ printd_g("Reset done."); ++ usleep_range(16000, 20000); ++ ++ mutex_unlock(&pimidi_global.lock); ++} ++ ++static int pimidi_read_version(struct pimidi_version_t *version, struct pimidi_instance *instance) ++{ ++ memset(version, 0, sizeof(*version)); ++ ++ const char cmd[4] = { 0xb2, 0x01, 0x01, 0x95 }; ++ ++ char result[9]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, sizeof(result), ++ CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xb7, 0x81, 0x01, 0x00 }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) ++ return -EPROTO; ++ ++ u32 v = ntohl(*(uint32_t *)(result + 4)); ++ ++ version->hwrev = v >> 24; ++ version->major = (v & 0x00ff0000) >> 16; ++ version->minor = (v & 0x0000ff00) >> 8; ++ version->build = v & 0x000000ff; ++ ++ return 0; ++} ++ ++static int pimidi_read_serial(char serial[11], struct pimidi_instance *instance) ++{ ++ memset(serial, 0, sizeof(char[11])); ++ ++ const char cmd[4] = { 0xb2, 0x03, 0x04, 0x97 }; ++ ++ char result[PIMIDI_MAX_PACKET_SIZE]; ++ ++ memset(result, 0, sizeof(result)); ++ ++ DECLARE_COMPLETION_ONSTACK(done); ++ ++ mutex_lock(&instance->comm_lock); ++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd)); ++ ++ if (err < 0) { ++ mutex_unlock(&instance->comm_lock); ++ return err; ++ } ++ instance->rx_buf = result; ++ instance->rx_len = sizeof(result); ++ instance->rx_completion = &done; ++ mutex_unlock(&instance->comm_lock); ++ ++ printd(instance, "Waiting for drdy"); ++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u)); ++ printd(instance, "Done waiting"); ++ ++ if (!completion_done(&done)) { ++ mutex_lock(&instance->comm_lock); ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_status = -ETIMEDOUT; ++ instance->rx_completion = NULL; ++ mutex_unlock(&instance->comm_lock); ++ printe(instance, "Timed out"); ++ return -ETIMEDOUT; ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, ++ (result[0] & 0x0f) + 2, CRC8_INIT_VALUE)) ++ return -EIO; ++ ++ const char expected[4] = { 0xbd, 0x83, 0x04, 0x0a }; ++ ++ if (memcmp(result, expected, sizeof(expected)) != 0) { ++ printe(instance, "Unexpected response: %02x %02x %02x %02x", result[0], result[1], ++ result[2], result[3]); ++ return -EPROTO; ++ } ++ ++ memcpy(serial, result + 4, 10); ++ ++ if (strspn(serial, "\xff") == 10) ++ strscpy(serial, "(unset)", 8); ++ ++ return 0; ++} ++ ++static void pimidi_handle_midi_data(struct pimidi_instance *instance, int port, const uint8_t *data, ++ unsigned int n) ++{ ++ printd(instance, "Handling MIDI data for port %d (%u bytes)", port, n); ++ if (n == 0) ++ return; ++ ++ struct pimidi_midi_port *midi_port = &instance->midi_port[port]; ++ ++ kfifo_in(&midi_port->in_fifo, data, n); ++ ++ if (!instance->stopping) ++ queue_work(pimidi_global.work_queue, &midi_port->in_handler); ++ ++ printd(instance, "Done"); ++} ++ ++static void pimidi_drdy_continue(struct pimidi_instance *instance) ++{ ++ if (instance->stopping) { ++ printd(instance, "Refusing to queue work / enable IRQ due to stopping."); ++ return; ++ } ++ ++ if (gpiod_get_value(instance->data_ready_gpio)) { ++ printd_rl(instance, "Queue work due to DRDY line still low"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ } else { ++ printd_rl(instance, "Enabling irq for more data"); ++ enable_irq(gpiod_to_irq(instance->data_ready_gpio)); ++ } ++} ++ ++static void pimidi_drdy_handler(struct work_struct *work) ++{ ++ struct pimidi_instance *instance = container_of(work, struct pimidi_instance, drdy_handler); ++ ++ printd(instance, "(%p)", work); ++ ++ mutex_lock(&instance->comm_lock); ++ if (!instance->rx_completion) { ++ u8 data[PIMIDI_MAX_PACKET_SIZE]; ++ int n = i2c_master_recv(instance->i2c_client, data, 3); ++ ++ if (n < 0) { ++ printe(instance, "Error reading from device: %d", n); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ if (data[0] == 0xfe) { ++ printe_rl(instance, "Invalid packet 0x%02x 0x%02x 0x%02x", data[0], data[1], ++ data[2]); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++ } ++ ++ int len = (data[0] & 0x0f) + 2; ++ ++ if (len > n) { ++ printd(instance, "Need %d more bytes", len - n); ++ int err = i2c_master_recv(instance->i2c_client, data + n, len - n); ++ ++ if (err < 0) { ++ printe(instance, "Error reading remainder from device: %d", err); ++ mutex_unlock(&instance->comm_lock); ++ pimidi_drdy_continue(instance); ++ return; ++#ifdef PIMIDI_DEBUG ++ } else { ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = n; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++#endif ++ } ++ } ++ ++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) == crc8(pimidi_crc8_table, data, len, ++ CRC8_INIT_VALUE)) { ++ switch (data[0] & 0xf0) { ++ case 0x00: ++ pimidi_handle_midi_data(instance, 0, data + 1, len - 2); ++ break; ++ case 0x10: ++ pimidi_handle_midi_data(instance, 1, data + 1, len - 2); ++ break; ++ default: ++ printd(instance, "Unhandled command %02x", data[0]); ++ break; ++ } ++ } else { ++ printe(instance, "I2C rx corruption detected."); ++ pr_info("Packet [%d]:", len); ++ int i; ++ ++ for (i = 0; i < len; ++i) ++ pr_cont(" %02x", data[i]); ++ pr_cont("\n"); ++ } ++ ++ mutex_unlock(&instance->comm_lock); ++ } else { ++ printd(instance, "Completing drdy"); ++ instance->rx_status = i2c_master_recv(instance->i2c_client, instance->rx_buf, 3); ++ printd(instance, "Recv_1 %02x %02x %02x", instance->rx_buf[0], instance->rx_buf[1], ++ instance->rx_buf[2]); ++ if (instance->rx_len > 3 && instance->rx_status == 3) { ++ instance->rx_status = i2c_master_recv(instance->i2c_client, ++ instance->rx_buf + 3, ++ instance->rx_len - 3); ++ if (instance->rx_status >= 0) ++ instance->rx_status += 3; ++#ifdef PIMIDI_DEBUG ++ pr_debug("Recv_2:"); ++ int i; ++ ++ for (i = 3; i < instance->rx_len; ++i) ++ pr_cont(" %02x", instance->rx_buf[i]); ++ pr_cont("\n"); ++#endif ++ } ++ struct completion *done = instance->rx_completion; ++ ++ instance->rx_buf = NULL; ++ instance->rx_len = 0; ++ instance->rx_completion = NULL; ++ complete_all(done); ++ mutex_unlock(&instance->comm_lock); ++ } ++ ++ pimidi_drdy_continue(instance); ++} ++ ++static irqreturn_t pimidi_drdy_interrupt_handler(int irq, void *dev_id) ++{ ++ struct pimidi_instance *instance = (struct pimidi_instance *)dev_id; ++ ++ if (instance->stopping) { ++ printd(instance, "DRDY interrupt, but stopping, ignoring..."); ++ return IRQ_HANDLED; ++ } ++ ++ printd(instance, "DRDY interrupt, masking"); ++ disable_irq_nosync(irq); ++ ++ printd(instance, "Queue work due to DRDY interrupt"); ++ queue_work(pimidi_global.work_queue, &instance->drdy_handler); ++ ++ return IRQ_HANDLED; ++} ++ ++static void pimidi_proc_stat_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ const unsigned int *d = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", *d); ++} ++ ++static void pimidi_proc_serial_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%s\n", instance->serial); ++} ++ ++static void pimidi_proc_version_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u.%u.%u\n", instance->version.major, instance->version.minor, ++ instance->version.build); ++} ++ ++static void pimidi_proc_hwrev_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer) ++{ ++ struct pimidi_instance *instance = entry->private_data; ++ ++ snd_iprintf(buffer, "%u\n", instance->version.hwrev); ++} ++ ++static int pimidi_i2c_probe(struct i2c_client *client) ++{ ++ struct snd_card *card = NULL; ++ int err, d, i; ++ ++ d = client->addr - 0x20; ++ ++ if (d < 0 || d >= 8) { ++ printe_g("Unexpected device address: %d", client->addr); ++ err = -EINVAL; ++ goto finalize; ++ } ++ ++ err = snd_card_new(&client->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, ++ sizeof(struct pimidi_instance), &card); ++ ++ if (err) { ++ printe_g("snd_card_new failed: %d", err); ++ return err; ++ } ++ ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->i2c_client = client; ++ instance->d = d; ++ ++ struct snd_rawmidi *rawmidi; ++ ++ err = snd_rawmidi_new(card, card->shortname, 0, 2, 2, &rawmidi); ++ if (err < 0) { ++ printe(instance, "snd_rawmidi_new failed: %d", err); ++ goto finalize; ++ } ++ ++ instance->rawmidi = rawmidi; ++ strscpy(rawmidi->name, "pimidi", sizeof(rawmidi->name)); ++ ++ rawmidi->info_flags = ++ SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; ++ rawmidi->private_data = instance; ++ rawmidi->ops = &pimidi_midi_ops; ++ ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &pimidi_midi_output_ops); ++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pimidi_midi_input_ops); ++ ++ instance->data_ready_gpio = devm_gpiod_get(&client->dev, "data-ready", GPIOD_OUT_HIGH); ++ if (IS_ERR(instance->data_ready_gpio)) { ++ err = PTR_ERR(instance->data_ready_gpio); ++ printe(instance, "devm_gpiod_get failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_register(instance); ++ if (err < 0) { ++ printe(instance, "pimidi_register failed: %d", err); ++ goto finalize; ++ } ++ ++ pimidi_perform_reset(); ++ ++ INIT_WORK(&instance->drdy_handler, pimidi_drdy_handler); ++ mutex_init(&instance->comm_lock); ++ ++ err = devm_request_irq(&client->dev, client->irq, pimidi_drdy_interrupt_handler, ++ IRQF_SHARED | IRQF_TRIGGER_LOW, "data_ready_int", instance); ++ ++ if (err != 0) { ++ printe(instance, "data_available IRQ request failed! %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_version(&instance->version, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_version failed: %d", err); ++ goto finalize; ++ } ++ ++ err = pimidi_read_serial(instance->serial, instance); ++ if (err < 0) { ++ printe(instance, "pimidi_read_serial failed: %d", err); ++ goto finalize; ++ } else if (instance->serial[0] != 'P' || instance->serial[1] != 'M' || ++ strlen(instance->serial) != 10) { ++ printe(instance, "Unexpected serial number: %s", instance->serial); ++ err = -EIO; ++ goto finalize; ++ } ++ ++ printi(instance, "pimidi%d hw:%d version %u.%u.%u-%u, serial %s", ++ d, ++ card->number, ++ instance->version.major, ++ instance->version.minor, ++ instance->version.build, ++ instance->version.hwrev, ++ instance->serial ++ ); ++ ++ strscpy(card->driver, "snd-pimidi", sizeof(card->driver)); ++ snprintf(card->shortname, sizeof(card->shortname), "pimidi%d", d); ++ snprintf(card->longname, sizeof(card->longname), "pimidi%d %s", d, instance->serial); ++ snprintf(card->id, sizeof(card->id), "pimidi%d", d); ++ ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 0)->name, ++ 10u, "pimidi%d-a", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 1)->name, ++ 10u, "pimidi%d-b", d); ++ ++ err = snd_card_ro_proc_new(card, "a-tx", &instance->midi_port[0].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "a-rx", &instance->midi_port[0].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-tx", &instance->midi_port[1].tx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "b-rx", &instance->midi_port[1].rx_cnt, ++ pimidi_proc_stat_show); ++ err = snd_card_ro_proc_new(card, "serial", instance, pimidi_proc_serial_show); ++ err = snd_card_ro_proc_new(card, "version", instance, pimidi_proc_version_show); ++ err = snd_card_ro_proc_new(card, "hwrev", instance, pimidi_proc_hwrev_show); ++ if (err < 0) { ++ printe(instance, "snd_card_ro_proc_new failed: %d", err); ++ goto finalize; ++ } ++ ++ err = snd_card_register(card); ++ if (err < 0) { ++ printe(instance, "snd_card_register failed: %d", err); ++ goto finalize; ++ } ++ ++finalize: ++ if (err) { ++ instance->stopping = true; ++ cancel_work_sync(&instance->drdy_handler); ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ return err; ++ } ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ struct pimidi_midi_port *port = &instance->midi_port[i]; ++ ++ mutex_init(&port->in_lock); ++ mutex_init(&port->out_lock); ++ INIT_WORK(&port->in_handler, ++ i == 0 ? pimidi_midi_in_handler_0 : pimidi_midi_in_handler_1); ++ INIT_DELAYED_WORK(&port->out_handler, ++ i == 0 ? pimidi_midi_out_handler_0 : pimidi_midi_out_handler_1); ++ INIT_KFIFO(port->in_fifo); ++ port->last_output_at = jiffies; ++ } ++ ++ i2c_set_clientdata(client, card); ++ return 0; ++} ++ ++static void pimidi_i2c_remove(struct i2c_client *client) ++{ ++ printd_g("(%p)", client); ++ ++ int i; ++ struct snd_card *card = i2c_get_clientdata(client); ++ ++ if (card) { ++ printi_g("Unloading hw:%d %s", card->number, card->longname); ++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data; ++ ++ instance->stopping = true; ++ i2c_set_clientdata(client, NULL); ++ devm_free_irq(&client->dev, client->irq, instance); ++ cancel_work_sync(&instance->drdy_handler); ++ ++ for (i = 0; i < PIMIDI_PORTS; ++i) { ++ cancel_work_sync(&instance->midi_port[i].in_handler); ++ cancel_delayed_work_sync(&instance->midi_port[i].out_handler); ++ mutex_destroy(&instance->midi_port[i].out_lock); ++ mutex_destroy(&instance->midi_port[i].in_lock); ++ kfifo_free(&instance->midi_port[i].in_fifo); ++ } ++ ++ mutex_destroy(&instance->comm_lock); ++ pimidi_unregister(instance); ++ snd_card_free(card); ++ } ++} ++ ++static const struct i2c_device_id pimidi_i2c_ids[] = { ++ { "pimidi", 0 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, pimidi_i2c_ids); ++ ++static const struct of_device_id pimidi_i2c_dt_ids[] = { ++ { .compatible = "blokaslabs,pimidi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, pimidi_i2c_dt_ids); ++ ++static struct i2c_driver pimidi_i2c_driver = { ++ .driver = { ++ .name = "pimidi", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(pimidi_i2c_dt_ids), ++ }, ++ .probe = pimidi_i2c_probe, ++ .remove = pimidi_i2c_remove, ++ .id_table = pimidi_i2c_ids, ++}; ++ ++int pimidi_module_init(void) ++{ ++ int err = 0; ++ ++ mutex_init(&pimidi_global.lock); ++ ++ INIT_LIST_HEAD(&pimidi_global.devices); ++ ++ pimidi_global.work_queue = create_singlethread_workqueue("pimidi"); ++ if (!pimidi_global.work_queue) { ++ err = -ENOMEM; ++ goto cleanup; ++ } ++ ++ err = i2c_add_driver(&pimidi_i2c_driver); ++ if (err < 0) ++ goto cleanup; ++ ++ crc8_populate_msb(pimidi_crc8_table, PIMIDI_CRC8_POLYNOMIAL); ++ ++ return 0; ++ ++cleanup: ++ mutex_destroy(&pimidi_global.lock); ++ return err; ++} ++ ++void pimidi_module_exit(void) ++{ ++ i2c_del_driver(&pimidi_i2c_driver); ++ mutex_lock(&pimidi_global.lock); ++ if (pimidi_global.reset_gpio) { ++ gpiod_put(pimidi_global.reset_gpio); ++ pimidi_global.reset_gpio = NULL; ++ } ++ mutex_unlock(&pimidi_global.lock); ++ ++ destroy_workqueue(pimidi_global.work_queue); ++ pimidi_global.work_queue = NULL; ++ ++ mutex_destroy(&pimidi_global.lock); ++} ++ ++module_init(pimidi_module_init); ++module_exit(pimidi_module_exit); ++ ++MODULE_AUTHOR("Giedrius Trainavi\xc4\x8dius "); ++MODULE_DESCRIPTION("MIDI driver for Blokas Pimidi, https://blokas.io/"); ++MODULE_LICENSE("GPL"); ++ ++/* vim: set ts=8 sw=8 noexpandtab: */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch b/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch new file mode 100644 index 000000000..ab46dbdb9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1406-Adding-pimidi-overlay.dts.patch @@ -0,0 +1,100 @@ +From 75ab92b077602734458f0a77e19a3599be29b93b Mon Sep 17 00:00:00 2001 +From: Giedrius +Date: Thu, 21 Nov 2024 08:05:49 +0000 +Subject: [PATCH] Adding pimidi-overlay.dts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Giedrius Trainavičius +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 8 +++ + arch/arm/boot/dts/overlays/pimidi-overlay.dts | 54 +++++++++++++++++++ + 3 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pimidi-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -203,6 +203,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + pifi-dac-zero.dtbo \ + pifi-mini-210.dtbo \ + piglow.dtbo \ ++ pimidi.dtbo \ + pineboards-hat-ai.dtbo \ + pineboards-hatdrive-poe-plus.dtbo \ + piscreen.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3701,6 +3701,14 @@ Load: dtoverlay=piglow + Params: + + ++Name: pimidi ++Info: Configures the Blokas Labs Pimidi card ++Load: dtoverlay=pimidi,= ++Params: sel The position used for the sel rotary switch. ++ Each unit in the stack must be set on a unique ++ position. If param is omitted, sel=0 is assumed. ++ ++ + Name: pineboards-hat-ai + Info: Pineboards Hat Ai! overlay for the Google Coral Edge TPU + Load: dtoverlay=pineboards-hat-ai +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +@@ -0,0 +1,54 @@ ++/* ++ * Pimidi Linux kernel module. ++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/ ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++#include ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c_arm>; ++ __overlay__ { ++ status = "okay"; ++ clock-frequency=<1000000>; ++ ++ pimidi_ctrl: pimidi_ctrl@20 { ++ compatible = "blokaslabs,pimidi"; ++ ++ reg = <0x20>; ++ status = "okay"; ++ ++ interrupt-parent = <&gpio>; ++ interrupts = <23 IRQ_TYPE_LEVEL_LOW>; ++ interrupt-names = "data_ready"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ ++ data-ready-gpios = <&gpio 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ sel = <&pimidi_ctrl>,"reg:0{0=0x20,1=0x21,2=0x22,3=0x23}", ++ <&pimidi_ctrl>,"data-ready-gpios:4{0=23,1=5,2=6,3=27}", ++ <&pimidi_ctrl>,"interrupts:0{0=23,1=5,2=6,3=27}"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch b/target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch similarity index 98% rename from target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch rename to target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch index 11147d9fd..871b2769b 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0297-staging-vchiq_arm-Add-36-bit-address-support.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1411-staging-vchiq_arm-Add-36-bit-address-support.patch @@ -1,7 +1,7 @@ -From fc5ed9d9bf0411523220bab60304da6d23257a64 Mon Sep 17 00:00:00 2001 +From a1e4b72997dc3ef423b6f510bfead470475750d4 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 1 Nov 2018 17:31:37 +0000 -Subject: [PATCH 0297/1085] staging: vchiq_arm: Add 36-bit address support +Subject: [PATCH] staging: vchiq_arm: Add 36-bit address support Conditional on a new compatible string, change the pagelist encoding such that the top 24 bits are the pfn, leaving 8 bits for run length diff --git a/target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch b/target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch similarity index 87% rename from target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch rename to target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch index 87bea65fc..df93f3a0c 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0298-staging-vchiq_arm-children-inherit-DMA-config.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1412-staging-vchiq_arm-children-inherit-DMA-config.patch @@ -1,7 +1,7 @@ -From d4712f611e6d60dd9cf09df581f5df6fad6a2207 Mon Sep 17 00:00:00 2001 +From 1129091b2d95273d930acf2926a569b90512a248 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 21 Jul 2020 17:34:09 +0100 -Subject: [PATCH 0298/1085] staging: vchiq_arm: children inherit DMA config +Subject: [PATCH] staging: vchiq_arm: children inherit DMA config Although it is no longer necessary for vchiq's children to have a different DMA configuration to the parent, they do still need to diff --git a/target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch b/target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch similarity index 96% rename from target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch rename to target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch index 162d333c6..7120123ec 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0299-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1413-staging-vchiq_arm-Usa-a-DMA-pool-for-small-bulks.patch @@ -1,7 +1,7 @@ -From 9f328c347fc9a5495b8383aa2bae1d3bc242a2ab Mon Sep 17 00:00:00 2001 +From 2d26a598ceceaea8a6837146c741eb742bbd4baa Mon Sep 17 00:00:00 2001 From: detule Date: Tue, 2 Oct 2018 04:10:08 -0400 -Subject: [PATCH 0299/1085] staging: vchiq_arm: Usa a DMA pool for small bulks +Subject: [PATCH] staging: vchiq_arm: Usa a DMA pool for small bulks During a bulk transfer we request a DMA allocation to hold the scatter-gather list. Most of the time, this allocation is small diff --git a/target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch b/target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch similarity index 88% rename from target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch rename to target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch index 51815f261..d85765bc3 100644 --- a/target/linux/bcm27xx/patches-6.6/950-0365-staging-vchiq_arm-Add-log_level-module-params.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1414-staging-vchiq_arm-Add-log_level-module-params.patch @@ -1,7 +1,7 @@ -From 79f24f7454a416fed9106c75ea9b3be480465dda Mon Sep 17 00:00:00 2001 +From 5b29221e96d1ba60a78d5c804a20fa35a6d0517a Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 29 Apr 2022 09:19:10 +0100 -Subject: [PATCH 0365/1085] staging: vchiq_arm: Add log_level module params +Subject: [PATCH] staging: vchiq_arm: Add log_level module params Add module parameters to control the logging levels for the various vchiq logging categories. diff --git a/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch b/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch new file mode 100644 index 000000000..9b5b0e47d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1415-media-i2c-imx477-Fix-link-frequency-menu.patch @@ -0,0 +1,25 @@ +From 8691544f688bd3ae9b6db0845a75ce230fc9e90f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 21 Nov 2024 15:54:58 +0000 +Subject: [PATCH] media: i2c: imx477: Fix link frequency menu + +"media: i2c: imx477: Add options for slightly modifying the link freq" +created a link frequency menu with 2 items in instead of one. +Correct this. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx477.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -2051,7 +2051,7 @@ static int imx477_init_controls(struct i + /* LINK_FREQ is also read only */ + imx477->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops, +- V4L2_CID_LINK_FREQ, 1, 0, ++ V4L2_CID_LINK_FREQ, 0, 0, + &link_freqs[imx477->link_freq_idx]); + if (imx477->link_freq) + imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; diff --git a/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch b/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch new file mode 100644 index 000000000..cf13be0a0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1416-misc-rp1-pio-Fix-copy-paste-error-in-pio_rp1.h.patch @@ -0,0 +1,25 @@ +From 99a0201bb0abc946dc431214b638b2cc6b01dda5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 25 Nov 2024 16:19:55 +0000 +Subject: [PATCH] misc/rp1-pio: Fix copy/paste error in pio_rp1.h + +As per the subject, there was a copy/paste error that caused +pio_sm_unclaim from a driver to result in a call to +pio_sm_claim. Fix it. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -320,7 +320,7 @@ static inline int pio_sm_unclaim(struct + if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES)) + return -EINVAL; + +- return rp1_pio_sm_claim(client, &args); ++ return rp1_pio_sm_unclaim(client, &args); + } + + static inline int pio_claim_unused_sm(struct rp1_pio_client *client, bool required) diff --git a/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch b/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch new file mode 100644 index 000000000..e68bd175d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1417-misc-rp1-pio-Fix-parameter-checks-wihout-client.patch @@ -0,0 +1,25 @@ +From 3687701e8d252864f440f91f1aedf8ffd58d6ee6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 25 Nov 2024 21:51:13 +0000 +Subject: [PATCH] misc: rp1-pio: Fix parameter checks wihout client + +Passing bad parameters to an API call without a pio pointer will cause +a NULL pointer exception when the persistent error is set. Guard +against that. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -20,7 +20,7 @@ + #endif + + #define bad_params_if(client, test) \ +- ({ bool f = (test); if (f) pio_set_error(client, -EINVAL); \ ++ ({ bool f = (test); if (f && client) pio_set_error(client, -EINVAL); \ + if (f && PARAM_WARNINGS_ENABLED) WARN_ON((test)); \ + f; }) + diff --git a/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch b/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch new file mode 100644 index 000000000..5e50af70d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1418-drm-vc4-dsi-Handle-the-different-command-FIFO-widths.patch @@ -0,0 +1,147 @@ +From 008c93b47b9b965368eb5bbfbef60b816931e0ab Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 20 Nov 2024 13:58:08 +0000 +Subject: [PATCH] drm: vc4: dsi: Handle the different command FIFO widths + +DSI0 and DSI1 have different widths for the command FIFO (24bit +vs 32bit), but the driver was assuming the 32bit width of DSI1 +in all cases. +DSI0 also wants the data packed as 24bit big endian, so the +formatting code needs updating. + +Handle the difference via the variant structure. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_dsi.c | 64 ++++++++++++++++++++++++----------- + 1 file changed, 44 insertions(+), 20 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_dsi.c ++++ b/drivers/gpu/drm/vc4/vc4_dsi.c +@@ -44,7 +44,6 @@ + + #define DSI_CMD_FIFO_DEPTH 16 + #define DSI_PIX_FIFO_DEPTH 256 +-#define DSI_PIX_FIFO_WIDTH 4 + + #define DSI0_CTRL 0x00 + +@@ -170,11 +169,15 @@ + #define DSI1_DISP1_CTRL 0x2c + /* Format of the data written to TXPKT_PIX_FIFO. */ + # define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1) +-# define DSI_DISP1_PFORMAT_SHIFT 1 +-# define DSI_DISP1_PFORMAT_16BIT 0 +-# define DSI_DISP1_PFORMAT_24BIT 1 +-# define DSI_DISP1_PFORMAT_32BIT_LE 2 +-# define DSI_DISP1_PFORMAT_32BIT_BE 3 ++# define DSI1_DISP1_PFORMAT_SHIFT 1 ++# define DSI0_DISP1_PFORMAT_16BIT 0 ++# define DSI0_DISP1_PFORMAT_16BIT_ADJ 1 ++# define DSI0_DISP1_PFORMAT_24BIT 2 ++# define DSI0_DISP1_PFORMAT_32BIT_LE 3 /* NB Invalid, but required for macros to work */ ++# define DSI1_DISP1_PFORMAT_16BIT 0 ++# define DSI1_DISP1_PFORMAT_24BIT 1 ++# define DSI1_DISP1_PFORMAT_32BIT_LE 2 ++# define DSI1_DISP1_PFORMAT_32BIT_BE 3 + + /* DISP1 is always command mode. */ + # define DSI_DISP1_ENABLE BIT(0) +@@ -553,6 +556,7 @@ struct vc4_dsi_variant { + unsigned int port; + + bool broken_axi_workaround; ++ unsigned int cmd_fifo_width; + + const char *debugfs_name; + const struct debugfs_reg32 *regs; +@@ -1151,10 +1155,16 @@ static void vc4_dsi_bridge_pre_enable(st + /* Set up DISP1 for transferring long command payloads through + * the pixfifo. + */ +- DSI_PORT_WRITE(DISP1_CTRL, +- VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE, +- DSI_DISP1_PFORMAT) | +- DSI_DISP1_ENABLE); ++ if (dsi->variant->cmd_fifo_width == 4) ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_32BIT_LE), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); ++ else ++ DSI_PORT_WRITE(DISP1_CTRL, ++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_24BIT), ++ DSI_DISP1_PFORMAT) | ++ DSI_DISP1_ENABLE); + + /* Bring AFE out of reset. */ + DSI_PORT_WRITE(PHY_AFEC0, +@@ -1235,9 +1245,9 @@ static ssize_t vc4_dsi_transfer(struct v + pix_fifo_len = 0; + } else { + cmd_fifo_len = (packet.payload_length % +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + pix_fifo_len = ((packet.payload_length - cmd_fifo_len) / +- DSI_PIX_FIFO_WIDTH); ++ dsi->variant->cmd_fifo_width); + } + + WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH); +@@ -1255,14 +1265,25 @@ static ssize_t vc4_dsi_transfer(struct v + + for (i = 0; i < cmd_fifo_len; i++) + DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]); +- for (i = 0; i < pix_fifo_len; i++) { +- const u8 *pix = packet.payload + cmd_fifo_len + i * 4; ++ if (dsi->variant->cmd_fifo_width == 4) { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 4; ++ ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[0] | ++ pix[1] << 8 | ++ pix[2] << 16 | ++ pix[3] << 24); ++ } ++ } else { ++ for (i = 0; i < pix_fifo_len; i++) { ++ const u8 *pix = packet.payload + cmd_fifo_len + i * 3; + +- DSI_PORT_WRITE(TXPKT_PIX_FIFO, +- pix[0] | +- pix[1] << 8 | +- pix[2] << 16 | +- pix[3] << 24); ++ DSI_PORT_WRITE(TXPKT_PIX_FIFO, ++ pix[2] | ++ pix[1] << 8 | ++ pix[0] << 16); ++ } + } + + if (msg->flags & MIPI_DSI_MSG_USE_LPM) +@@ -1516,6 +1537,7 @@ static const struct drm_encoder_funcs vc + + static const struct vc4_dsi_variant bcm2711_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, + .nregs = ARRAY_SIZE(dsi1_regs), +@@ -1523,6 +1545,7 @@ static const struct vc4_dsi_variant bcm2 + + static const struct vc4_dsi_variant bcm2835_dsi0_variant = { + .port = 0, ++ .cmd_fifo_width = 3, + .debugfs_name = "dsi0_regs", + .regs = dsi0_regs, + .nregs = ARRAY_SIZE(dsi0_regs), +@@ -1530,6 +1553,7 @@ static const struct vc4_dsi_variant bcm2 + + static const struct vc4_dsi_variant bcm2835_dsi1_variant = { + .port = 1, ++ .cmd_fifo_width = 4, + .broken_axi_workaround = true, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, diff --git a/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch b/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch new file mode 100644 index 000000000..0aab1043b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1419-dts-bcm2712-rpi-For-CM5IO-i2c_csi_dsi-needs-to-be-CA.patch @@ -0,0 +1,30 @@ +From eafaa6015fc0ed676f6115905e7c4145d23f5b7d Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 26 Nov 2024 15:53:24 +0000 +Subject: [PATCH] dts: bcm2712-rpi: For CM5IO, i2c_csi_dsi needs to be + CAM/DISP1 + +Noted setting up a display on CM5IO. Add +"dtoverlay=vc4-kms-dsi-ili7881-7inch" fails as it tries to +find the regulator/backlight/touch on i2c_csi_dsi, which pointed +at i2c_csi_dsi0 by default. + +Adding the dsi0 override updated to point at dsi0, and pointed +the i2c at i2c_csi_dsi0, which all works. + +The default with i2c_csi_dsi needs to be consistent in using +dsi1/csi1 and the corresponding i2c interface (i2c_csi_dsi1). + +Signed-off-by: Dave Stevenson +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi +@@ -11,4 +11,4 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f + symlink = "i2c-6"; + }; + +-i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable ++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable diff --git a/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch b/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch new file mode 100644 index 000000000..e2a01216a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1420-dts-bcm2712-rpi-cm5-Remove-inaccessible-USB_OC_N.patch @@ -0,0 +1,25 @@ +From d128c123754e9dd03ad72c16851a1652331d6da1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 27 Nov 2024 10:24:47 +0000 +Subject: [PATCH] dts: bcm2712-rpi-cm5: Remove inaccessible USB_OC_N + +Although VBUS_EN on GPIO42 appears on the CM5's 100-way headers, +USB_OC_N on GPIO43 does not. Remove the signal name to avoid further +confusion and disappointment. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -718,7 +718,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + "-", // GPIO40 + "-", // GPIO41 + "USB_VBUS_EN", // GPIO42 +- "USB_OC_N", // GPIO43 ++ "-", // GPIO43 + "RP1_STAT_LED", // GPIO44 + "FAN_PWM", // GPIO45 + "-", // GPIO46 diff --git a/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch b/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch new file mode 100644 index 000000000..8cdb2504a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1421-overlays-qca7000-replace-URL-with-textual-hint.patch @@ -0,0 +1,33 @@ +From 77389e715039b1feac9c6261727600892cc12fdb Mon Sep 17 00:00:00 2001 +From: Michael Heimpold +Date: Fri, 29 Nov 2024 14:10:04 +0100 +Subject: [PATCH] overlays: qca7000: replace URL with textual hint + +The deep link into the website is not that stable, so let's +replace it with a textual description where to find the +product information. + +Signed-off-by: Michael Heimpold +--- + arch/arm/boot/dts/overlays/qca7000-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/qca7000-overlay.dts ++++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts +@@ -1,5 +1,5 @@ + // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK +-// Visit: https://chargebyte.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + + /dts-v1/; + /plugin/; +--- a/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts ++++ b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts +@@ -1,5 +1,5 @@ + // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK +-// Visit: https://in-tech-smartcharging.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details ++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details + + /dts-v1/; + /plugin/; diff --git a/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch b/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch new file mode 100644 index 000000000..1364b4f71 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1422-dt-bindings-net-cdns-macb-Add-compatible-for-Raspber.patch @@ -0,0 +1,24 @@ +From 178f1c2747c3920723242f26ba290785d45bffae Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 11 Nov 2024 16:38:01 +0000 +Subject: [PATCH] dt-bindings: net: cdns,macb: Add compatible for Raspberry Pi + RP1 + +The Raspberry Pi RP1 chip has the Cadence GEM ethernet +controller, so add a compatible string for it. + +Signed-off-by: Dave Stevenson +--- + Documentation/devicetree/bindings/net/cdns,macb.yaml | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml ++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml +@@ -54,6 +54,7 @@ properties: + - cdns,np4-macb # NP4 SoC devices + - microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface + - microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface ++ - raspberrypi,rp1-gem # Raspberry Pi RP1 gigabit ethernet interface + - sifive,fu540-c000-gem # SiFive FU540-C000 SoC + - cdns,emac # Generic + - cdns,gem # Generic diff --git a/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch b/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch new file mode 100644 index 000000000..c34d1c872 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1423-net-macb-Add-support-for-Raspberry-Pi-RP1-ethernet-c.patch @@ -0,0 +1,43 @@ +From f9f0024bd9bf04a58b64bae356be4c04022d23bc Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 11 Nov 2024 16:40:07 +0000 +Subject: [PATCH] net: macb: Add support for Raspberry Pi RP1 ethernet + controller + +The RP1 chip has the Cadence GEM block, but wants the tx_clock +to always run at 125MHz, in the same way as sama7g5. +Add the relevant configuration. + +Signed-off-by: Dave Stevenson +--- + drivers/net/ethernet/cadence/macb_main.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -5030,6 +5030,17 @@ static const struct macb_config versal_c + .usrio = &macb_default_usrio, + }; + ++static const struct macb_config raspberrypi_rp1_config = { ++ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | ++ MACB_CAPS_JUMBO | ++ MACB_CAPS_GEM_HAS_PTP, ++ .dma_burst_length = 16, ++ .clk_init = macb_clk_init, ++ .init = macb_init, ++ .usrio = &macb_default_usrio, ++ .jumbo_max_len = 10240, ++}; ++ + static const struct of_device_id macb_dt_ids[] = { + { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, + { .compatible = "cdns,macb" }, +@@ -5050,6 +5061,7 @@ static const struct of_device_id macb_dt + { .compatible = "microchip,mpfs-macb", .data = &mpfs_config }, + { .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config }, + { .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config }, ++ { .compatible = "raspberrypi,rp1-gem", .data = &raspberrypi_rp1_config }, + { .compatible = "xlnx,zynqmp-gem", .data = &zynqmp_config}, + { .compatible = "xlnx,zynq-gem", .data = &zynq_config }, + { .compatible = "xlnx,versal-gem", .data = &versal_config}, diff --git a/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch b/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch new file mode 100644 index 000000000..91d836f71 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1424-rp1-clk-Only-set-PLL_SEC_RST-in-rp1_pll_divider_off.patch @@ -0,0 +1,26 @@ +From 33c225f622d596034a9261316666089a92aa6834 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 25 Nov 2024 12:30:06 +0000 +Subject: [PATCH] rp1: clk: Only set PLL_SEC_RST in rp1_pll_divider_off + +Rather than clearing all the bits in rp1_pll_divider_off +and setting PLL_SEC_RST, retain the status of all the other +bits. + +Signed-off-by: Dave Stevenson +--- + drivers/clk/clk-rp1.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/clk/clk-rp1.c ++++ b/drivers/clk/clk-rp1.c +@@ -927,7 +927,8 @@ static void rp1_pll_divider_off(struct c + const struct rp1_pll_data *data = divider->data; + + spin_lock(&clockman->regs_lock); +- clockman_write(clockman, data->ctrl_reg, PLL_SEC_RST); ++ clockman_write(clockman, data->ctrl_reg, ++ clockman_read(clockman, data->ctrl_reg) | PLL_SEC_RST); + spin_unlock(&clockman->regs_lock); + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch b/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch new file mode 100644 index 000000000..3f4850c89 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1425-rp1-clk-Rationalise-the-use-of-the-CLK_IS_CRITICAL-f.patch @@ -0,0 +1,123 @@ +From eb836a6a299322a8e2b9627cccd23c7a76d068ba Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 8 Nov 2024 17:36:13 +0000 +Subject: [PATCH] rp1: clk: Rationalise the use of the CLK_IS_CRITICAL flag + +The clock setup had been copied from clk-bcm2835 which had to cope +with the firmware having configured clocks, so there were flags +of CLK_IS_CRITICAL and CLK_IGNORE_UNUSED dotted around. + +That isn't the situation with RP1 where only the main PLLs, CLK_SYS, +and CLK_SLOW_SYS are critical, so update the configuration to match. + +Signed-off-by: Dave Stevenson +--- + drivers/clk/clk-rp1.c | 41 ++++++----------------------------------- + 1 file changed, 6 insertions(+), 35 deletions(-) + +--- a/drivers/clk/clk-rp1.c ++++ b/drivers/clk/clk-rp1.c +@@ -1504,8 +1504,6 @@ static const struct clk_ops rp1_varsrc_o + .round_rate = rp1_varsrc_round_rate, + }; + +-static bool rp1_clk_is_claimed(const char *name); +- + static struct clk_hw *rp1_register_pll_core(struct rp1_clockman *clockman, + const void *data) + { +@@ -1521,7 +1519,7 @@ static struct clk_hw *rp1_register_pll_c + init.num_parents = 1; + init.name = pll_core_data->name; + init.ops = &rp1_pll_core_ops; +- init.flags = pll_core_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; ++ init.flags = pll_core_data->flags | CLK_IS_CRITICAL; + + pll_core = kzalloc(sizeof(*pll_core), GFP_KERNEL); + if (!pll_core) +@@ -1554,7 +1552,7 @@ static struct clk_hw *rp1_register_pll(s + init.num_parents = 1; + init.name = pll_data->name; + init.ops = &rp1_pll_ops; +- init.flags = pll_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL; ++ init.flags = pll_data->flags | CLK_IGNORE_UNUSED; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) +@@ -1635,11 +1633,6 @@ static struct clk_hw *rp1_register_pll_d + divider->div.hw.init = &init; + divider->div.table = pll_sec_div_table; + +- if (!rp1_clk_is_claimed(divider_data->source_pll)) +- init.flags |= CLK_IS_CRITICAL; +- if (!rp1_clk_is_claimed(divider_data->name)) +- divider->div.flags |= CLK_IS_CRITICAL; +- + divider->clockman = clockman; + divider->data = divider_data; + +@@ -1861,6 +1854,8 @@ static const struct rp1_clk_desc clk_des + .max_freq = 200 * MHz, + .fc0_src = FC_NUM(0, 4), + .clk_src_mask = 0x3, ++ /* Always enabled in hardware */ ++ .flags = CLK_IS_CRITICAL, + ), + + [RP1_CLK_SLOW_SYS] = REGISTER_CLK( +@@ -1875,6 +1870,8 @@ static const struct rp1_clk_desc clk_des + .max_freq = 50 * MHz, + .fc0_src = FC_NUM(1, 4), + .clk_src_mask = 0x1, ++ /* Always enabled in hardware */ ++ .flags = CLK_IS_CRITICAL, + ), + + [RP1_CLK_UART] = REGISTER_CLK( +@@ -2394,24 +2391,6 @@ static const struct rp1_clk_desc clk_des + [RP1_CLK_MIPI1_DSI_BYTECLOCK] = REGISTER_VARSRC("clksrc_mipi1_dsi_byteclk"), + }; + +-static bool rp1_clk_claimed[ARRAY_SIZE(clk_desc_array)]; +- +-static bool rp1_clk_is_claimed(const char *name) +-{ +- unsigned int i; +- +- for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) { +- if (clk_desc_array[i].data) { +- const char *clk_name = *(const char **)(clk_desc_array[i].data); +- +- if (!strcmp(name, clk_name)) +- return rp1_clk_claimed[i]; +- } +- } +- +- return false; +-} +- + static int rp1_clk_probe(struct platform_device *pdev) + { + const struct rp1_clk_desc *desc; +@@ -2422,7 +2401,6 @@ static int rp1_clk_probe(struct platform + const size_t asize = ARRAY_SIZE(clk_desc_array); + u32 chip_id, platform; + unsigned int i; +- u32 clk_id; + int ret; + + clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize), +@@ -2439,13 +2417,6 @@ static int rp1_clk_probe(struct platform + if (IS_ERR(clockman->regs)) + return PTR_ERR(clockman->regs); + +- memset(rp1_clk_claimed, 0, sizeof(rp1_clk_claimed)); +- for (i = 0; +- !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks", +- i, &clk_id); +- i++) +- rp1_clk_claimed[clk_id] = true; +- + platform_set_drvdata(pdev, clockman); + + clockman->onecell.num = asize; diff --git a/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch b/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch new file mode 100644 index 000000000..f213d8def --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1426-dt-arm64-Fixup-RP1-ethernet-DT-configuration.patch @@ -0,0 +1,50 @@ +From 0b4af929b7125abd3a262577b380c7c81ee9b1c5 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 11 Nov 2024 15:18:14 +0000 +Subject: [PATCH] dt: arm64: Fixup RP1 ethernet DT configuration + +Configure RP1's ethernet block to do the correct thing. +clk_eth is intended to be fixed at 125MHz, so use a new compatible, +and use assigned-clocks to configure the clock appropriately. + +Signed-off-by: Dave Stevenson +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -32,6 +32,7 @@ + // RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers + <&rp1_clocks RP1_PLL_SYS>, + <&rp1_clocks RP1_PLL_SYS_SEC>, ++ <&rp1_clocks RP1_CLK_ETH>, + <&rp1_clocks RP1_PLL_AUDIO>, + <&rp1_clocks RP1_PLL_AUDIO_SEC>, + <&rp1_clocks RP1_CLK_SYS>, +@@ -46,6 +47,7 @@ + <1536000000>, // RP1_PLL_AUDIO_CORE + <200000000>, // RP1_PLL_SYS + <125000000>, // RP1_PLL_SYS_SEC ++ <125000000>, // RP1_CLK_ETH + <61440000>, // RP1_PLL_AUDIO + <192000000>, // RP1_PLL_AUDIO_SEC + <200000000>, // RP1_CLK_SYS +@@ -976,12 +978,14 @@ + + rp1_eth: ethernet@100000 { + reg = <0xc0 0x40100000 0x0 0x4000>; +- compatible = "cdns,macb"; ++ compatible = "raspberrypi,rp1-gem", "cdns,macb"; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; +- clocks = <&macb_pclk &macb_hclk &rp1_clocks RP1_CLK_ETH_TSU>; +- clock-names = "pclk", "hclk", "tsu_clk"; ++ clocks = <&macb_pclk &macb_hclk ++ &rp1_clocks RP1_CLK_ETH_TSU ++ &rp1_clocks RP1_CLK_ETH>; ++ clock-names = "pclk", "hclk", "tsu_clk", "tx_clk"; + phy-mode = "rgmii-id"; + cdns,aw2w-max-pipe = /bits/ 8 <8>; + cdns,ar2r-max-pipe = /bits/ 8 <8>; diff --git a/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch b/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch new file mode 100644 index 000000000..b13becd18 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1427-clk-rp1-Add-RP1_CLK_DMA.patch @@ -0,0 +1,44 @@ +From d4e41ed9954fa86c4774f98d393aa401c81a68e7 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 13 Nov 2024 13:10:27 +0000 +Subject: [PATCH] clk: rp1: Add RP1_CLK_DMA. + +The DMA block has a clock, but wasn't defined in the driver. This +resulted in the parent being disabled as unused, and then DMA +stopped working. + +Signed-off-by: Dave Stevenson +--- + drivers/clk/clk-rp1.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/clk/clk-rp1.c ++++ b/drivers/clk/clk-rp1.c +@@ -1874,6 +1874,27 @@ static const struct rp1_clk_desc clk_des + .flags = CLK_IS_CRITICAL, + ), + ++ [RP1_CLK_DMA] = REGISTER_CLK( ++ .name = "clk_dma", ++ .parents = {"pll_sys_pri_ph", ++ "pll_video", ++ "xosc", ++ "clksrc_gp0", ++ "clksrc_gp1", ++ "clksrc_gp2", ++ "clksrc_gp3", ++ "clksrc_gp4", ++ "clksrc_gp5"}, ++ .num_std_parents = 0, ++ .num_aux_parents = 9, ++ .ctrl_reg = CLK_DMA_CTRL, ++ .div_int_reg = CLK_DMA_DIV_INT, ++ .sel_reg = CLK_DMA_SEL, ++ .div_int_max = DIV_INT_8BIT_MAX, ++ .max_freq = 100 * MHz, ++ .fc0_src = FC_NUM(2, 2), ++ ), ++ + [RP1_CLK_UART] = REGISTER_CLK( + .name = "clk_uart", + .parents = {"pll_sys_pri_ph", diff --git a/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch b/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch new file mode 100644 index 000000000..662a2485d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1428-rp1-clk-Remove-CLK_IGNORE_UNUSED-flags.patch @@ -0,0 +1,59 @@ +From 9049e4df2c54b5e620f855f66db3a18c9f2e181f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 8 Nov 2024 17:37:08 +0000 +Subject: [PATCH] rp1: clk: Remove CLK_IGNORE_UNUSED flags + +There should be no issue in disabling the RP1 clocks as long as +the kernel knows about all consumers. + +Signed-off-by: Dave Stevenson +--- + drivers/clk/clk-rp1.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/clk/clk-rp1.c ++++ b/drivers/clk/clk-rp1.c +@@ -1552,7 +1552,7 @@ static struct clk_hw *rp1_register_pll(s + init.num_parents = 1; + init.name = pll_data->name; + init.ops = &rp1_pll_ops; +- init.flags = pll_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = pll_data->flags; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) +@@ -1586,7 +1586,7 @@ static struct clk_hw *rp1_register_pll_p + init.num_parents = 1; + init.name = ph_data->name; + init.ops = &rp1_pll_ph_ops; +- init.flags = ph_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = ph_data->flags; + + ph = kzalloc(sizeof(*ph), GFP_KERNEL); + if (!ph) +@@ -1619,7 +1619,7 @@ static struct clk_hw *rp1_register_pll_d + init.num_parents = 1; + init.name = divider_data->name; + init.ops = &rp1_pll_divider_ops; +- init.flags = divider_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = divider_data->flags; + + divider = devm_kzalloc(clockman->dev, sizeof(*divider), GFP_KERNEL); + if (!divider) +@@ -1662,7 +1662,7 @@ static struct clk_hw *rp1_register_clock + init.num_parents = + clock_data->num_std_parents + clock_data->num_aux_parents; + init.name = clock_data->name; +- init.flags = clock_data->flags | CLK_IGNORE_UNUSED; ++ init.flags = clock_data->flags; + init.ops = &rp1_clk_ops; + + clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL); +@@ -1692,7 +1692,6 @@ static struct clk_hw *rp1_register_varsr + init.parent_names = &ref_clock; + init.num_parents = 1; + init.name = name; +- init.flags = CLK_IGNORE_UNUSED; + init.ops = &rp1_varsrc_ops; + + clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL); diff --git a/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch b/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch new file mode 100644 index 000000000..937b12571 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1429-dt-rp1-Use-clk_sys-for-ethernet-hclk-and-pclk.patch @@ -0,0 +1,45 @@ +From 542d0f7f2e9f90fc0f02f8cb141f7c3fbf46081b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 11 Nov 2024 17:11:18 +0000 +Subject: [PATCH] dt: rp1: Use clk_sys for ethernet hclk and pclk + +hclk and pclk of the MAC are connected to clk_sys, so define +them as being connected accordingly, rather than having fake +fixed clocks for them. + +Signed-off-by: Dave Stevenson +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 15 ++------------- + 1 file changed, 2 insertions(+), 13 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -982,7 +982,8 @@ + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; +- clocks = <&macb_pclk &macb_hclk ++ clocks = <&rp1_clocks RP1_CLK_SYS ++ &rp1_clocks RP1_CLK_SYS + &rp1_clocks RP1_CLK_ETH_TSU + &rp1_clocks RP1_CLK_ETH>; + clock-names = "pclk", "hclk", "tsu_clk", "tx_clk"; +@@ -1230,18 +1231,6 @@ + clock-output-names = "xosc"; + clock-frequency = <50000000>; + }; +- macb_pclk: macb_pclk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-output-names = "pclk"; +- clock-frequency = <200000000>; +- }; +- macb_hclk: macb_hclk { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-output-names = "hclk"; +- clock-frequency = <200000000>; +- }; + sdio_src: sdio_src { + // 400 MHz on FPGA. PLL sys VCO on asic + compatible = "fixed-clock"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch b/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch new file mode 100644 index 000000000..623fcc6cc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1430-dt-rp1-Link-RP1-DMA-to-the-associated-clock.patch @@ -0,0 +1,24 @@ +From efecbda4014b490e042c7fd090942b32316f9345 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 13 Nov 2024 13:11:33 +0000 +Subject: [PATCH] dt: rp1: Link RP1 DMA to the associated clock + +This makes the kernel representation of the clock structure +match reality. + +Signed-off-by: Dave Stevenson +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -1081,7 +1081,7 @@ + reg = <0xc0 0x40188000 0x0 0x1000>; + compatible = "snps,axi-dma-1.01a"; + interrupts = ; +- clocks = <&sdhci_core &rp1_clocks RP1_CLK_SYS>; ++ clocks = <&rp1_clocks RP1_CLK_DMA &rp1_clocks RP1_CLK_SYS>; + clock-names = "core-clk", "cfgr-clk"; + + #dma-cells = <1>; diff --git a/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch b/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch new file mode 100644 index 000000000..7e518bff1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1431-raspberrypi-firmware-Add-the-RPI-firmware-UART-APIs.patch @@ -0,0 +1,23 @@ +From eb035f3ad7da1324d310ef83b42398f47d5bafe7 Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Fri, 1 Nov 2024 19:42:17 +0000 +Subject: [PATCH] raspberrypi-firmware: Add the RPI firmware UART APIs + +Add VideoCore mailbox definitions for the new RPi firmware UART. + +Signed-off-by: Tim Gover +--- + include/soc/bcm2835/raspberrypi-firmware.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -98,6 +98,8 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_GET_REBOOT_FLAGS = 0x00030064, + RPI_FIRMWARE_SET_REBOOT_FLAGS = 0x00038064, + RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066, ++ RPI_FIRMWARE_GET_SW_UART = 0x0003008a, ++ RPI_FIRMWARE_SET_SW_UART = 0x0003808a, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, diff --git a/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch b/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch new file mode 100644 index 000000000..5767f407f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1432-serial-core-Add-the-Raspberry-Pi-firmware-UART-id.patch @@ -0,0 +1,22 @@ +From b8a0e563fd181205565a0edaaebc82b1abf0c5be Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Fri, 1 Nov 2024 19:43:21 +0000 +Subject: [PATCH] serial: core: Add the Raspberry Pi firmware UART id + +Assign a new serial core number for the RPi firmware UART. + +Signed-off-by: Tim Gover +--- + include/uapi/linux/serial_core.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -245,4 +245,7 @@ + /* Sunplus UART */ + #define PORT_SUNPLUS 123 + ++/* RPi firmware UART */ ++#define PORT_RPI_FW 124 ++ + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch b/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch new file mode 100644 index 000000000..f26c1bee8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1433-serial-tty-Add-a-driver-for-the-RPi-firmware-UART.patch @@ -0,0 +1,630 @@ +From 2548d954d78bca44c5cf430f8ea6de7c771312d7 Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Wed, 28 Aug 2024 09:46:50 +0100 +Subject: [PATCH] serial: tty: Add a driver for the RPi firmware UART + +On Raspberry Pi 4 and earlier models the firmware provides +a low speed (up to 115200 baud) bit-bashed UART on arbitrary +GPIOs using the second VPU core. + +The firmware driver is designed to support 19200 baud. Higher +rates up to 115200 seem to work but there may be more jitter. + +This can be useful for debug or managing additional low +speed peripherals if the hardware PL011 and 8250 hardware +UARTs are already used for console / bluetooth. + +The firmware driver requires a fixed core clock frequency +and also requires the VPU PWM audio driver to be disabled +(dtparam=audio=off) + +Runtime configuration is handled via the vc-mailbox APIs +with the FIFO buffers being allocated in uncached VPU +addressable memory. The FIFO pointers are stored in spare +VideoCore multi-core sync registers in order to reduce the number +of uncached SDRAM accesses thereby reducing jitter. + +Signed-off-by: Tim Gover +--- + drivers/tty/serial/Kconfig | 11 + + drivers/tty/serial/Makefile | 1 + + drivers/tty/serial/rpi-fw-uart.c | 563 +++++++++++++++++++++++++++++++ + 3 files changed, 575 insertions(+) + create mode 100644 drivers/tty/serial/rpi-fw-uart.c + +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1578,6 +1578,17 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE + but you can alter that using a kernel command line option such as + "console=ttyNVTx". + ++config SERIAL_RPI_FW ++ tristate "Raspberry Pi Firmware software UART support" ++ depends on ARM_AMBA || COMPILE_TEST ++ select SERIAL_CORE ++ help ++ This selects the Raspberry Pi firmware UART. This is a bit-bashed ++ implementation running on the Raspbery Pi VPU core. ++ This is not supported on Raspberry Pi 5 or newer platforms. ++ ++ If unsure, say N. ++ + endmenu + + config SERIAL_MCTRL_GPIO +--- a/drivers/tty/serial/Makefile ++++ b/drivers/tty/serial/Makefile +@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += mi + obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o + obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o + obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o ++obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o + + # GPIOLIB helpers for modem control lines + obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o +--- /dev/null ++++ b/drivers/tty/serial/rpi-fw-uart.c +@@ -0,0 +1,563 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2024, Raspberry Pi Ltd. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RPI_FW_UART_RX_FIFO_RD 0xb0 ++#define RPI_FW_UART_RX_FIFO_WR 0xb4 ++#define RPI_FW_UART_TX_FIFO_RD 0xb8 ++#define RPI_FW_UART_TX_FIFO_WR 0xbc ++ ++#define RPI_FW_UART_FIFO_SIZE 32 ++#define RPI_FW_UART_FIFO_SIZE_MASK (RPI_FW_UART_FIFO_SIZE - 1) ++ ++#define RPI_FW_UART_MIN_VERSION 3 ++ ++struct rpi_fw_uart_params { ++ u32 start; ++ u32 baud; ++ u32 data_bits; ++ u32 stop_bits; ++ u32 gpio_rx; ++ u32 gpio_tx; ++ u32 flags; ++ u32 fifosize; ++ u32 rx_buffer; ++ u32 tx_buffer; ++ u32 version; ++ u32 fifo_reg_base; ++}; ++ ++struct rpi_fw_uart { ++ struct uart_driver driver; ++ struct uart_port port; ++ struct rpi_firmware *firmware; ++ struct gpio_desc *rx_gpiod; ++ struct gpio_desc *tx_gpiod; ++ unsigned int rx_gpio; ++ unsigned int tx_gpio; ++ unsigned int baud; ++ unsigned int data_bits; ++ unsigned int stop_bits; ++ unsigned char __iomem *base; ++ size_t dma_buffer_size; ++ ++ struct hrtimer trigger_start_rx; ++ ktime_t rx_poll_delay; ++ void *rx_buffer; ++ dma_addr_t rx_buffer_dma_addr; ++ int rx_stop; ++ ++ void *tx_buffer; ++ dma_addr_t tx_buffer_dma_addr; ++}; ++ ++static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd; ++} ++ ++static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->tx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u32 rd, wr; ++ ++ if (!rfu->rx_buffer) ++ return 1; ++ ++ rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR); ++ ++ return rd == wr; ++} ++ ++static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port) ++{ ++ return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0; ++} ++ ++static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ /* ++ * No hardware flow control, firmware automatically configures ++ * TX to output high and RX to input low. ++ */ ++ dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl); ++} ++ ++static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port) ++{ ++ /* No hardware flow control */ ++ return TIOCM_CTS; ++} ++ ++static void rpi_fw_uart_stop(struct uart_port *port) ++{ ++ struct rpi_fw_uart_params msg = {.start = 0}; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ hrtimer_cancel(&rfu->trigger_start_rx); ++ ++ if (rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg))) ++ dev_warn(port->dev, ++ "Failed to shutdown rpi-fw uart. Firmware not configured?"); ++} ++ ++static void rpi_fw_uart_stop_tx(struct uart_port *port) ++{ ++ /* No supported by the current firmware APIs. */ ++} ++ ++static void rpi_fw_uart_stop_rx(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ rfu->rx_stop = 1; ++} ++ ++static unsigned int rpi_fw_write(struct uart_port *port, const char *s, ++ unsigned int count) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ u8 *out = rfu->tx_buffer; ++ unsigned int consumed = 0; ++ ++ while (consumed < count && !rpi_fw_uart_tx_is_full(port)) { ++ u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ out[wp] = s[consumed++]; ++ wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR); ++ } ++ return consumed; ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_tx(struct uart_port *port) ++{ ++ struct circ_buf *xmit; ++ ++ xmit = &port->state->xmit; ++ for (;;) { ++ unsigned int consumed; ++ unsigned long count = CIRC_CNT_TO_END(xmit->head, xmit->tail, ++ UART_XMIT_SIZE); ++ if (!count) ++ break; ++ ++ consumed = rpi_fw_write(port, &xmit->buf[xmit->tail], count); ++ uart_xmit_advance(port, consumed); ++ } ++ uart_write_wakeup(port); ++} ++ ++/* Called with port.lock taken */ ++static void rpi_fw_uart_start_rx(struct uart_port *port) ++{ ++ struct tty_port *tty_port = &port->state->port; ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ int count = 0; ++ ++ /* ++ * RX is polled, read up to a full buffer of data before trying again ++ * so that this can be interrupted if the firmware is filling the ++ * buffer too fast ++ */ ++ while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) { ++ const u8 *in = rfu->rx_buffer; ++ u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD) ++ & RPI_FW_UART_FIFO_SIZE_MASK; ++ ++ tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL); ++ rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK; ++ writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD); ++ count++; ++ } ++ if (count) ++ tty_flip_buffer_push(tty_port); ++} ++ ++static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t) ++{ ++ unsigned long flags; ++ struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart, ++ trigger_start_rx); ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ if (rfu->rx_stop) { ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return HRTIMER_NORESTART; ++ } ++ ++ rpi_fw_uart_start_rx(&rfu->port); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ hrtimer_forward_now(t, rfu->rx_poll_delay); ++ return HRTIMER_RESTART; ++} ++ ++static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl) ++{ ++ dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl); ++} ++ ++static int rpi_fw_uart_configure(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ struct rpi_fw_uart_params msg; ++ unsigned long flags; ++ int rc; ++ ++ rpi_fw_uart_stop(port); ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.start = 1; ++ msg.gpio_rx = rfu->rx_gpio; ++ msg.gpio_tx = rfu->tx_gpio; ++ msg.data_bits = rfu->data_bits; ++ msg.stop_bits = rfu->stop_bits; ++ msg.baud = rfu->baud; ++ msg.fifosize = RPI_FW_UART_FIFO_SIZE; ++ msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr; ++ msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr; ++ ++ rfu->rx_poll_delay = ms_to_ktime(50); ++ ++ /* ++ * Reconfigures the firmware UART with the new settings. On the first ++ * call retrieve the addresses of the FIFO buffers. The buffers are ++ * allocated at startup and are not de-allocated. ++ * NB rpi_firmware_property can block ++ */ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_SET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ rc = rpi_firmware_property(rfu->firmware, ++ RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (rc) ++ goto fail; ++ ++ dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version, ++ msg.fifo_reg_base); ++ ++ dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", ++ msg.start, msg.baud, msg.data_bits, msg.stop_bits, ++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); ++ ++ if (msg.fifosize != port->fifosize) { ++ dev_err(port->dev, "Expected fifo size %u actual %u", ++ port->fifosize, msg.fifosize); ++ rc = -EINVAL; ++ goto fail; ++ } ++ ++ if (!msg.start) { ++ dev_err(port->dev, "Firmware service not running\n"); ++ rc = -EINVAL; ++ } ++ ++ spin_lock_irqsave(&rfu->port.lock, flags); ++ rfu->rx_stop = 0; ++ hrtimer_start(&rfu->trigger_start_rx, ++ rfu->rx_poll_delay, HRTIMER_MODE_REL); ++ spin_unlock_irqrestore(&rfu->port.lock, flags); ++ return 0; ++fail: ++ dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?"); ++ return rc; ++} ++ ++static void rpi_fw_uart_free_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->rx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->rx_buffer, GFP_ATOMIC); ++ ++ if (rfu->tx_buffer) ++ dma_free_coherent(port->dev, rfu->dma_buffer_size, ++ rfu->tx_buffer, GFP_ATOMIC); ++ ++ rfu->rx_buffer = NULL; ++ rfu->tx_buffer = NULL; ++ rfu->rx_buffer_dma_addr = 0; ++ rfu->tx_buffer_dma_addr = 0; ++} ++ ++static int rpi_fw_uart_alloc_buffers(struct uart_port *port) ++{ ++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port); ++ ++ if (rfu->tx_buffer) ++ return 0; ++ ++ rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE); ++ ++ rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->rx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->rx_buffer) ++ goto alloc_fail; ++ ++ rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size, ++ &rfu->tx_buffer_dma_addr, GFP_ATOMIC); ++ ++ if (!rfu->tx_buffer) ++ goto alloc_fail; ++ ++ dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n", ++ rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr, ++ rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr); ++ return 0; ++ ++alloc_fail: ++ dev_err(port->dev, "%s uart buffer allocation failed\n", __func__); ++ rpi_fw_uart_free_buffers(port); ++ return -ENOMEM; ++} ++ ++static int rpi_fw_uart_startup(struct uart_port *port) ++{ ++ int rc; ++ ++ rc = rpi_fw_uart_alloc_buffers(port); ++ if (rc) ++ dev_err(port->dev, "Failed to start\n"); ++ return rc; ++} ++ ++static void rpi_fw_uart_shutdown(struct uart_port *port) ++{ ++ rpi_fw_uart_stop(port); ++ rpi_fw_uart_free_buffers(port); ++} ++ ++static void rpi_fw_uart_set_termios(struct uart_port *port, ++ struct ktermios *new, ++ const struct ktermios *old) ++{ ++ struct rpi_fw_uart *rfu = ++ container_of(port, struct rpi_fw_uart, port); ++ rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200); ++ rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1; ++ ++ rpi_fw_uart_configure(port); ++} ++ ++static const struct uart_ops rpi_fw_uart_ops = { ++ .tx_empty = rpi_fw_uart_tx_empty, ++ .set_mctrl = rpi_fw_uart_set_mctrl, ++ .get_mctrl = rpi_fw_uart_get_mctrl, ++ .stop_rx = rpi_fw_uart_stop_rx, ++ .stop_tx = rpi_fw_uart_stop_tx, ++ .start_tx = rpi_fw_uart_start_tx, ++ .break_ctl = rpi_fw_uart_break_ctl, ++ .startup = rpi_fw_uart_startup, ++ .shutdown = rpi_fw_uart_shutdown, ++ .set_termios = rpi_fw_uart_set_termios, ++}; ++ ++static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name) ++{ ++ struct of_phandle_args of_args = { 0 }; ++ bool is_bcm28xx; ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n"); ++ ++ is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") || ++ of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio"); ++ of_node_put(of_args.np); ++ if (!is_bcm28xx || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n"); ++ ++ return of_args.args[0]; ++} ++ ++static int rpi_fw_uart_probe(struct platform_device *pdev) ++{ ++ struct device_node *firmware_node; ++ struct device *dev = &pdev->dev; ++ struct rpi_firmware *firmware; ++ struct uart_port *port; ++ struct rpi_fw_uart *rfu; ++ struct rpi_fw_uart_params msg; ++ int version_major; ++ int err; ++ ++ dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node); ++ ++ /* ++ * We can be probed either through the an old-fashioned ++ * platform device registration or through a DT node that is a ++ * child of the firmware node. Handle both cases. ++ */ ++ if (dev->of_node) ++ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0); ++ else ++ firmware_node = of_find_compatible_node(NULL, NULL, ++ "raspberrypi,bcm2835-firmware"); ++ if (!firmware_node) { ++ dev_err(dev, "Missing firmware node\n"); ++ return -ENOENT; ++ } ++ ++ firmware = devm_rpi_firmware_get(dev, firmware_node); ++ of_node_put(firmware_node); ++ if (!firmware) ++ return -EPROBE_DEFER; ++ ++ rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL); ++ if (!rfu) ++ return -ENOMEM; ++ ++ rfu->firmware = firmware; ++ ++ err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART, ++ &msg, sizeof(msg)); ++ if (err) { ++ dev_err(dev, "VC firmware does not support rpi-fw-uart\n"); ++ return err; ++ } ++ ++ version_major = msg.version >> 16; ++ if (msg.version < RPI_FW_UART_MIN_VERSION) { ++ dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n", ++ version_major, RPI_FW_UART_MIN_VERSION); ++ return -EINVAL; ++ } ++ ++ rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN); ++ if (IS_ERR(rfu->rx_gpiod)) ++ return PTR_ERR(rfu->rx_gpiod); ++ ++ rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH); ++ if (IS_ERR(rfu->tx_gpiod)) ++ return PTR_ERR(rfu->tx_gpiod); ++ ++ rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios"); ++ if (rfu->rx_gpio < 0) ++ return rfu->rx_gpio; ++ rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios"); ++ if (rfu->tx_gpio < 0) ++ return rfu->tx_gpio; ++ ++ rfu->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(rfu->base)) ++ return PTR_ERR(rfu->base); ++ ++ /* setup the driver */ ++ rfu->driver.owner = THIS_MODULE; ++ rfu->driver.driver_name = "ttyRFU"; ++ rfu->driver.dev_name = "ttyRFU"; ++ rfu->driver.nr = 1; ++ rfu->data_bits = 8; ++ ++ /* RX is polled */ ++ hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx; ++ ++ err = uart_register_driver(&rfu->driver); ++ if (err) { ++ dev_err(dev, "failed to register UART driver: %d\n", ++ err); ++ return err; ++ } ++ ++ /* setup the port */ ++ port = &rfu->port; ++ spin_lock_init(&port->lock); ++ port->dev = &pdev->dev; ++ port->type = PORT_RPI_FW; ++ port->ops = &rpi_fw_uart_ops; ++ port->fifosize = RPI_FW_UART_FIFO_SIZE; ++ port->iotype = UPIO_MEM; ++ port->flags = UPF_BOOT_AUTOCONF; ++ port->private_data = rfu; ++ ++ err = uart_add_one_port(&rfu->driver, port); ++ if (err) { ++ dev_err(dev, "failed to add UART port: %d\n", err); ++ goto unregister_uart; ++ } ++ platform_set_drvdata(pdev, rfu); ++ ++ dev_info(dev, "version %d.%d gpios tx %u rx %u\n", ++ msg.version >> 16, msg.version & 0xffff, ++ rfu->tx_gpio, rfu->rx_gpio); ++ return 0; ++ ++unregister_uart: ++ uart_unregister_driver(&rfu->driver); ++ ++ return err; ++} ++ ++static int rpi_fw_uart_remove(struct platform_device *pdev) ++{ ++ struct rpi_fw_uart *rfu = platform_get_drvdata(pdev); ++ ++ uart_remove_one_port(&rfu->driver, &rfu->port); ++ uart_unregister_driver(&rfu->driver); ++ ++ return 0; ++} ++ ++static const struct of_device_id rpi_fw_match[] = { ++ { .compatible = "raspberrypi,firmware-uart" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpi_fw_match); ++ ++static struct platform_driver rpi_fw_driver = { ++ .driver = { ++ .name = "rpi_fw-uart", ++ .of_match_table = rpi_fw_match, ++ }, ++ .probe = rpi_fw_uart_probe, ++ .remove = rpi_fw_uart_remove, ++}; ++module_platform_driver(rpi_fw_driver); ++ ++MODULE_AUTHOR("Tim Gover "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch b/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch new file mode 100644 index 000000000..0e4b3e374 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1435-dtoverlay-Add-an-overlay-for-the-Raspberry-Pi-firmwa.patch @@ -0,0 +1,95 @@ +From b6b126861062020fb50859c5af71d8846ce43d7c Mon Sep 17 00:00:00 2001 +From: Tim Gover +Date: Mon, 4 Nov 2024 13:44:10 +0000 +Subject: [PATCH] dtoverlay: Add an overlay for the Raspberry Pi firmware UART + +Add a device-tree overlay to configure the GPIOs for the +Raspberry Pi firmware UART. + +Example config.txt +dtoverlay=rpi-fw-uart,txd0_pin=20,rxd0_pin=21 + +Signed-off-by: Phil Elwell +Signed-off-by: Tim Gover +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 12 ++++++ + .../boot/dts/overlays/rpi-fw-uart-overlay.dts | 41 +++++++++++++++++++ + 3 files changed, 54 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -233,6 +233,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + rpi-dacpro.dtbo \ + rpi-digiampplus.dtbo \ + rpi-ft5406.dtbo \ ++ rpi-fw-uart.dtbo \ + rpi-poe.dtbo \ + rpi-poe-plus.dtbo \ + rpi-sense.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4141,6 +4141,18 @@ Params: touchscreen-size-x Touchscr + touchscreen-swapped-x-y Swap X and Y cordinates (default 0); + + ++Name: rpi-fw-uart ++Info: Configures the firmware software UART driver. ++ This driver requires exclusive usage of the second VPU core. The ++ following config.txt entries should be set when this driver is used. ++ dtparam=audio=off ++ isp_use_vpu0=1 ++Load: dtoverlay=rpi-fw-uart,[=] ++Params: txd0_pin GPIO pin for TXD0 (any free - default 20) ++ ++ rxd0_pin GPIO pin for RXD0 (any free - default 21) ++ ++ + Name: rpi-poe + Info: Raspberry Pi PoE HAT fan + Load: dtoverlay=rpi-poe,[=] +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Overlay for the Raspberry Pi Firmware UART driver ++/dts-v1/; ++/plugin/; ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_fw_uart_pins: rpi_fw_uart_pins@4 { ++ brcm,pins = <20 21>; ++ brcm,function = <1 0>; /* output input */ ++ brcm,pull = <0 2>; /* none pull-up */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&soc>; ++ __overlay__ { ++ rpi_fw_uart: rpi_fw_uart@7e000000 { ++ compatible = "raspberrypi,firmware-uart"; ++ reg = <0x7e000000 0x100>; /* VideoCore MS sync regs */ ++ firmware = <&firmware>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_fw_uart_pins>; ++ tx-gpios = <&gpio 20 0>; ++ rx-gpios = <&gpio 21 0>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ txd0_pin = <&rpi_fw_uart>,"tx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:0"; ++ rxd0_pin = <&rpi_fw_uart>,"rx-gpios:4", ++ <&rpi_fw_uart_pins>, "brcm,pins:4"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch b/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch new file mode 100644 index 000000000..7829a4b75 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1436-ARM-dts-Remove-duplicate-tags.patch @@ -0,0 +1,142 @@ +From 1993b453dc4a62378e90d91e9e0006a6c085f38a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 18 Sep 2024 10:23:41 +0100 +Subject: [PATCH] ARM: dts: Remove duplicate tags + +A dts file should have exactly one /dts-v1/ tag, and overlays should +also have one /plugin/ tag. Through careless inclusion of other files, +some Device Trees and overlays end up with duplicated tags - this +commit removes them. + +The change is largely cosmetic, unless using an old version of dtc. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts | 1 - + arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi | 3 --- + arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi | 2 -- + arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts | 3 --- + arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts | 3 --- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 1 - + 11 files changed, 28 deletions(-) + +--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts ++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts +@@ -1,5 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 +-/dts-v1/; + #include "bcm2711-rpi-4-b.dts" + + / { +--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi +@@ -1,7 +1,4 @@ + // Definitions for I2C based sensors using the Industrial IO or HWMON interface. +-/dts-v1/; +-/plugin/; +- + #include + + / { +--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi ++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi +@@ -1,8 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Partial definitions for IMX290 or IMX327 camera module on VC I2C bus + // The compatible string should be set in an overlay that then includes this one +-/dts-v1/; +-/plugin/; + + #include + +--- a/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts +@@ -17,9 +17,6 @@ + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +-/dts-v1/; +-/plugin/; +- + #include "pisound-overlay.dts" + + &pisound_spi { +--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-fkms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include "cma-overlay.dts" + + / { +--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-fkms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include "cma-overlay.dts" + + &frag0 { +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-kms-v3d-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include + + #include "cma-overlay.dts" +--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts +@@ -2,9 +2,6 @@ + * vc4-kms-v3d-pi4-overlay.dts + */ + +-/dts-v1/; +-/plugin/; +- + #include + + #include "cma-overlay.dts" +--- a/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts +@@ -1,6 +1,3 @@ +-/dts-v1/; +-/plugin/; +- + #include "w1-gpio-overlay.dts" + + / { +--- a/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts ++++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts +@@ -1,6 +1,3 @@ +-/dts-v1/; +-/plugin/; +- + #include "w1-gpio-pullup-overlay.dts" + + / { +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi +@@ -1,5 +1,4 @@ + // SPDX-License-Identifier: GPL-2.0 +-/dts-v1/; + + #include "bcm2712-rpi-cm5.dtsi" + diff --git a/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch b/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch new file mode 100644 index 000000000..1d14a0bfe --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1437-Allow-setting-I-C-clock-frequency-via-i2c_arm_baudra.patch @@ -0,0 +1,25 @@ +From e33702e5e5fe9fef6ec967961e2e5e1c2285ba36 Mon Sep 17 00:00:00 2001 +From: gtrainavicius +Date: Wed, 4 Dec 2024 11:18:14 +0200 +Subject: [PATCH] =?UTF-8?q?Allow=20setting=20I=C2=B2C=20clock=20frequency?= + =?UTF-8?q?=20via=20i2c=5Farm=5Fbaudrate=20dtparam=20when=20using=20pimidi?= + =?UTF-8?q?=20overlay.?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This change removes the forced 1MHz clock frequency, so it can be overridden using `i2c_arm_baudrate`. +--- + arch/arm/boot/dts/overlays/pimidi-overlay.dts | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/pimidi-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts +@@ -26,7 +26,6 @@ + target = <&i2c_arm>; + __overlay__ { + status = "okay"; +- clock-frequency=<1000000>; + + pimidi_ctrl: pimidi_ctrl@20 { + compatible = "blokaslabs,pimidi"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch b/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch new file mode 100644 index 000000000..02c4dc449 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1438-nvme-pci-Disable-Host-Memory-Buffer-usage.patch @@ -0,0 +1,48 @@ +From fda47c026dee7acd975ee2c0f7a440d4038cfaa3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 3 Dec 2024 15:57:01 +0000 +Subject: [PATCH] nvme-pci: Disable Host Memory Buffer usage + +Some NVME drives seem to request significant amounts of DMA coherent +memory - enough to exhaust our standard 64MB CMA allocation. + +Try disabling the feature to see what effect it has - drives should +continue to function without it. + +Link: https://github.com/raspberrypi/linux/issues/6504 + +Signed-off-by: Phil Elwell +--- + drivers/nvme/host/pci.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -1948,6 +1948,7 @@ static void nvme_free_host_mem(struct nv + dev->nr_host_mem_descs = 0; + } + ++#if 0 + static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + u32 chunk_size) + { +@@ -2016,9 +2017,11 @@ out: + dev->host_mem_descs = NULL; + return -ENOMEM; + } ++#endif + + static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) + { ++#if 0 + u64 min_chunk = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES); + u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2); + u64 chunk_size; +@@ -2031,6 +2034,7 @@ static int nvme_alloc_host_mem(struct nv + nvme_free_host_mem(dev); + } + } ++#endif + + return -ENOMEM; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch b/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch new file mode 100644 index 000000000..0f18dc4a8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1439-fixup-serial-tty-Add-a-driver-for-the-RPi-firmware-U.patch @@ -0,0 +1,23 @@ +From 0313a0961b685973f7833017479a277e3a4c05a4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 4 Dec 2024 14:40:59 +0000 +Subject: [PATCH] fixup! serial: tty: Add a driver for the RPi firmware UART + +Make SERIAL_RPI_FW depend on RASPBERRYPI_FIRMWARE. + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1580,7 +1580,7 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE + + config SERIAL_RPI_FW + tristate "Raspberry Pi Firmware software UART support" +- depends on ARM_AMBA || COMPILE_TEST ++ depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST + select SERIAL_CORE + help + This selects the Raspberry Pi firmware UART. This is a bit-bashed diff --git a/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch b/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch new file mode 100644 index 000000000..271f74328 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1440-serial-rpi-fw-uart-Demote-debug-log-messages.patch @@ -0,0 +1,28 @@ +From 0a5be0fe6ba3a981508421131def7eab55d6d75c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 5 Dec 2024 12:08:23 +0000 +Subject: [PATCH] serial: rpi-fw-uart: Demote debug log messages + +A dev_info call in rpi_fw_uart_configure causes kernel log output every +time one opens the UART. Demote it to dev_dbg. + +Signed-off-by: Phil Elwell +--- + drivers/tty/serial/rpi-fw-uart.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/tty/serial/rpi-fw-uart.c ++++ b/drivers/tty/serial/rpi-fw-uart.c +@@ -277,9 +277,9 @@ static int rpi_fw_uart_configure(struct + dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version, + msg.fifo_reg_base); + +- dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", +- msg.start, msg.baud, msg.data_bits, msg.stop_bits, +- msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); ++ dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n", ++ msg.start, msg.baud, msg.data_bits, msg.stop_bits, ++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize); + + if (msg.fifosize != port->fifosize) { + dev_err(port->dev, "Expected fifo size %u actual %u", diff --git a/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch b/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch new file mode 100644 index 000000000..354ff961f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1441-dtoverlays-Add-Arducam-override-for-ov9281.patch @@ -0,0 +1,55 @@ +From 02dee262a9c7295ea514e9db7b9aa4b239922cb3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 2 Dec 2024 15:41:21 +0000 +Subject: [PATCH] dtoverlays: Add Arducam override for ov9281 + +The Arducam module is slow starting up, so add an override +to slow the regulator down. +https://forums.raspberrypi.com/viewtopic.php?t=380236 + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 2 ++ + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 13 ++++++++++++- + 2 files changed, 14 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3538,6 +3538,8 @@ Params: rotation Mounting + configuring the sensor (default on) + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). ++ arducam Slow down the regulator for slow Arducam ++ modules. + + + Name: papirus +--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts +@@ -57,6 +57,14 @@ + }; + }; + ++ reg_frag: fragment@5 { ++ target = <&cam1_reg>; ++ __dormant__ { ++ startup-delay-us = <20000>; ++ off-on-delay-us = <30000>; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; +@@ -65,7 +73,10 @@ + <&csi_frag>, "target:0=",<&csi0>, + <&clk_frag>, "target:0=",<&cam0_clk>, + <&cam_node>, "clocks:0=",<&cam0_clk>, +- <&cam_node>, "avdd-supply:0=",<&cam0_reg>; ++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>, ++ <®_frag>, "target:0=",<&cam0_reg>; ++ arducam = <0>, "+5"; ++ + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch b/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch new file mode 100644 index 000000000..d13982e9b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1442-drivers-input-touchscreen-Add-support-for-no-irq-to-.patch @@ -0,0 +1,118 @@ +From 97638920f1a40e2e0cab363d1e03837ff50c5478 Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 17:19:23 +0800 +Subject: [PATCH] drivers:input:touchscreen: Add support for no irq to ili210x + driver + +Signed-off-by: eng33 +--- + drivers/input/touchscreen/ili210x.c | 63 ++++++++++++++++++++++++----- + 1 file changed, 52 insertions(+), 11 deletions(-) + +--- a/drivers/input/touchscreen/ili210x.c ++++ b/drivers/input/touchscreen/ili210x.c +@@ -67,6 +67,8 @@ struct ili210x { + u8 version_proto[2]; + u8 ic_mode[2]; + bool stop; ++ struct timer_list poll_timer; ++ struct work_struct poll_work; + }; + + static int ili210x_read_reg(struct i2c_client *client, +@@ -360,6 +362,34 @@ static irqreturn_t ili210x_irq(int irq, + return IRQ_HANDLED; + } + ++static void ili210x_poll_work(struct work_struct *work) ++{ ++ struct ili210x *priv = container_of(work, struct ili210x, poll_work); ++ struct i2c_client *client = priv->client; ++ const struct ili2xxx_chip *chip = priv->chip; ++ u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; ++ bool touch; ++ int error; ++ ++ error = chip->get_touch_data(client, touchdata); ++ if (error) { ++ dev_err(&client->dev, "Unable to get touch data: %d\n", error); ++ return; ++ } ++ ++ touch = ili210x_report_events(priv, touchdata); ++} ++ ++static void ili210x_poll_timer_callback(struct timer_list *t) ++{ ++ struct ili210x *priv = from_timer(priv, t, poll_timer); ++ ++ schedule_work(&priv->poll_work); ++ ++ if (!priv->stop) ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++} ++ + static int ili251x_firmware_update_resolution(struct device *dev) + { + struct i2c_client *client = to_i2c_client(dev); +@@ -945,11 +975,6 @@ static int ili210x_i2c_probe(struct i2c_ + return -ENODEV; + } + +- if (client->irq <= 0) { +- dev_err(dev, "No IRQ!\n"); +- return -EINVAL; +- } +- + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); +@@ -1001,12 +1026,17 @@ static int ili210x_i2c_probe(struct i2c_ + return error; + } + +- error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, +- IRQF_ONESHOT, client->name, priv); +- if (error) { +- dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", +- error); +- return error; ++ if (client->irq) { ++ error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, ++ IRQF_ONESHOT, client->name, priv); ++ if (error) { ++ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); ++ return error; ++ } ++ } else { ++ timer_setup(&priv->poll_timer, ili210x_poll_timer_callback, 0); ++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD)); ++ INIT_WORK(&priv->poll_work, ili210x_poll_work); + } + + error = devm_add_action_or_reset(dev, ili210x_stop, priv); +@@ -1029,6 +1059,16 @@ static int ili210x_i2c_probe(struct i2c_ + return 0; + } + ++static void ili210x_i2c_remove(struct i2c_client *client) ++{ ++ struct ili210x *tsdata = i2c_get_clientdata(client); ++ ++ if (!client->irq) { ++ del_timer(&tsdata->poll_timer); ++ cancel_work_sync(&tsdata->poll_work); ++ } ++} ++ + static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", (long)&ili210x_chip }, + { "ili2117", (long)&ili211x_chip }, +@@ -1054,6 +1094,7 @@ static struct i2c_driver ili210x_ts_driv + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, ++ .remove = ili210x_i2c_remove, + }; + + module_i2c_driver(ili210x_ts_driver); diff --git a/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch b/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch new file mode 100644 index 000000000..c6861c6a5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1443-drivers-gpu-drm-panel-Added-waveshare-13.3inch-panel.patch @@ -0,0 +1,309 @@ +From 4a89fda8f73df89e009a6188ef07ab97b1d03c7f Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 17:20:22 +0800 +Subject: [PATCH] drivers:gpu:drm:panel: Added waveshare 13.3inch panel(support + 2/4lane) + +Signed-off-by: eng33 +--- + drivers/gpu/drm/panel/panel-waveshare-dsi.c | 155 +++++++++++++++++--- + 1 file changed, 138 insertions(+), 17 deletions(-) + +--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c ++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c +@@ -32,6 +32,12 @@ struct ws_panel { + enum drm_panel_orientation orientation; + }; + ++struct ws_panel_data { ++ const struct drm_display_mode *mode; ++ int lanes; ++ unsigned long mode_flags; ++}; ++ + /* 2.8inch 480x640 + * https://www.waveshare.com/product/raspberry-pi/displays/2.8inch-dsi-lcd.htm + */ +@@ -47,6 +53,12 @@ static const struct drm_display_mode ws_ + .vtotal = 640 + 150 + 50 + 150, + }; + ++static const struct ws_panel_data ws_panel_2_8_data = { ++ .mode = &ws_panel_2_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 3.4inch 800x800 Round + * https://www.waveshare.com/product/displays/lcd-oled/3.4inch-dsi-lcd-c.htm + */ +@@ -62,6 +74,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 8 + 4 + 16, + }; + ++static const struct ws_panel_data ws_panel_3_4_data = { ++ .mode = &ws_panel_3_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 4.0inch 480x800 + * https://www.waveshare.com/product/raspberry-pi/displays/4inch-dsi-lcd.htm + */ +@@ -77,6 +95,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 20 + 100 + 20, + }; + ++static const struct ws_panel_data ws_panel_4_0_data = { ++ .mode = &ws_panel_4_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 7.0inch C 1024x600 + * https://www.waveshare.com/product/raspberry-pi/displays/lcd-oled/7inch-dsi-lcd-c-with-case-a.htm + */ +@@ -92,6 +116,12 @@ static const struct drm_display_mode ws_ + .vtotal = 600 + 10 + 10 + 10, + }; + ++static const struct ws_panel_data ws_panel_7_0_c_data = { ++ .mode = &ws_panel_7_0_c_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 7.9inch 400x1280 + * https://www.waveshare.com/product/raspberry-pi/displays/7.9inch-dsi-lcd.htm + */ +@@ -107,6 +137,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1280 + 20 + 10 + 20, + }; + ++static const struct ws_panel_data ws_panel_7_9_data = { ++ .mode = &ws_panel_7_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 8.0inch or 10.1inch 1280x800 + * https://www.waveshare.com/product/raspberry-pi/displays/8inch-dsi-lcd-c.htm + * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm +@@ -123,6 +159,12 @@ static const struct drm_display_mode ws_ + .vtotal = 800 + 40 + 48 + 40, + }; + ++static const struct ws_panel_data ws_panel_10_1_data = { ++ .mode = &ws_panel_10_1_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 11.9inch 320x1480 + * https://www.waveshare.com/product/raspberry-pi/displays/11.9inch-dsi-lcd.htm + */ +@@ -138,6 +180,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1480 + 60 + 60 + 60, + }; + ++static const struct ws_panel_data ws_panel_11_9_data = { ++ .mode = &ws_panel_11_9_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + static const struct drm_display_mode ws_panel_4_mode = { + .clock = 50000, + .hdisplay = 720, +@@ -150,6 +198,12 @@ static const struct drm_display_mode ws_ + .vtotal = 720 + 8 + 4 + 16, + }; + ++static const struct ws_panel_data ws_panel_4_data = { ++ .mode = &ws_panel_4_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 5.0inch 720x1280 + * https://www.waveshare.com/5inch-dsi-lcd-d.htm + */ +@@ -165,6 +219,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1280 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_5_0_data = { ++ .mode = &ws_panel_5_0_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 6.25inch 720x1560 + * https://www.waveshare.com/6.25inch-dsi-lcd.htm + */ +@@ -180,6 +240,12 @@ static const struct drm_display_mode ws_ + .vtotal = 1560 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_6_25_data = { ++ .mode = &ws_panel_6_25_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ + /* 8.8inch 480x1920 + * https://www.waveshare.com/8.8inch-dsi-lcd.htm + */ +@@ -195,6 +261,48 @@ static const struct drm_display_mode ws_ + .vtotal = 1920 + 20 + 20 + 20, + }; + ++static const struct ws_panel_data ws_panel_8_8_data = { ++ .mode = &ws_panel_8_8_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_4lane_mode = { ++ .clock = 148500, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_4lane_data = { ++ .mode = &ws_panel_13_3_4lane_mode, ++ .lanes = 4, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ ++static const struct drm_display_mode ws_panel_13_3_2lane_mode = { ++ .clock = 83333, ++ .hdisplay = 1920, ++ .hsync_start = 1920 + 88, ++ .hsync_end = 1920 + 88 + 44, ++ .htotal = 1920 + 88 + 44 + 148, ++ .vdisplay = 1080, ++ .vsync_start = 1080 + 4, ++ .vsync_end = 1080 + 4 + 5, ++ .vtotal = 1080 + 4 + 5 + 36, ++}; ++ ++static const struct ws_panel_data ws_panel_13_3_2lane_data = { ++ .mode = &ws_panel_13_3_2lane_mode, ++ .lanes = 2, ++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, ++}; ++ + static struct ws_panel *panel_to_ts(struct drm_panel *panel) + { + return container_of(panel, struct ws_panel, base); +@@ -232,7 +340,10 @@ static int ws_panel_enable(struct drm_pa + { + struct ws_panel *ts = panel_to_ts(panel); + +- ws_panel_i2c_write(ts, 0xad, 0x01); ++ if (ts->mode == &ws_panel_13_3_2lane_mode) ++ ws_panel_i2c_write(ts, 0xad, 0x02); ++ else ++ ws_panel_i2c_write(ts, 0xad, 0x01); + + return 0; + } +@@ -328,13 +439,18 @@ static int ws_panel_probe(struct i2c_cli + .channel = 0, + .node = NULL, + }; ++ const struct ws_panel_data *_ws_panel_data; + int ret; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + +- ts->mode = of_device_get_match_data(dev); ++ _ws_panel_data = of_device_get_match_data(dev); ++ if (!_ws_panel_data) ++ return -EINVAL; ++ ++ ts->mode = _ws_panel_data->mode; + if (!ts->mode) + return -EINVAL; + +@@ -396,10 +512,9 @@ static int ws_panel_probe(struct i2c_cli + */ + drm_panel_add(&ts->base); + +- ts->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | +- MIPI_DSI_CLOCK_NON_CONTINUOUS; ++ ts->dsi->mode_flags = _ws_panel_data->mode_flags; + ts->dsi->format = MIPI_DSI_FMT_RGB888; +- ts->dsi->lanes = 2; ++ ts->dsi->lanes = _ws_panel_data->lanes; + + ret = devm_mipi_dsi_attach(dev, ts->dsi); + +@@ -432,40 +547,46 @@ static void ws_panel_shutdown(struct i2c + static const struct of_device_id ws_panel_of_ids[] = { + { + .compatible = "waveshare,2.8inch-panel", +- .data = &ws_panel_2_8_mode, ++ .data = &ws_panel_2_8_data, + }, { + .compatible = "waveshare,3.4inch-panel", +- .data = &ws_panel_3_4_mode, ++ .data = &ws_panel_3_4_data, + }, { + .compatible = "waveshare,4.0inch-panel", +- .data = &ws_panel_4_0_mode, ++ .data = &ws_panel_4_0_data, + }, { + .compatible = "waveshare,7.0inch-c-panel", +- .data = &ws_panel_7_0_c_mode, ++ .data = &ws_panel_7_0_c_data, + }, { + .compatible = "waveshare,7.9inch-panel", +- .data = &ws_panel_7_9_mode, ++ .data = &ws_panel_7_9_data, + }, { + .compatible = "waveshare,8.0inch-panel", +- .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,10.1inch-panel", +- .data = &ws_panel_10_1_mode, ++ .data = &ws_panel_10_1_data, + }, { + .compatible = "waveshare,11.9inch-panel", +- .data = &ws_panel_11_9_mode, ++ .data = &ws_panel_11_9_data, + }, { + .compatible = "waveshare,4inch-panel", +- .data = &ws_panel_4_mode, ++ .data = &ws_panel_4_data, + }, { + .compatible = "waveshare,5.0inch-panel", +- .data = &ws_panel_5_0_mode, ++ .data = &ws_panel_5_0_data, + }, { + .compatible = "waveshare,6.25inch-panel", +- .data = &ws_panel_6_25_mode, ++ .data = &ws_panel_6_25_data, + }, { + .compatible = "waveshare,8.8inch-panel", +- .data = &ws_panel_8_8_mode, ++ .data = &ws_panel_8_8_data, ++ }, { ++ .compatible = "waveshare,13.3inch-4lane-panel", ++ .data = &ws_panel_13_3_4lane_data, ++ }, { ++ .compatible = "waveshare,13.3inch-2lane-panel", ++ .data = &ws_panel_13_3_2lane_data, + }, { + /* sentinel */ + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch b/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch new file mode 100644 index 000000000..1d144bf2c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1444-arch-arm-boot-dts-overlays-Added-waveshare-13.3inch-.patch @@ -0,0 +1,46 @@ +From e442e5c1ab6bff5b5460b4fc949beb72aaf77970 Mon Sep 17 00:00:00 2001 +From: eng33 +Date: Thu, 5 Dec 2024 18:11:26 +0800 +Subject: [PATCH] arch:arm:boot:dts:overlays: Added waveshare 13.3inch panel + support + +Signed-off-by: eng33 +--- + arch/arm/boot/dts/overlays/README | 2 ++ + .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 7 +++++++ + 2 files changed, 9 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5338,6 +5338,8 @@ Params: 2_8_inch 2.8" 480 + 8_0_inch 8.0" 1280x800 + 10_1_inch 10.1" 1280x800 + 11_9_inch 11.9" 320x1480 ++ 13_3_inch_4lane 13.3" 1920x1080 4lane ++ 13_3_inch_2lane 13.3" 1920x1080 2lane + i2c1 Use i2c-1 with jumper wires from GPIOs 2&3 + disable_touch Disable the touch controller + rotation Set the panel orientation property +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts +@@ -51,6 +51,11 @@ + reg = <0x14>; + compatible = "goodix,gt911"; + }; ++ ++ touch2: ilitek@41 { ++ compatible = "ilitek,ili251x"; ++ reg = <0x41>; ++ }; + }; + }; + +@@ -120,6 +125,8 @@ + <&touch>, "touchscreen-inverted-x?", + <&touch>, "touchscreen-inverted-y?"; + 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel"; ++ 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel"; ++ 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel"; + i2c1 = <&i2c_frag>, "target:0=",<&i2c1>, + <0>, "-3-4+5"; + disable_touch = <&touch>, "status=disabled"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch b/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch new file mode 100644 index 000000000..b3a73945f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1445-fixup-cgroup-Use-kernel-command-line-to-disable-memo.patch @@ -0,0 +1,37 @@ +From 166dfc4399643681f2e4277bf7b7407e926861e5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 9 Dec 2024 14:58:16 +0000 +Subject: [PATCH] fixup! cgroup: Use kernel command line to disable memory + cgroup + +cgroup features are distinct from cgroup subsystems - handle them +correctly. + +Signed-off-by: Phil Elwell +--- + kernel/cgroup/cgroup.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/kernel/cgroup/cgroup.c ++++ b/kernel/cgroup/cgroup.c +@@ -6773,11 +6773,19 @@ static int __init cgroup_enable(char *st + strcmp(token, ss->legacy_name)) + continue; + +- cgroup_feature_disable_mask &= ~(1 << i); + static_branch_enable(cgroup_subsys_enabled_key[i]); + pr_info("Enabling %s control group subsystem\n", + ss->name); + } ++ ++ for (i = 0; i < OPT_FEATURE_COUNT; i++) { ++ if (strcmp(token, cgroup_opt_feature_names[i])) ++ continue; ++ cgroup_feature_disable_mask &= ~(1 << i); ++ pr_info("Enabling %s control group feature\n", ++ cgroup_opt_feature_names[i]); ++ break; ++ } + } + return 1; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch b/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch new file mode 100644 index 000000000..e222ea886 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1451-Revert-drm-vc4-hvs-Don-t-write-gamma-luts-on-2711.patch @@ -0,0 +1,22 @@ +From 448a2db3990534810b45d3e4202df96ab2dc5815 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Tue, 10 Dec 2024 15:28:28 +0000 +Subject: [PATCH] Revert "drm/vc4: hvs: Don't write gamma luts on 2711" + +This reverts commit 40c77e93cfdda320f47fc1a00a76ce466d20e976. +--- + drivers/gpu/drm/vc4/vc4_hvs.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/gpu/drm/vc4/vc4_hvs.c ++++ b/drivers/gpu/drm/vc4/vc4_hvs.c +@@ -521,9 +521,6 @@ static void vc4_hvs_lut_load(struct vc4_ + if (!drm_dev_enter(drm, &idx)) + return; + +- if (hvs->vc4->gen == VC4_GEN_5) +- return; +- + /* The LUT memory is laid out with each HVS channel in order, + * each of which takes 256 writes for R, 256 for G, then 256 + * for B. diff --git a/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch b/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch new file mode 100644 index 000000000..470a90cb9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1452-Revert-PCI-Warn-if-no-host-bridge-NUMA-node-info.patch @@ -0,0 +1,29 @@ +From 746662562995125ef7fb2c294300b0bd061b1251 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Tue, 10 Dec 2024 16:39:31 +0000 +Subject: [PATCH] Revert "PCI: Warn if no host bridge NUMA node info" + +This warning doesn't mean anyting on our platform and +the warning causes confusion. + +See: https://forums.raspberrypi.com/viewtopic.php?p=2276125#p2276125 + +This reverts commit ad5086108b9f0361929aa9a79cf959ab5681d249. + +Signed-off-by: Dom Cobley +--- + drivers/pci/probe.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -967,9 +967,6 @@ static int pci_register_host_bridge(stru + else + pr_info("PCI host bridge to bus %s\n", name); + +- if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE) +- dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n"); +- + /* Coalesce contiguous windows */ + resource_list_for_each_entry_safe(window, n, &resources) { + if (list_is_last(&window->node, &resources)) diff --git a/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch b/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch new file mode 100644 index 000000000..9e4ba2a75 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1454-drm-bridge-panel-Connector-to-allow-interlaced-modes.patch @@ -0,0 +1,25 @@ +From 7d294fbff4863e53a64685335b30aed9604cae49 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Tue, 19 Nov 2024 16:11:32 +0000 +Subject: [PATCH] drm: bridge: panel: Connector to allow interlaced modes + +When initialized from panel_bridge_attach(), connector should +allow interlaced modes rather than invariably rejecting them, +so that other components can validate them. + +Signed-off-by: Nick Hollinghurst +--- + drivers/gpu/drm/bridge/panel.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/gpu/drm/bridge/panel.c ++++ b/drivers/gpu/drm/bridge/panel.c +@@ -82,6 +82,8 @@ static int panel_bridge_attach(struct dr + return ret; + } + ++ connector->interlace_allowed = true; ++ + drm_panel_bridge_set_orientation(connector, bridge); + + drm_connector_attach_encoder(&panel_bridge->connector, diff --git a/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch b/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch new file mode 100644 index 000000000..1d9792305 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1455-dts-overlays-vc4-kms-dpi-generic-overlay-Add-interla.patch @@ -0,0 +1,34 @@ +From 2b0acbe8fd008e09a904b7a3c796a2dc79bf10ea Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Tue, 19 Nov 2024 16:17:40 +0000 +Subject: [PATCH] dts: overlays: vc4-kms-dpi-generic-overlay: Add "interlaced" + property + +Almost no DPI hardware supports it, but it's useful for RP1 video out. + +Signed-off-by: Nick Hollinghurst +--- + arch/arm/boot/dts/overlays/README | 1 + + arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts | 1 + + 2 files changed, 2 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5099,6 +5099,7 @@ Params: clock-frequency Display + vsync-invert Vertical sync active low + de-invert Data Enable active low + pixclk-invert Negative edge pixel clock ++ interlaced Use an interlaced mode (where supported) + width-mm Define the screen width in mm + height-mm Define the screen height in mm + rgb565 Change to RGB565 output on GPIOs 0-19 +--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts +@@ -59,6 +59,7 @@ + vsync-invert = <&timing>, "vsync-active:0=0"; + de-invert = <&timing>, "de-active:0=0"; + pixclk-invert = <&timing>, "pixelclk-active:0=0"; ++ interlaced = <&timing>, "interlaced?"; + + width-mm = <&panel_generic>, "width-mm:0"; + height-mm = <&panel_generic>, "height-mm:0"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch b/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch new file mode 100644 index 000000000..e78ee378a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1456-drm-rp1-rp1-dpi-Add-interlaced-modes-and-PIO-program.patch @@ -0,0 +1,762 @@ +From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Tue, 10 Dec 2024 13:23:09 +0000 +Subject: [PATCH] drm: rp1: rp1-dpi: Add interlaced modes and PIO program to + fix VSYNC + +Implement interlaced modes by wobbling the base pointer and VFP width +for every field. This results in correct pixels but incorrect VSYNC. + +Now use PIO to generate a fixed-up VSYNC by sampling DE and HSYNC. +This requires DPI's DE output to be mapped to GPIO1, which we check. + +When DE is not exposed, the internal fixup is disabled. VSYNC/GPIO2 +becomes a modified signal, designed to help an external device or +PIO program synthesize CSYNC or VSYNC. + +Signed-off-by: Nick Hollinghurst +--- + drivers/gpu/drm/rp1/rp1-dpi/Makefile | 2 +- + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 34 ++- + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 18 ++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 253 ++++++++++++++++------ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c | 225 +++++++++++++++++++ + 5 files changed, 461 insertions(+), 71 deletions(-) + create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c + +--- a/drivers/gpu/drm/rp1/rp1-dpi/Makefile ++++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0-only + +-drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o ++drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o rp1_dpi_pio.o + + obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c +@@ -80,6 +80,7 @@ static void rp1dpi_pipe_update(struct dr + if (dpi->dpi_running && + fb->format->format != dpi->cur_fmt) { + rp1dpi_hw_stop(dpi); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + if (!dpi->dpi_running) { +@@ -88,6 +89,7 @@ static void rp1dpi_pipe_update(struct dr + dpi->bus_fmt, + dpi->de_inv, + &pipe->crtc.state->mode); ++ rp1dpi_pio_start(dpi, &pipe->crtc.state->mode); + dpi->dpi_running = true; + } + dpi->cur_fmt = fb->format->format; +@@ -187,6 +189,7 @@ static void rp1dpi_pipe_disable(struct d + drm_crtc_vblank_off(&pipe->crtc); + if (dpi->dpi_running) { + rp1dpi_hw_stop(dpi); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]); +@@ -236,6 +239,7 @@ static void rp1dpi_stopall(struct drm_de + if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) { + rp1dpi_hw_stop(dpi); + clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]); ++ rp1dpi_pio_stop(dpi); + dpi->dpi_running = false; + } + rp1dpi_vidout_poweroff(dpi); +@@ -273,7 +277,7 @@ static int rp1dpi_platform_probe(struct + struct rp1_dpi *dpi; + struct drm_bridge *bridge = NULL; + struct drm_panel *panel; +- int i, ret; ++ int i, j, ret; + + dev_info(dev, __func__); + ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0, +@@ -295,6 +299,7 @@ static int rp1dpi_platform_probe(struct + return ret; + } + dpi->pdev = pdev; ++ spin_lock_init(&dpi->hw_lock); + + dpi->bus_fmt = default_bus_fmt; + ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt); +@@ -332,6 +337,33 @@ static int rp1dpi_platform_probe(struct + if (ret) + goto done_err; + ++ /* Check if PIO can snoop on or override DPI's GPIO1 */ ++ dpi->gpio1_used = false; ++ for (i = 0; !dpi->gpio1_used; i++) { ++ u32 p = 0; ++ const char *str = NULL; ++ struct device_node *np1 = of_parse_phandle(dev->of_node, "pinctrl-0", i); ++ ++ if (!np1) ++ break; ++ ++ if (!of_property_read_string(np1, "function", &str) && !strcmp(str, "dpi")) { ++ for (j = 0; !dpi->gpio1_used; j++) { ++ if (of_property_read_string_index(np1, "pins", j, &str)) ++ break; ++ if (!strcmp(str, "gpio1")) ++ dpi->gpio1_used = true; ++ } ++ for (j = 0; !dpi->gpio1_used; j++) { ++ if (of_property_read_u32_index(np1, "brcm,pins", j, &p)) ++ break; ++ if (p == 1) ++ dpi->gpio1_used = true; ++ } ++ } ++ of_node_put(np1); ++ } ++ + /* Now we have all our resources, finish driver initialization */ + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + init_completion(&dpi->finished); +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h +@@ -46,6 +46,18 @@ struct rp1_dpi { + bool de_inv, clk_inv; + bool dpi_running, pipe_enabled; + struct completion finished; ++ ++ /* Experimental stuff for interlace follows */ ++ struct rp1_pio_client *pio; ++ bool gpio1_used; ++ bool pio_stole_gpio2; ++ ++ spinlock_t hw_lock; /* the following are used in line-match ISR */ ++ dma_addr_t last_dma_addr; ++ u32 last_stride; ++ u32 shorter_front_porch; ++ bool interlaced; ++ bool lower_field_flag; + }; + + /* ---------------------------------------------------------------------- */ +@@ -67,3 +79,9 @@ void rp1dpi_hw_vblank_ctrl(struct rp1_dp + + void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge); + void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi); ++ ++/* ---------------------------------------------------------------------- */ ++/* PIO control -- we need PIO to generate VSync (from DE) when interlaced */ ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode); ++void rp1dpi_pio_stop(struct rp1_dpi *dpi); +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c +@@ -202,7 +202,7 @@ + // Status + #define DPI_DMA_STATUS 0x3c + +-#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK)) ++#define BITS(field, val) FIELD_PREP((field ## _MASK), val) + + static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg) + { +@@ -231,69 +231,73 @@ struct rp1dpi_ipixfmt { + u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */ + }; + +-#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \ +- BITS(DPI_DMA_IMASK_G, g) | \ +- BITS(DPI_DMA_IMASK_B, b)) +-#define OMASK_RGB(r, g, b) (BITS(DPI_DMA_OMASK_R, r) | \ +- BITS(DPI_DMA_OMASK_G, g) | \ +- BITS(DPI_DMA_OMASK_B, b)) +-#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \ +- BITS(DPI_DMA_SHIFT_IG, g) | \ +- BITS(DPI_DMA_SHIFT_IB, b)) +-#define OSHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_OR, r) | \ +- BITS(DPI_DMA_SHIFT_OG, g) | \ +- BITS(DPI_DMA_SHIFT_OB, b)) ++#define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_IMASK_G_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_IMASK_B_MASK, b)) ++#define OMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_OMASK_R_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_OMASK_G_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_OMASK_B_MASK, b)) ++#define ISHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_IR_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_IG_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_IB_MASK, b)) ++#define OSHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_OR_MASK, r) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \ ++ FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b)) + + static const struct rp1dpi_ipixfmt my_formats[] = { + { + .format = DRM_FORMAT_XRGB8888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + }, + { + .format = DRM_FORMAT_XBGR8888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + }, + { + .format = DRM_FORMAT_ARGB8888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + }, + { + .format = DRM_FORMAT_ABGR8888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + }, + { + .format = DRM_FORMAT_RGB888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), + }, + { + .format = DRM_FORMAT_BGR888, + .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), + .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2), ++ .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), + }, + { + .format = DRM_FORMAT_RGB565, + .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), + .shift = ISHIFT_RGB(15, 10, 4), +- .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) | +- BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1), ++ .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), + }, + { + .format = DRM_FORMAT_BGR565, + .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), + .shift = ISHIFT_RGB(4, 10, 15), +- .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) | +- BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1), ++ .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | ++ FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), + } + }; + +@@ -354,42 +358,26 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi + u32 in_format, u32 bus_format, bool de_inv, + struct drm_display_mode const *mode) + { +- u32 shift, imask, omask, rgbsz; ++ u32 shift, imask, omask, rgbsz, vctrl; + int i; + +- pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC", +- __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format, +- mode->hdisplay, mode->vdisplay, +- mode->htotal, mode->vtotal, +- mode->clock, +- (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+', +- (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+', +- de_inv ? '-' : '+', +- dpi->clk_inv ? '-' : '+'); ++ drm_info(&dpi->drm, ++ "in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d%s %dkHz %cH%cV%cD%cC", ++ in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format, ++ mode->hdisplay, mode->vdisplay, ++ mode->htotal, mode->vtotal, ++ (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "i" : "", ++ mode->clock, ++ (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+', ++ (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+', ++ de_inv ? '-' : '+', ++ dpi->clk_inv ? '-' : '+'); + + /* + * Configure all DPI/DMA block registers, except base address. + * DMA will not actually start until a FB base address is specified + * using rp1dpi_hw_update(). + */ +- rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, +- BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) | +- BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); +- +- rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, +- BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) | +- BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1)); +- +- /* In these registers, "back porch" time includes sync width */ +- rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, +- BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) | +- BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1)); +- +- rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, +- BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) | +- BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1)); +- +- /* Input to output pixel format conversion */ + for (i = 0; i < ARRAY_SIZE(my_formats); ++i) { + if (my_formats[i].format == in_format) + break; +@@ -417,6 +405,89 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi + BITS(DPI_DMA_QOS_LLEV, 0x8) | + BITS(DPI_DMA_QOS_LQOS, 0x7)); + ++ if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) { ++ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, ++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) | ++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, ++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, ++ mode->vsync_end - mode->vsync_start - 1) | ++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, ++ mode->hsync_end - mode->hsync_start - 1)); ++ ++ /* In these registers, "back porch" time includes sync width */ ++ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, ++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, ++ mode->vtotal - mode->vsync_start - 1) | ++ BITS(DPI_DMA_BACK_PORCH_COLSM1, ++ mode->htotal - mode->hsync_start - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, ++ mode->vsync_start - mode->vdisplay - 1) | ++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, ++ mode->hsync_start - mode->hdisplay - 1)); ++ ++ vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) | ++ BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_start)) | ++ BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) | ++ BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start)); ++ ++ dpi->interlaced = false; ++ } else { ++ /* ++ * Experimental interlace support ++ * ++ * RP1 DPI hardware wasn't designed to support interlace, but lets us change ++ * both the VFP line count and the next DMA address while running. That allows ++ * pixel data to be correctly timed for interlace, but VSYNC remains wrong. ++ * ++ * It is necessary to use external hardware (such as PIO) to regenerate VSYNC ++ * based on HSYNC, DE (which *must* both be mapped to GPIOs 1, 3 respectively). ++ * This driver includes a PIO program to do that, when DE is enabled. ++ * ++ * An alternative fixup is to synthesize CSYNC from HSYNC and modified-VSYNC. ++ * We don't implement that here, but to facilitate it, DPI's VSYNC is replaced ++ * by a "helper signal" that pulses low for 1 or 2 scan-lines, starting 2.0 or ++ * 2.5 scan-lines respectively before nominal VSYNC start. ++ */ ++ int vact = mode->vdisplay >> 1; /* visible lines per field. Can't do half-lines */ ++ int vtot0 = mode->vtotal >> 1; /* vtotal should always be odd when interlaced. */ ++ int vfp0 = (mode->vsync_start >= mode->vdisplay + 4) ? ++ ((mode->vsync_start - mode->vdisplay - 2) >> 1) : 1; ++ int vbp = max(0, vtot0 - vact - vfp0); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA, ++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, vact - 1) | ++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH, ++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, vtot0 - 2) | ++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, ++ mode->hsync_end - mode->hsync_start - 1)); ++ ++ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH, ++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, vbp - 1) | ++ BITS(DPI_DMA_BACK_PORCH_COLSM1, ++ mode->htotal - mode->hsync_start - 1)); ++ ++ dpi->shorter_front_porch = ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, vfp0 - 1) | ++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, ++ mode->hsync_start - mode->hdisplay - 1); ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, dpi->shorter_front_porch); ++ ++ vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, 0) | ++ BITS(DPI_DMA_CONTROL_VBP_EN, (vbp > 0)) | ++ BITS(DPI_DMA_CONTROL_VFP_EN, 1) | ++ BITS(DPI_DMA_CONTROL_VSYNC_EN, 1); ++ ++ dpi->interlaced = true; ++ } ++ dpi->lower_field_flag = false; ++ dpi->last_dma_addr = 0; ++ + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1); + rp1dpi_hw_vblank_ctrl(dpi, 1); + +@@ -425,49 +496,64 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi + pr_warn("%s: Unexpectedly busy at start!", __func__); + + rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ++ vctrl | + BITS(DPI_DMA_CONTROL_ARM, !i) | + BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) | + BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) | + BITS(DPI_DMA_CONTROL_DEN_POL, de_inv) | + BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) | +- BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) | +- BITS(DPI_DMA_CONTROL_COLORM, 0) | +- BITS(DPI_DMA_CONTROL_SHUTDN, 0) | + BITS(DPI_DMA_CONTROL_HBP_EN, (mode->htotal != mode->hsync_end)) | + BITS(DPI_DMA_CONTROL_HFP_EN, (mode->hsync_start != mode->hdisplay)) | +- BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_end)) | +- BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) | +- BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)) | +- BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start))); ++ BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start))); + } + + void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride) + { +- u64 a = addr + offset; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dpi->hw_lock, flags); + + /* + * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(), + * DMA starts immediately; if already running, the buffer will flip at +- * the next vertical sync event. ++ * the next vertical sync event. In interlaced mode, we need to adjust ++ * the address and stride to display only the current field, saving ++ * the original address (so it can be flipped for subsequent fields). + */ ++ addr += offset; ++ dpi->last_dma_addr = addr; ++ dpi->last_stride = stride; ++ if (dpi->interlaced) { ++ if (dpi->lower_field_flag) ++ addr += stride; ++ stride *= 2; ++ } + rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride); +- rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32); +- rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, addr >> 32); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, addr & 0xFFFFFFFFu); ++ ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); + } + + void rp1dpi_hw_stop(struct rp1_dpi *dpi) + { + u32 ctrl; ++ unsigned long flags; + + /* +- * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for +- * the current and any queued frame to end. "Force drain" flags are not used, +- * as they seem to prevent DMA from re-starting properly; it's safer to wait. ++ * Stop DMA by turning off Auto-Repeat (and disable S/W field-flip), ++ * then wait up to 100ms for the current and any queued frame to end. ++ * (There is a "force drain" flag, but it can leave DPI in a broken ++ * state which prevents it from restarting; it's safer to wait.) + */ ++ spin_lock_irqsave(&dpi->hw_lock, flags); ++ dpi->last_dma_addr = 0; + reinit_completion(&dpi->finished); + ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL); + ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK); + rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl); ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); ++ + if (!wait_for_completion_timeout(&dpi->finished, HZ / 10)) + drm_err(&dpi->drm, "%s: timed out waiting for idle\n", __func__); + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0); +@@ -476,10 +562,11 @@ void rp1dpi_hw_stop(struct rp1_dpi *dpi) + void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable) + { + rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, +- BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) | +- BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) | +- BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) | +- BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095)); ++ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) | ++ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) | ++ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) | ++ BITS(DPI_DMA_IRQ_EN_MATCH, dpi->interlaced) | ++ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 32)); + } + + irqreturn_t rp1dpi_hw_isr(int irq, void *dev) +@@ -498,7 +585,35 @@ irqreturn_t rp1dpi_hw_isr(int irq, void + drm_crtc_handle_vblank(&dpi->pipe.crtc); + if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK) + complete(&dpi->finished); ++ ++ /* ++ * Added for interlace support: We use this mid-frame interrupt to ++ * wobble the VFP between fields, re-submitting the next-buffer address ++ * with an offset to display the opposite field. NB: rp1dpi_hw_update() ++ * may be called at any time, before or after, so locking is needed. ++ * H/W Auto-update is no longer needed (unless this IRQ is lost). ++ */ ++ if ((u & DPI_DMA_IRQ_FLAGS_MATCH_MASK) && dpi->interlaced) { ++ unsigned long flags; ++ dma_addr_t a; ++ ++ spin_lock_irqsave(&dpi->hw_lock, flags); ++ dpi->lower_field_flag = !dpi->lower_field_flag; ++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, ++ dpi->shorter_front_porch + ++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, ++ dpi->lower_field_flag)); ++ a = dpi->last_dma_addr; ++ if (a) { ++ if (dpi->lower_field_flag) ++ a += dpi->last_stride; ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32); ++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu); ++ } ++ spin_unlock_irqrestore(&dpi->hw_lock, flags); ++ } + } + } ++ + return u ? IRQ_HANDLED : IRQ_NONE; + } +--- /dev/null ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c +@@ -0,0 +1,225 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * PIO code for Raspberry Pi RP1 DPI driver ++ * ++ * Copyright (c) 2024 Raspberry Pi Limited. ++ */ ++ ++/* ++ * Use PIO to fix up VSYNC for interlaced modes. ++ * ++ * For this to work we *require* DPI's pinctrl to enable DE on GPIO1. ++ * PIO can then snoop on HSYNC and DE pins to generate corrected VSYNC. ++ * ++ * Note that corrected VSYNC outputs will not be synchronous to DPICLK, ++ * will lag HSYNC by about 30ns and may suffer up to 5ns of jitter. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rp1_dpi.h" ++ ++/* ++ * Start a PIO SM to generate an interrupt just after HSYNC onset, then another ++ * after a fixed delay (during which we assume HSYNC will have been deasserted). ++ */ ++ ++static int rp1dpi_pio_start_timer_both(struct rp1_dpi *dpi, u32 flags, u32 tc) ++{ ++ static const u16 instructions[2][5] = { ++ { 0xa022, 0x2083, 0xc001, 0x0043, 0xc001 }, /* posedge */ ++ { 0xa022, 0x2003, 0xc001, 0x0043, 0xc001 }, /* negedge */ ++ }; ++ const struct pio_program prog = { ++ .instructions = instructions[(flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0], ++ .length = ARRAY_SIZE(instructions[0]), ++ .origin = -1 ++ }; ++ int offset, sm; ++ ++ sm = pio_claim_unused_sm(dpi->pio, true); ++ if (sm < 0) ++ return -EBUSY; ++ ++ offset = pio_add_program(dpi->pio, &prog); ++ if (offset == PIO_ORIGIN_ANY) ++ return -EBUSY; ++ ++ pio_sm_config cfg = pio_get_default_sm_config(); ++ ++ pio_sm_set_enabled(dpi->pio, sm, false); ++ sm_config_set_wrap(&cfg, offset, offset + 4); ++ pio_sm_init(dpi->pio, sm, offset, &cfg); ++ ++ pio_sm_put(dpi->pio, sm, tc - 4); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_set_enabled(dpi->pio, sm, true); ++ ++ return 0; ++} ++ ++/* ++ * Snoop on DE, HSYNC to count half-lines in the vertical blanking interval ++ * to determine when the VSYNC pulse should start and finish. Then, at a ++ * suitable moment (which should be an odd number of half-lines since the ++ * last active line), sample DE again to detect field phase. ++ * ++ * This version assumes VFP length is within 2..129 half-lines for any field ++ * (one half-line delay is needed to sample DE; we always wait for the next ++ * half-line boundary to improve VSync start accuracy). ++ */ ++ ++static int rp1dpi_pio_vsync_ilace(struct rp1_dpi *dpi, ++ struct drm_display_mode const *mode) ++{ ++ static const int wrap_target = 14; ++ static const int wrap = 26; ++ u16 instructions[] = { /* This is mutable */ ++ 0xa0e6, // 0: mov osr, isr side 0 ; top: rewind parameters ++ 0x2081, // 1: wait 1 gpio, 1 side 0 ; main: while (!DE) wait; ++ 0x2783, // 2: wait 1 gpio, 3 side 0 [7] ; do { @HSync ++ 0xc041, // 3: irq clear 1 side 0 ; flush stale IRQs ++ 0x20c1, // 4: wait 1 irq, 1 side 0 ; @midline ++ 0x00c1, // 5: jmp pin, 1 side 0 ; } while (DE) ++ 0x0007, // 6: jmp 7 side 0 ; ++ 0x6027, // 7: out x, 7 side 0 ; x = VFPlen - 2 ++ 0x000a, // 8: jmp 10 side 0 ; while (x--) { ++ 0x20c1, // 9: wait 1 irq, 1 side 0 ; @halfline ++ 0x0049, // 10: jmp x--, 9 side 0 ; } ++ 0x6021, // 11: out x, 1 side 0 ; test for aligned case ++ 0x003a, // 12: jmp !x, 26 side 0 ; if (!x) goto precise; ++ 0x20c1, // 13: wait 1 irq, 1 side 0 ; @halfline ++ // .wrap_target ; vsjoin: ++ 0xb722, // 14: mov x, y side 1 [7] ; VSYNC=1; x = VSyncLen ++ 0xd041, // 15: irq clear 1 side 1 ; VSYNC=1; flush stale IRQs ++ 0x30c1, // 16: wait 1 irq, 1 side 1 ; VSYNC=1; do { @halfline ++ 0x1050, // 17: jmp x--, 16 side 1 ; VSYNC=1; } while (x--) ++ 0x6028, // 18: out x, 8 side 0 ; VSYNC=0; x = VBPLen ++ 0x0015, // 19: jmp 21 side 0 ; while (x--) { ++ 0x20c1, // 20: wait 1 irq, 1 side 0 ; @halfline ++ 0x0054, // 21: jmp x--, 20 side 0 ; } ++ 0x00c0, // 22: jmp pin, 0 side 0 ; if (DE) reset phase ++ 0x0018, // 23: jmp 24 side 0 ; ++ 0x00e1, // 24: jmp !osre, 1 side 0 ; if (!phase) goto main ++ 0x0000, // 25: jmp 0 side 0 ; goto top ++ 0x2083, // 26: wait 1 gpio, 3 side 0 ; precise: @HSync ++ // .wrap ; goto vsjoin ++ }; ++ struct pio_program prog = { ++ .instructions = instructions, ++ .length = ARRAY_SIZE(instructions), ++ .origin = -1 ++ }; ++ pio_sm_config cfg = pio_get_default_sm_config(); ++ unsigned int i, offset; ++ u32 tc, vfp, vbp; ++ u32 sysclk = clock_get_hz(clk_sys); ++ int sm = pio_claim_unused_sm(dpi->pio, true); ++ ++ if (sm < 0) ++ return -EBUSY; ++ ++ /* Compute mid-line time constant and start the timer SM */ ++ tc = (mode->htotal * (u64)sysclk) / (u64)(2000u * mode->clock); ++ if (rp1dpi_pio_start_timer_both(dpi, mode->flags, tc) < 0) { ++ pio_sm_unclaim(dpi->pio, sm); ++ return -EBUSY; ++ } ++ ++ /* Adapt program code according to DE and Sync polarity; configure program */ ++ pio_sm_set_enabled(dpi->pio, sm, false); ++ if (dpi->de_inv) { ++ instructions[1] ^= 0x0080; ++ instructions[5] = 0x00c7; ++ instructions[6] = 0x0001; ++ instructions[22] = 0x00d8; ++ instructions[23] = 0x0000; ++ } ++ for (i = 0; i < ARRAY_SIZE(instructions); i++) { ++ if (mode->flags & DRM_MODE_FLAG_NVSYNC) ++ instructions[i] ^= 0x1000; ++ if ((mode->flags & DRM_MODE_FLAG_NHSYNC) && (instructions[i] & 0xe07f) == 0x2003) ++ instructions[i] ^= 0x0080; ++ } ++ offset = pio_add_program(dpi->pio, &prog); ++ if (offset == PIO_ORIGIN_ANY) ++ return -EBUSY; ++ ++ /* Configure pins and SM */ ++ dpi->pio_stole_gpio2 = true; ++ sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap); ++ sm_config_set_sideset(&cfg, 1, false, false); ++ sm_config_set_sideset_pins(&cfg, 2); ++ pio_gpio_init(dpi->pio, 2); ++ sm_config_set_jmp_pin(&cfg, 1); /* "DE" is always GPIO1 */ ++ pio_sm_init(dpi->pio, sm, offset, &cfg); ++ pio_sm_set_consecutive_pindirs(dpi->pio, sm, 2, 1, true); ++ ++ /* Compute vertical times, remembering how we rounded vdisplay, vtotal */ ++ vfp = mode->vsync_start - (mode->vdisplay & ~1); ++ vbp = (mode->vtotal | 1) - mode->vsync_end; ++ if (vfp > 128) { ++ vbp += vfp - 128; ++ vfp = 128; ++ } else if (vfp < 3) { ++ vbp = (vbp > 3 - vfp) ? (vbp - 3 + vfp) : 0; ++ vfp = 3; ++ } ++ ++ pio_sm_put(dpi->pio, sm, ++ (vfp - 2) + ((vfp & 1) << 7) + (vbp << 8) + ++ ((vfp - 3) << 16) + (((~vfp) & 1) << 23) + ((vbp + 1) << 24)); ++ pio_sm_put(dpi->pio, sm, mode->vsync_end - mode->vsync_start - 1); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_in(pio_y, 32)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false)); ++ pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32)); ++ pio_sm_set_enabled(dpi->pio, sm, true); ++ ++ return 0; ++} ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode) ++{ ++ int r; ++ ++ if (!(mode->flags & DRM_MODE_FLAG_INTERLACE) || !dpi->gpio1_used) ++ return 0; ++ ++ if (dpi->pio) ++ pio_close(dpi->pio); ++ ++ dpi->pio = pio_open(); ++ if (IS_ERR(dpi->pio)) { ++ drm_err(&dpi->drm, "Could not open PIO\n"); ++ dpi->pio = NULL; ++ return -ENODEV; ++ } ++ ++ r = rp1dpi_pio_vsync_ilace(dpi, mode); ++ if (r) { ++ drm_err(&dpi->drm, "Failed to initialize PIO\n"); ++ rp1dpi_pio_stop(dpi); ++ } ++ ++ return r; ++} ++ ++void rp1dpi_pio_stop(struct rp1_dpi *dpi) ++{ ++ if (dpi->pio) { ++ if (dpi->pio_stole_gpio2) ++ pio_gpio_set_function(dpi->pio, 2, GPIO_FUNC_FSEL1); ++ pio_close(dpi->pio); ++ dpi->pio_stole_gpio2 = false; ++ dpi->pio = NULL; ++ } ++} diff --git a/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch b/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch new file mode 100644 index 000000000..8e279df68 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1459-ASoC-allo-piano-dac-plus-Fix-volume-limit-locking.patch @@ -0,0 +1,79 @@ +From ac0cd73932aa1e371ffaf0b974855ed3cd22937f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 11 Dec 2024 13:47:30 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Fix volume limit locking + +Calling snd_soc_limit_volume from within a kcontrol put handler seems +to cause a deadlock as it attempts to claim a write lock that is already +held. Call snd_soc_limit_volume from the main initialisation code +instead, to avoid the recursive locking. + +See: https://github.com/raspberrypi/linux/issues/6527 + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 32 +++++++++++++---------------- + 1 file changed, 14 insertions(+), 18 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -452,14 +452,6 @@ static int pcm512x_set_reg_sub(struct sn + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + +- if (digital_gain_0db_limit) { +- ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); +- } +- + // When in Dual Mono, Sub vol control should not set anything. + if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode + +@@ -562,14 +554,6 @@ static int pcm512x_set_reg_master(struct + + rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); + +- if (digital_gain_0db_limit) { +- ret = snd_soc_limit_volume(card, "Master Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); +- } +- + if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode + + ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component, +@@ -750,6 +734,18 @@ static int snd_allo_piano_dac_init(struc + if (digital_gain_0db_limit) { + int ret; + ++ ret = snd_soc_limit_volume(card, "Master Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set master volume limit: %d\n", ++ ret); ++ ++ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n", ++ ret); ++ + //Set volume limit on both dacs + for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { + char cname[256]; +@@ -757,8 +753,8 @@ static int snd_allo_piano_dac_init(struc + sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]); + ret = snd_soc_limit_volume(card, cname, 207); + if (ret < 0) +- dev_warn(card->dev, "Failed to set volume limit: %d\n", +- ret); ++ dev_warn(card->dev, "Failed to set %s volume limit: %d\n", ++ cname, ret); + } + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch b/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch new file mode 100644 index 000000000..93de0f99a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1460-drm-vc4-txp-Do-not-allow-24bpp-formats-when-transpos.patch @@ -0,0 +1,30 @@ +From af4ab4fb77dfc697c8ae068b18f27de1ee5d609f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 11 Dec 2024 16:30:43 +0000 +Subject: [PATCH] drm: vc4: txp: Do not allow 24bpp formats when transposing + +The hardware doesn't support transposing to 24bpp (RGB888/BGR888) +formats. There's no way to advertise this through DRM, so block +it from atomic_check instead. + +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/vc4/vc4_txp.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/gpu/drm/vc4/vc4_txp.c ++++ b/drivers/gpu/drm/vc4/vc4_txp.c +@@ -272,6 +272,13 @@ static int vc4_txp_connector_atomic_chec + return -EINVAL; + } + ++ if (conn_state->rotation & DRM_MODE_TRANSPOSE && ++ (fb->format->format == DRM_FORMAT_RGB888 || ++ fb->format->format == DRM_FORMAT_BGR888)) { ++ DRM_DEBUG_KMS("24bpp formats not supported when transposing\n"); ++ return -EINVAL; ++ } ++ + for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { + if (fb->format->format == drm_fmts[i]) + break; diff --git a/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch b/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch new file mode 100644 index 000000000..2a6115981 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1461-drm-Validate-connector-rotation-has-one-bit-set-in-t.patch @@ -0,0 +1,29 @@ +From 0b216b3988e5b7035cd5ed8a9910eacbb3420ce0 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 12 Dec 2024 11:59:52 +0000 +Subject: [PATCH] drm: Validate connector rotation has one bit set in the + rotation property + +Copy the same validation logic as from the plane rotation property. + +Fixes: 8fec3ff87049 ("drm: Add a rotation parameter to connectors.") +Signed-off-by: Dave Stevenson +--- + drivers/gpu/drm/drm_atomic_uapi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/gpu/drm/drm_atomic_uapi.c ++++ b/drivers/gpu/drm/drm_atomic_uapi.c +@@ -812,6 +812,12 @@ static int drm_atomic_connector_set_prop + } else if (property == connector->privacy_screen_sw_state_property) { + state->privacy_screen_sw_state = val; + } else if (property == connector->rotation_property) { ++ if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) { ++ drm_dbg_atomic(connector->dev, ++ "[CONNECTOR:%d:%s] bad rotation bitmask: 0x%llx\n", ++ connector->base.id, connector->name, val); ++ return -EINVAL; ++ } + state->rotation = val; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, diff --git a/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch b/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch new file mode 100644 index 000000000..67bce0281 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1462-ASoC-allo-piano-dac-plus-Suppress-517-errors.patch @@ -0,0 +1,73 @@ +From 61494a7aa2ea887fa1cd1399a8db1317c87f661b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 12 Dec 2024 13:05:41 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Suppress -517 errors + +Use dev_err_probe to simplify the code and suppress EPROBE_DEFER errors. + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 37 ++++++++--------------------- + 1 file changed, 10 insertions(+), 27 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -974,48 +974,31 @@ static int snd_allo_piano_dac_probe(stru + + allo_piano_2_1_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); +- if (!allo_piano_2_1_codecs[0].of_node) { +- dev_err(&pdev->dev, +- "Property 'audio-codec' missing or invalid\n"); +- return -EINVAL; +- } +- + allo_piano_2_1_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "audio-codec", 1); +- if (!allo_piano_2_1_codecs[1].of_node) { +- dev_err(&pdev->dev, ++ if (!allo_piano_2_1_codecs[0].of_node || !allo_piano_2_1_codecs[1].of_node) ++ return dev_err_probe(&pdev->dev, -EINVAL, + "Property 'audio-codec' missing or invalid\n"); +- return -EINVAL; +- } + + mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1", + GPIOD_OUT_LOW); +- if (IS_ERR(mute_gpio[0])) { +- ret = PTR_ERR(mute_gpio[0]); +- dev_err(&pdev->dev, +- "failed to get mute1 gpio6: %d\n", ret); +- return ret; +- } ++ if (IS_ERR(mute_gpio[0])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[0]), ++ "failed to get mute1 gpio\n"); + + mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2", + GPIOD_OUT_LOW); +- if (IS_ERR(mute_gpio[1])) { +- ret = PTR_ERR(mute_gpio[1]); +- dev_err(&pdev->dev, +- "failed to get mute2 gpio25: %d\n", ret); +- return ret; +- } ++ if (IS_ERR(mute_gpio[1])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[1]), ++ "failed to get mute2 gpio\n"); + + if (mute_gpio[0] && mute_gpio[1]) + snd_allo_piano_dac.set_bias_level = + snd_allo_piano_set_bias_level; + + ret = snd_soc_register_card(&snd_allo_piano_dac); +- if (ret < 0) { +- dev_err(&pdev->dev, +- "snd_soc_register_card() failed: %d\n", ret); +- return ret; +- } ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + + if ((mute_gpio[0]) && (mute_gpio[1])) + snd_allo_piano_gpio_mute(&snd_allo_piano_dac); diff --git a/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch b/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch new file mode 100644 index 000000000..13c735eb4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1463-drm-rp1-rp1-dpi-Fix-optional-dependency-on-RP1_PIO.patch @@ -0,0 +1,98 @@ +From 80533a952218696c0ef1b346bab50dc401e6b74c Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Thu, 12 Dec 2024 11:58:12 +0000 +Subject: [PATCH] drm: rp1: rp1-dpi: Fix optional dependency on RP1_PIO + +Add optional dependency to Kconfig, and conditionally compile +PIO-dependent code. Add a mode validation function to reject +interlaced modes when RP1_PIO is not present. + +Signed-off-by: Nick Hollinghurst +--- + drivers/gpu/drm/rp1/rp1-dpi/Kconfig | 7 ++++++- + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 16 ++++++++++++++++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c | 18 +++++++++++++++++- + 3 files changed, 39 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/rp1/rp1-dpi/Kconfig ++++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig +@@ -7,5 +7,10 @@ config DRM_RP1_DPI + select DRM_VRAM_HELPER + select DRM_TTM + select DRM_TTM_HELPER ++ depends on RP1_PIO || !RP1_PIO + help +- Choose this option to enable Video Out on RP1 ++ Choose this option to enable DPI output on Raspberry Pi RP1 ++ ++ There is an optional dependency on RP1_PIO, as the PIO block ++ must be used to fix up interlaced sync. Interlaced DPI modes ++ will be unavailable when RP1_PIO is not selected. +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c +@@ -217,12 +217,28 @@ static void rp1dpi_pipe_disable_vblank(s + rp1dpi_hw_vblank_ctrl(dpi, 0); + } + ++static enum drm_mode_status rp1dpi_pipe_mode_valid(struct drm_simple_display_pipe *pipe, ++ const struct drm_display_mode *mode) ++{ ++#if !IS_REACHABLE(CONFIG_RP1_PIO) ++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) ++ return MODE_NO_INTERLACE; ++#endif ++ if (mode->clock < 1000) /* 1 MHz */ ++ return MODE_CLOCK_LOW; ++ if (mode->clock > 200000) /* 200 MHz */ ++ return MODE_CLOCK_HIGH; ++ ++ return MODE_OK; ++} ++ + static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = { + .enable = rp1dpi_pipe_enable, + .update = rp1dpi_pipe_update, + .disable = rp1dpi_pipe_disable, + .enable_vblank = rp1dpi_pipe_enable_vblank, + .disable_vblank = rp1dpi_pipe_disable_vblank, ++ .mode_valid = rp1dpi_pipe_mode_valid, + }; + + static const struct drm_mode_config_funcs rp1dpi_mode_funcs = { +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c +@@ -18,13 +18,16 @@ + #include + #include + #include +-#include + #include + #include + #include + + #include "rp1_dpi.h" + ++#if IS_REACHABLE(CONFIG_RP1_PIO) ++ ++#include ++ + /* + * Start a PIO SM to generate an interrupt just after HSYNC onset, then another + * after a fixed delay (during which we assume HSYNC will have been deasserted). +@@ -223,3 +226,16 @@ void rp1dpi_pio_stop(struct rp1_dpi *dpi + dpi->pio = NULL; + } + } ++ ++#else /* !IS_REACHABLE(CONFIG_RP1_PIO) */ ++ ++int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode) ++{ ++ return -ENODEV; ++} ++ ++void rp1dpi_pio_stop(struct rp1_dpi *dpi) ++{ ++} ++ ++#endif diff --git a/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch b/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch new file mode 100644 index 000000000..2f52823af --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1464-serial-sc16is7xx-announce-support-for-SER_RS485_RTS_.patch @@ -0,0 +1,42 @@ +From 694247173f2e136196d7cb3a392c84cda65674d2 Mon Sep 17 00:00:00 2001 +From: Hugo Villeneuve +Date: Mon, 7 Oct 2024 12:27:15 -0400 +Subject: [PATCH] serial: sc16is7xx: announce support for SER_RS485_RTS_ON_SEND + +commit 068d35a7be65fa3bca4bba21c269bfe0b39158a6 upstream. + +When specifying flag SER_RS485_RTS_ON_SEND in RS485 configuration, +we get the following warning after commit 4afeced55baa ("serial: core: +fix sanitizing check for RTS settings"): + + invalid RTS setting, using RTS_AFTER_SEND instead + +This results in SER_RS485_RTS_AFTER_SEND being set and the +driver always write to the register field SC16IS7XX_EFCR_RTS_INVERT_BIT, +which breaks some hardware using these chips. + +The hardware supports both RTS_ON_SEND and RTS_AFTER_SEND modes, so fix +this by announcing support for RTS_ON_SEND. + +Signed-off-by: Hugo Villeneuve +Suggested-by: Konstantin Pugin +Link: https://lore.kernel.org/lkml/20240422133219.2710061-2-ria.freelander@gmail.com +Reviewed-by: Andy Shevchenko +Tested-by: Hugo Villeneuve +Link: https://lore.kernel.org/r/20241007162716.3122912-1-hugo@hugovil.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/sc16is7xx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/tty/serial/sc16is7xx.c ++++ b/drivers/tty/serial/sc16is7xx.c +@@ -1480,7 +1480,7 @@ static int sc16is7xx_setup_mctrl_ports(s + } + + static const struct serial_rs485 sc16is7xx_rs485_supported = { +- .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, ++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch b/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch new file mode 100644 index 000000000..d5f72d1c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1467-dtoverlays-Add-override-for-target-path-on-I2C-overl.patch @@ -0,0 +1,550 @@ +From b75fd2a9385e1358fa82218184e73513f9a5e57f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 16 Dec 2024 15:11:08 +0000 +Subject: [PATCH] dtoverlays: Add override for target-path on I2C overlays + +To allow for attaching any of the standard overlays to a +bitbashed i2c-gpio bus, allow specifying the target path for +the overlay. + +Suggested by: +https://forums.raspberrypi.com/viewtopic.php?t=381059 + +Example: +dtoverlay=i2c-gpio,i2c_gpio_sda=10,i2c_gpio_scl=11 +dtoverlay=mcp23017,i2c-path=/i2c@0 +dtoverlay=i2c-gpio,i2c_gpio_sda=12,i2c_gpio_scl=13,bus=3 +dtoverlay=mcp23017,i2c-path=/i2c@3 + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 59 +++++++++++++++++++ + .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 + + .../boot/dts/overlays/edt-ft5406-overlay.dts | 3 + + arch/arm/boot/dts/overlays/goodix-overlay.dts | 4 +- + .../dts/overlays/hd44780-i2c-lcd-overlay.dts | 4 +- + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 + + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 + + .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 + + .../boot/dts/overlays/ilitek251x-overlay.dts | 4 +- + .../boot/dts/overlays/mcp23017-overlay.dts | 2 + + .../arm/boot/dts/overlays/pca953x-overlay.dts | 30 +++++++++- + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 30 +++++++++- + .../dts/overlays/sc16is750-i2c-overlay.dts | 30 +++++++++- + .../dts/overlays/sc16is752-i2c-overlay.dts | 30 +++++++++- + 16 files changed, 201 insertions(+), 7 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -555,6 +555,7 @@ Params: addr I2C bus + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + Channel parameters can be set for each enabled channel. + A maximum of 4 channels can be enabled (letters a thru d). +@@ -1238,6 +1239,7 @@ Params: sizex Touchscr + addr Sets the address for the touch controller. Note + that the device must be configured to use the + specified address. ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: enc28j60 +@@ -1439,6 +1441,7 @@ Info: Enables I2C connected Goodix gt9 + Load: dtoverlay=goodix,= + Params: interrupt GPIO used for interrupt (default 4) + reset GPIO used for reset (default 17) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: googlevoicehat-soundcard +@@ -1730,6 +1733,7 @@ Params: addr I2C addr + display_height Height of the display in characters (default 2) + + display_width Width of the display in characters (default 16) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: hd44780-lcd +@@ -2095,6 +2099,8 @@ Params: addr Sets the + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + minpwm PWM setting for the fan when the SoC is below + mintemp (range 0-255. default 0) + maxpwm PWM setting for the fan when the SoC is above +@@ -2165,6 +2171,8 @@ Params: pca9542 Select t + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + disconnect_on_idle Force the mux to disconnect all child buses + after every transaction. + +@@ -2186,6 +2194,7 @@ Params: addr I2C addr + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: i2c-rtc +@@ -2255,6 +2264,8 @@ Params: abx80x Select o + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + addr Sets the address for the RTC. Note that the + device must be configured to use the specified + address. +@@ -2519,6 +2530,8 @@ Params: addr Set the + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) + ++ i2c-path Override I2C path to allow for i2c-gpio buses ++ + + Name: i2c0 + Info: Change i2c0 pin usage. Not all pin combinations are usable on all +@@ -2661,6 +2674,7 @@ Params: interrupt GPIO use + touchscreen (in pixels) + sizey Touchscreen size y, vertical resolution of + touchscreen (in pixels) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: imx219 +@@ -3138,6 +3152,7 @@ Params: gpiopin Gpio pin + overlay - BCM2711 only) + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: mcp23s17 +@@ -3587,6 +3602,17 @@ Params: addr I2C addr + cat9554 Select the Onnn CAT9554 (8 bit) + pca9654 Select the Onnn PCA9654 (8 bit) + xra1202 Select the Exar XRA1202 (8 bit) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcf857x +@@ -3598,6 +3624,17 @@ Params: addr I2C addr + pcf8574a Select the NXP PCF8574A (8 bit) + pcf8575 Select the NXP PCF8575 (16 bit) + pca8574 Select the NXP PCA8574 (8 bit) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcie-32bit-dma +@@ -4257,6 +4294,17 @@ Load: dtoverlay=sc16is750-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C4 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c5 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is750-spi0 +@@ -4275,6 +4323,17 @@ Load: dtoverlay=sc16is752-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) ++ i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c3 Choose the I2C3 bus (configure with the i2c3 ++ overlay - BCM2711 only) ++ i2c4 Choose the I2C4 bus (configure with the i2c4 ++ overlay - BCM2711 only) ++ i2c5 Choose the I2C5 bus (configure with the i2c5 ++ overlay - BCM2711 only) ++ i2c6 Choose the I2C6 bus (configure with the i2c6 ++ overlay - BCM2711 only) ++ i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is752-spi0 +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -131,5 +131,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts ++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts +@@ -41,6 +41,9 @@ + i2c6 = <&ts_i2c_frag>, "target?=0", + <&ts_i2c_frag>, "target-path=i2c6", + <0>,"-0-1"; ++ i2c-path = <&ts_i2c_frag>, "target?=0", ++ <&ts_i2c_frag>, "target-path", ++ <0>,"-0-1"; + addr = <&ft5406>,"reg:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts ++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts +@@ -16,7 +16,7 @@ + }; + }; + +- fragment@1 { ++ i2c_frag: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -42,5 +42,7 @@ + <>9271>,"irq-gpios:4"; + reset = <&goodix_pins>,"brcm,pins:4", + <>9271>,"reset-gpios:4"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ i2c_frag: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + status = "okay"; +@@ -52,6 +52,8 @@ + display_height = <&lcd_screen>,"display-height-chars:0"; + display_width = <&lcd_screen>,"display-width-chars:0"; + addr = <&pcf857x>,"reg:0"; ++ i2c-path = <&i2c_frag>, "target?=0", ++ <&i2c_frag>, "target-path"; + }; + + }; +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -93,6 +93,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + addr = <&emc2301>,"reg:0"; + minpwm = <&emc2301>,"emc2305,pwm-min.0"; + maxpwm = <&emc2301>,"emc2305,pwm-max.0"; +--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts +@@ -175,6 +175,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + disconnect_on_idle = + <&pca9542>,"idle-state:0=", , + <&pca9545>,"idle-state:0=", , +--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts +@@ -57,5 +57,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -38,5 +38,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -38,5 +38,7 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts +@@ -16,7 +16,7 @@ + }; + }; + +- fragment@1 { ++ frag1: fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; +@@ -41,5 +41,7 @@ + <&ili251x>,"interrupts:0"; + sizex = <&ili251x>,"touchscreen-size-x:0"; + sizey = <&ili251x>,"touchscreen-size-y:0"; ++ i2c-path = <&frag1>, "target?=0", ++ <&frag1>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -98,6 +98,8 @@ + <&frag100>, "target-path=i2c5"; + i2c6 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c6"; ++ i2c-path = <&frag100>, "target?=0", ++ <&frag100>, "target-path"; + }; + }; + +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -5,7 +5,7 @@ + /{ + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -204,6 +204,20 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + addr = <&pca>,"reg:0"; + pca6416 = <0>, "+1"; +@@ -236,5 +250,19 @@ + cat9554 = <0>, "+28"; + pca9654 = <0>, "+29"; + xra1202 = <0>, "+30"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -6,7 +6,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -22,11 +22,39 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + pcf8574 = <&pcf857x>,"compatible=nxp,pcf8574", <&pcf857x>,"reg:0=0x20"; + pcf8574a = <&pcf857x>,"compatible=nxp,pcf8574a", <&pcf857x>,"reg:0=0x38"; + pcf8575 = <&pcf857x>,"compatible=nxp,pcf8575", <&pcf857x>,"reg:0=0x20"; + pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; + addr = <&pcf857x>,"reg:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -48,10 +48,38 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + int_pin = <&sc16is750>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is750>,"reg:0", <&sc16is750_clk>,"name"; + xtal = <&sc16is750_clk>,"clock-frequency:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -4,7 +4,7 @@ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { ++ frag0: fragment@0 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; +@@ -48,10 +48,38 @@ + }; + }; + ++ fragment@100 { ++ target = <&i2c0if>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0mux>; ++ __dormant__ { ++ status = "okay"; ++ }; ++ }; ++ + __overrides__ { + int_pin = <&sc16is752>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is752>,"reg:0",<&sc16is752_clk>,"name"; + xtal = <&sc16is752_clk>,"clock-frequency:0"; ++ i2c0 = <&frag0>, "target:0=",<&i2c0>, ++ <0>,"+100+101"; ++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, ++ <0>,"+100+101"; ++ i2c3 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c3"; ++ i2c4 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c4"; ++ i2c5 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c5"; ++ i2c6 = <&frag0>, "target?=0", ++ <&frag0>, "target-path=i2c6"; ++ i2c-path = <&frag0>, "target?=0", ++ <&frag0>, "target-path"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch b/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch new file mode 100644 index 000000000..3183fb580 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1468-misc-rp1-pio-Support-larger-data-transfers.patch @@ -0,0 +1,127 @@ +From 4b0ca96738bb937529655a0062d60775f47b0f5e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 16 Dec 2024 23:01:41 +0000 +Subject: [PATCH] misc: rp1-pio: Support larger data transfers + +Add a separate IOCTL for larger transfer with a 32-bit data_bytes +field. + +See: https://github.com/raspberrypi/utils/issues/107 + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 43 +++++++++++++++++++++++++++++++--- + include/uapi/misc/rp1_pio_if.h | 8 +++++++ + 2 files changed, 48 insertions(+), 3 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -824,9 +824,9 @@ static int rp1_pio_sm_rx_user(struct rp1 + return ret; + } + +-static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) ++static int rp1_pio_sm_xfer_data32(struct rp1_pio_client *client, void *param) + { +- struct rp1_pio_sm_xfer_data_args *args = param; ++ struct rp1_pio_sm_xfer_data32_args *args = param; + struct rp1_pio_device *pio = client->pio; + struct dma_info *dma; + +@@ -842,6 +842,19 @@ static int rp1_pio_sm_xfer_data(struct r + return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); + } + ++static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_xfer_data_args *args = param; ++ struct rp1_pio_sm_xfer_data32_args args32; ++ ++ args32.sm = args->sm; ++ args32.dir = args->dir; ++ args32.data_bytes = args->data_bytes; ++ args32.data = args->data; ++ ++ return rp1_pio_sm_xfer_data32(client, &args32); ++} ++ + struct handler_info { + const char *name; + int (*func)(struct rp1_pio_client *client, void *param); +@@ -849,6 +862,7 @@ struct handler_info { + } ioctl_handlers[] = { + HANDLER(SM_CONFIG_XFER, sm_config_xfer), + HANDLER(SM_XFER_DATA, sm_xfer_data), ++ HANDLER(SM_XFER_DATA32, sm_xfer_data32), + + HANDLER(CAN_ADD_PROGRAM, can_add_program), + HANDLER(ADD_PROGRAM, add_program), +@@ -1032,13 +1046,23 @@ struct rp1_pio_sm_xfer_data_args_compat + compat_uptr_t data; + }; + ++struct rp1_pio_sm_xfer_data32_args_compat { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t data_bytes; ++ compat_uptr_t data; ++}; ++ + struct rp1_access_hw_args_compat { + uint32_t addr; + uint32_t len; + compat_uptr_t data; + }; + +-#define PIO_IOC_SM_XFER_DATA_COMPAT _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args_compat) ++#define PIO_IOC_SM_XFER_DATA_COMPAT \ ++ _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args_compat) ++#define PIO_IOC_SM_XFER_DATA32_COMPAT \ ++ _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args_compat) + #define PIO_IOC_READ_HW_COMPAT _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args_compat) + #define PIO_IOC_WRITE_HW_COMPAT _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args_compat) + +@@ -1061,6 +1085,19 @@ static long rp1_pio_compat_ioctl(struct + param.data = compat_ptr(compat_param.data); + return rp1_pio_sm_xfer_data(client, ¶m); + } ++ case PIO_IOC_SM_XFER_DATA32_COMPAT: ++ { ++ struct rp1_pio_sm_xfer_data32_args_compat compat_param; ++ struct rp1_pio_sm_xfer_data32_args param; ++ ++ if (copy_from_user(&compat_param, compat_ptr(ioctl_param), sizeof(compat_param))) ++ return -EFAULT; ++ param.sm = compat_param.sm; ++ param.dir = compat_param.dir; ++ param.data_bytes = compat_param.data_bytes; ++ param.data = compat_ptr(compat_param.data); ++ return rp1_pio_sm_xfer_data32(client, ¶m); ++ } + + case PIO_IOC_READ_HW_COMPAT: + case PIO_IOC_WRITE_HW_COMPAT: +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -167,6 +167,13 @@ struct rp1_pio_sm_xfer_data_args { + void *data; + }; + ++struct rp1_pio_sm_xfer_data32_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t data_bytes; ++ void *data; ++}; ++ + struct rp1_access_hw_args { + uint32_t addr; + uint32_t len; +@@ -177,6 +184,7 @@ struct rp1_access_hw_args { + + #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) + #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) ++#define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args) + + #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) + #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) diff --git a/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch b/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch new file mode 100644 index 000000000..c59e72eb1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1469-dtoverlays-Use-continuous-clock-mode-for-ov9281.patch @@ -0,0 +1,34 @@ +From a4a4d7f9183bae11d81616346038e9efaba2fce1 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 16 Dec 2024 19:15:52 +0000 +Subject: [PATCH] dtoverlays: Use continuous clock mode for ov9281 + +This increases the maximum frame rate from 247 to 260fps in +10-bit mode. + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 1 - + arch/arm/boot/dts/overlays/ov9281.dtsi | 1 - + 2 files changed, 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts +@@ -29,7 +29,6 @@ + csi_ep: endpoint { + remote-endpoint = <&cam_endpoint>; + data-lanes = <1 2>; +- clock-noncontinuous; + }; + }; + }; +--- a/arch/arm/boot/dts/overlays/ov9281.dtsi ++++ b/arch/arm/boot/dts/overlays/ov9281.dtsi +@@ -19,7 +19,6 @@ cam_node: ov9281@60 { + cam_endpoint: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; +- clock-noncontinuous; + link-frequencies = + /bits/ 64 <400000000>; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch b/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch new file mode 100644 index 000000000..508896b3b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1470-overlays-goodix-Allow-override-i2c-address.patch @@ -0,0 +1,36 @@ +From 62085522016ee2dadbe8668a6a97919770020817 Mon Sep 17 00:00:00 2001 +From: Renjaya Raga Zenta +Date: Wed, 18 Dec 2024 16:44:32 +0700 +Subject: [PATCH] overlays: goodix: Allow override i2c address + +Some Goodix devices e.g. gt911 use address 0x5d instead of 0x14. +So, make the address overridable. + +Signed-off-by: Renjaya Raga Zenta +--- + arch/arm/boot/dts/overlays/README | 3 ++- + arch/arm/boot/dts/overlays/goodix-overlay.dts | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1439,7 +1439,8 @@ Name: goodix + Info: Enables I2C connected Goodix gt9271 multiple touch controller using + GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset. + Load: dtoverlay=goodix,= +-Params: interrupt GPIO used for interrupt (default 4) ++Params: addr I2C address (default 0x14) ++ interrupt GPIO used for interrupt (default 4) + reset GPIO used for reset (default 17) + i2c-path Override I2C path to allow for i2c-gpio buses + +--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts ++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts +@@ -37,6 +37,7 @@ + }; + + __overrides__ { ++ addr = <>9271>,"reg:0"; + interrupt = <&goodix_pins>,"brcm,pins:0", + <>9271>,"interrupts:0", + <>9271>,"irq-gpios:4"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch new file mode 100644 index 000000000..5adc2a69c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1471-fixup-misc-Add-RP1-PIO-driver.patch @@ -0,0 +1,24 @@ +From cd26850713088942ca4f9a248a8bed1f0504a58f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Dec 2024 15:11:40 +0000 +Subject: [PATCH] fixup! misc: Add RP1 PIO driver + +Change the Kconfig dependencies so that RP1_PIO depends on FIRMWARE_RP1, +rather than selecting it. + +Signed-off-by: Phil Elwell +--- + drivers/misc/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -19,7 +19,7 @@ config BCM2835_SMI + + config RP1_PIO + tristate "Raspberry Pi RP1 PIO driver" +- select FIRMWARE_RP1 ++ depends on FIRMWARE_RP1 || COMPILE_TEST + default n + help + Driver providing control of the Raspberry Pi PIO block, as found in diff --git a/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch b/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch new file mode 100644 index 000000000..b67b02901 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1473-misc-rp1-pio-More-logical-probe-sequence.patch @@ -0,0 +1,81 @@ +From 468b525d45a726e4ba704b33c4eba53de47ac684 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 5 Dec 2024 16:03:39 +0000 +Subject: [PATCH] misc: rp1-pio: More logical probe sequence + +Sort the probe function initialisation into a more logical order. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 31 +++++++++++++++---------------- + 1 file changed, 15 insertions(+), 16 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1153,6 +1153,10 @@ static int rp1_pio_probe(struct platform + return -EINVAL; + } + ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); ++ if (pdev->id < 0) ++ return dev_err_probe(dev, pdev->id, "alias is missing\n"); ++ + fw = devm_rp1_firmware_get(dev, dev->of_node); + if (IS_ERR(fw)) + return PTR_ERR(fw); +@@ -1185,31 +1189,26 @@ static int rp1_pio_probe(struct platform + goto out_err; + } + +- cdev_init(&pio->cdev, &rp1_pio_fops); +- ret = cdev_add(&pio->cdev, pio->dev_num, 1); +- if (ret) { +- dev_err(dev, "cdev_add failed (err %d)\n", ret); +- goto out_unregister; +- } +- + pio->dev_class = class_create(DRIVER_NAME); + if (IS_ERR(pio->dev_class)) { + ret = PTR_ERR(pio->dev_class); + dev_err(dev, "class_create failed (err %d)\n", ret); +- goto out_cdev_del; ++ goto out_unregister; + } +- pdev->id = of_alias_get_id(pdev->dev.of_node, "pio"); +- if (pdev->id < 0) { +- dev_err(dev, "alias is missing\n"); +- return -EINVAL; ++ ++ cdev_init(&pio->cdev, &rp1_pio_fops); ++ ret = cdev_add(&pio->cdev, pio->dev_num, 1); ++ if (ret) { ++ dev_err(dev, "cdev_add failed (err %d)\n", ret); + goto out_class_destroy; + } ++ + sprintf(dev_name, "pio%d", pdev->id); + cdev = device_create(pio->dev_class, NULL, pio->dev_num, NULL, dev_name); + if (IS_ERR(cdev)) { + ret = PTR_ERR(cdev); + dev_err(dev, "%s: device_create failed (err %d)\n", __func__, ret); +- goto out_class_destroy; ++ goto out_cdev_del; + } + + g_pio = pio; +@@ -1217,12 +1216,12 @@ static int rp1_pio_probe(struct platform + dev_info(dev, "Created instance as %s\n", dev_name); + return 0; + +-out_class_destroy: +- class_destroy(pio->dev_class); +- + out_cdev_del: + cdev_del(&pio->cdev); + ++out_class_destroy: ++ class_destroy(pio->dev_class); ++ + out_unregister: + unregister_chrdev_region(pio->dev_num, 1); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch b/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch new file mode 100644 index 000000000..d843f1f91 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1474-misc-rp1-pio-Convert-floats-to-24.8-fixed-point.patch @@ -0,0 +1,92 @@ +From 5c07ba20630a629399eaa6583457aca93ff74606 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 9 Dec 2024 09:58:29 +0000 +Subject: [PATCH] misc: rp1-pio: Convert floats to 24.8 fixed point + +Floating point arithmetic is not supported in the kernel, so use fixed +point instead. + +Signed-off-by: Phil Elwell +--- + include/linux/pio_rp1.h | 29 +++++++++++++++++++++-------- + 1 file changed, 21 insertions(+), 8 deletions(-) + +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -171,6 +171,10 @@ enum gpio_drive_strength { + GPIO_DRIVE_STRENGTH_12MA = 3 + }; + ++struct fp24_8 { ++ uint32_t val; ++}; ++ + typedef rp1_pio_sm_config pio_sm_config; + + typedef struct rp1_pio_client *PIO; +@@ -218,6 +222,13 @@ void pio_close(PIO pio); + int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); + int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data); + ++static inline struct fp24_8 make_fp24_8(uint mul, uint div) ++{ ++ struct fp24_8 res = { .val = ((unsigned long long)mul << 8) / div }; ++ ++ return res; ++} ++ + static inline bool pio_can_add_program(struct rp1_pio_client *client, + const pio_program_t *program) + { +@@ -396,16 +407,18 @@ static inline int pio_sm_clear_fifos(str + return rp1_pio_sm_clear_fifos(client, &args); + } + +-static inline bool pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, ++static inline bool pio_calculate_clkdiv_from_fp24_8(struct fp24_8 div, uint16_t *div_int, + uint8_t *div_frac) + { +- if (bad_params_if(NULL, div < 1 || div > 65536)) ++ uint inum = (div.val >> 8); ++ ++ if (bad_params_if(NULL, inum < 1 || inum > 65536)) + return false; +- *div_int = (uint16_t)div; ++ *div_int = (uint16_t)inum; + if (*div_int == 0) + *div_frac = 0; + else +- *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); ++ *div_frac = div.val & 0xff; + return true; + } + +@@ -421,11 +434,11 @@ static inline int pio_sm_set_clkdiv_int_ + return rp1_pio_sm_set_clkdiv(client, &args); + } + +-static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, float div) ++static inline int pio_sm_set_clkdiv(struct rp1_pio_client *client, uint sm, struct fp24_8 div) + { + struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm }; + +- if (!pio_calculate_clkdiv_from_float(div, &args.div_int, &args.div_frac)) ++ if (!pio_calculate_clkdiv_from_fp24_8(div, &args.div_int, &args.div_frac)) + return -EINVAL; + return rp1_pio_sm_set_clkdiv(client, &args); + } +@@ -745,12 +758,12 @@ static inline void sm_config_set_clkdiv_ + (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); + } + +-static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) ++static inline void sm_config_set_clkdiv(pio_sm_config *c, struct fp24_8 div) + { + uint16_t div_int; + uint8_t div_frac; + +- pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); ++ pio_calculate_clkdiv_from_fp24_8(div, &div_int, &div_frac); + sm_config_set_clkdiv_int_frac(c, div_int, div_frac); + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch b/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch new file mode 100644 index 000000000..439698d3e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1475-misc-rp1-pio-Minor-cosmetic-tweaks.patch @@ -0,0 +1,42 @@ +From 75203c6641cfe47dfb817b095430021b0981ff47 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 10 Dec 2024 12:06:14 +0000 +Subject: [PATCH] misc: rp1-pio: Minor cosmetic tweaks + +No functional change. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -683,7 +683,7 @@ err_dma_free: + } + + static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, +- const void __user *userbuf, size_t bytes) ++ const void __user *userbuf, size_t bytes) + { + struct platform_device *pdev = pio->pdev; + struct dma_async_tx_descriptor *desc; +@@ -757,7 +757,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + } + + static int rp1_pio_sm_rx_user(struct rp1_pio_device *pio, struct dma_info *dma, +- void __user *userbuf, size_t bytes) ++ void __user *userbuf, size_t bytes) + { + struct platform_device *pdev = pio->pdev; + struct dma_async_tx_descriptor *desc; +@@ -809,8 +809,7 @@ static int rp1_pio_sm_rx_user(struct rp1 + desc->callback = rp1_pio_sm_dma_callback; + desc->callback_param = dma; + +- // Submit the buffer - the callback will kick the semaphore +- ++ /* Submit the buffer - the callback will kick the semaphore */ + ret = dmaengine_submit(desc); + if (ret < 0) + break; diff --git a/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch b/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch new file mode 100644 index 000000000..facfb23c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1476-misc-rp1-pio-Add-in-kernel-DMA-support.patch @@ -0,0 +1,508 @@ +From fddd3e9318dbf01fb763b6880021abc558fce8e6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 12 Dec 2024 17:09:27 +0000 +Subject: [PATCH] misc: rp1-pio: Add in-kernel DMA support + +Add kernel-facing implementations of pio_sm_config_xfer and +pio_xm_xfer_data. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 208 ++++++++++++++++++++++++++++++---------- + include/linux/pio_rp1.h | 60 ++++++++++-- + 2 files changed, 210 insertions(+), 58 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -61,9 +61,15 @@ + #define DMA_BOUNCE_BUFFER_SIZE 0x1000 + #define DMA_BOUNCE_BUFFER_COUNT 4 + ++struct dma_xfer_state { ++ struct dma_info *dma; ++ void (*callback)(void *param); ++ void *callback_param; ++}; ++ + struct dma_buf_info { + void *buf; +- dma_addr_t phys; ++ dma_addr_t dma_addr; + struct scatterlist sgl; + }; + +@@ -572,21 +578,34 @@ static void rp1_pio_sm_dma_callback(void + up(&dma->buf_sem); + } + ++static void rp1_pio_sm_kernel_dma_callback(void *param) ++{ ++ struct dma_xfer_state *dxs = param; ++ ++ dxs->dma->tail_idx++; ++ up(&dxs->dma->buf_sem); ++ ++ dxs->callback(dxs->callback_param); ++ ++ kfree(dxs); ++} ++ + static void rp1_pio_sm_dma_free(struct device *dev, struct dma_info *dma) + { + dmaengine_terminate_all(dma->chan); + while (dma->buf_count > 0) { + dma->buf_count--; + dma_free_coherent(dev, ROUND_UP(dma->buf_size, PAGE_SIZE), +- dma->bufs[dma->buf_count].buf, dma->bufs[dma->buf_count].phys); ++ dma->bufs[dma->buf_count].buf, ++ dma->bufs[dma->buf_count].dma_addr); + } + + dma_release_channel(dma->chan); + } + +-static int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, void *param) ++static int rp1_pio_sm_config_xfer_internal(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) + { +- struct rp1_pio_sm_config_xfer_args *args = param; + struct rp1_pio_sm_set_dmactrl_args set_dmactrl_args; + struct rp1_pio_device *pio = client->pio; + struct platform_device *pdev = pio->pdev; +@@ -596,17 +615,18 @@ static int rp1_pio_sm_config_xfer(struct + struct dma_info *dma; + uint32_t dma_mask; + char chan_name[4]; +- uint buf_size; + int ret = 0; + +- if (args->sm >= RP1_PIO_SMS_COUNT || args->dir >= RP1_PIO_DIR_COUNT || +- !args->buf_size || (args->buf_size & 3) || +- !args->buf_count || args->buf_count > DMA_BOUNCE_BUFFER_COUNT) ++ if (sm >= RP1_PIO_SMS_COUNT || dir >= RP1_PIO_DIR_COUNT) ++ return -EINVAL; ++ if ((buf_count || buf_size) && ++ (!buf_size || (buf_size & 3) || ++ !buf_count || buf_count > DMA_BOUNCE_BUFFER_COUNT)) + return -EINVAL; + +- dma_mask = 1 << (args->sm * 2 + args->dir); ++ dma_mask = 1 << (sm * 2 + dir); + +- dma = &pio->dma_configs[args->sm][args->dir]; ++ dma = &pio->dma_configs[sm][dir]; + + spin_lock(&pio->lock); + if (pio->claimed_dmas & dma_mask) +@@ -615,16 +635,16 @@ static int rp1_pio_sm_config_xfer(struct + client->claimed_dmas |= dma_mask; + spin_unlock(&pio->lock); + +- dma->buf_size = args->buf_size; ++ dma->buf_size = buf_size; + /* Round up the allocations */ +- buf_size = ROUND_UP(args->buf_size, PAGE_SIZE); ++ buf_size = ROUND_UP(buf_size, PAGE_SIZE); + sema_init(&dma->buf_sem, 0); + + /* Allocate and configure a DMA channel */ + /* Careful - each SM FIFO has its own DREQ value */ +- chan_name[0] = (args->dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; ++ chan_name[0] = (dir == RP1_PIO_DIR_TO_SM) ? 't' : 'r'; + chan_name[1] = 'x'; +- chan_name[2] = '0' + args->sm; ++ chan_name[2] = '0' + sm; + chan_name[3] = '\0'; + + dma->chan = dma_request_chan(dev, chan_name); +@@ -632,37 +652,37 @@ static int rp1_pio_sm_config_xfer(struct + return PTR_ERR(dma->chan); + + /* Alloc and map bounce buffers */ +- for (dma->buf_count = 0; dma->buf_count < args->buf_count; dma->buf_count++) { ++ for (dma->buf_count = 0; dma->buf_count < buf_count; dma->buf_count++) { + struct dma_buf_info *dbi = &dma->bufs[dma->buf_count]; + + dbi->buf = dma_alloc_coherent(dma->chan->device->dev, buf_size, +- &dbi->phys, GFP_KERNEL); ++ &dbi->dma_addr, GFP_KERNEL); + if (!dbi->buf) { + ret = -ENOMEM; + goto err_dma_free; + } + sg_init_table(&dbi->sgl, 1); +- sg_dma_address(&dbi->sgl) = dbi->phys; ++ sg_dma_address(&dbi->sgl) = dbi->dma_addr; + } + + fifo_addr = pio->phys_addr; +- fifo_addr += args->sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); +- fifo_addr += (args->dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; ++ fifo_addr += sm * (RP1_PIO_FIFO_TX1 - RP1_PIO_FIFO_TX0); ++ fifo_addr += (dir == RP1_PIO_DIR_TO_SM) ? RP1_PIO_FIFO_TX0 : RP1_PIO_FIFO_RX0; + + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_addr = fifo_addr; + config.dst_addr = fifo_addr; +- config.direction = (args->dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; ++ config.direction = (dir == RP1_PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + + ret = dmaengine_slave_config(dma->chan, &config); + if (ret) + goto err_dma_free; + +- set_dmactrl_args.sm = args->sm; +- set_dmactrl_args.is_tx = (args->dir == RP1_PIO_DIR_TO_SM); ++ set_dmactrl_args.sm = sm; ++ set_dmactrl_args.is_tx = (dir == RP1_PIO_DIR_TO_SM); + set_dmactrl_args.ctrl = RP1_PIO_DMACTRL_DEFAULT; +- if (args->dir == RP1_PIO_DIR_FROM_SM) ++ if (dir == RP1_PIO_DIR_FROM_SM) + set_dmactrl_args.ctrl = (RP1_PIO_DMACTRL_DEFAULT & ~0x1f) | 1; + + ret = rp1_pio_sm_set_dmactrl(client, &set_dmactrl_args); +@@ -682,6 +702,14 @@ err_dma_free: + return ret; + } + ++static int rp1_pio_sm_config_xfer_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer_args *args = param; ++ ++ return rp1_pio_sm_config_xfer_internal(client, args->sm, args->dir, ++ args->buf_size, args->buf_count); ++} ++ + static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, + const void __user *userbuf, size_t bytes) + { +@@ -723,7 +751,7 @@ static int rp1_pio_sm_tx_user(struct rp1 + DMA_PREP_INTERRUPT | DMA_CTRL_ACK | + DMA_PREP_FENCE); + if (!desc) { +- dev_err(dev, "DMA preparation failedzn"); ++ dev_err(dev, "DMA preparation failed\n"); + ret = -EIO; + break; + } +@@ -779,7 +807,7 @@ static int rp1_pio_sm_rx_user(struct rp1 + if (!bytes || dma->head_idx - dma->tail_idx == dma->buf_count) { + if (down_timeout(&dma->buf_sem, + msecs_to_jiffies(1000))) { +- dev_err(dev, "DMA wait timed out"); ++ dev_err(dev, "DMA wait timed out\n"); + ret = -ETIMEDOUT; + break; + } +@@ -801,7 +829,7 @@ static int rp1_pio_sm_rx_user(struct rp1 + DMA_PREP_INTERRUPT | DMA_CTRL_ACK | + DMA_PREP_FENCE); + if (!desc) { +- dev_err(dev, "DMA preparation failed"); ++ dev_err(dev, "DMA preparation failed\n"); + ret = -EIO; + break; + } +@@ -823,7 +851,7 @@ static int rp1_pio_sm_rx_user(struct rp1 + return ret; + } + +-static int rp1_pio_sm_xfer_data32(struct rp1_pio_client *client, void *param) ++static int rp1_pio_sm_xfer_data32_user(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_xfer_data32_args *args = param; + struct rp1_pio_device *pio = client->pio; +@@ -841,7 +869,7 @@ static int rp1_pio_sm_xfer_data32(struct + return rp1_pio_sm_rx_user(pio, dma, args->data, args->data_bytes); + } + +-static int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, void *param) ++static int rp1_pio_sm_xfer_data_user(struct rp1_pio_client *client, void *param) + { + struct rp1_pio_sm_xfer_data_args *args = param; + struct rp1_pio_sm_xfer_data32_args args32; +@@ -851,17 +879,97 @@ static int rp1_pio_sm_xfer_data(struct r + args32.data_bytes = args->data_bytes; + args32.data = args->data; + +- return rp1_pio_sm_xfer_data32(client, &args32); ++ return rp1_pio_sm_xfer_data32_user(client, &args32); ++} ++ ++int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) ++{ ++ return rp1_pio_sm_config_xfer_internal(client, sm, dir, buf_size, buf_count); ++} ++EXPORT_SYMBOL_GPL(rp1_pio_sm_config_xfer); ++ ++int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param) ++{ ++ struct rp1_pio_device *pio = client->pio; ++ struct platform_device *pdev = pio->pdev; ++ struct dma_async_tx_descriptor *desc; ++ struct dma_xfer_state *dxs = NULL; ++ struct device *dev = &pdev->dev; ++ struct dma_buf_info *dbi = NULL; ++ struct scatterlist sg; ++ struct dma_info *dma; ++ int ret = 0; ++ ++ if (sm >= RP1_PIO_SMS_COUNT || dir >= RP1_PIO_DIR_COUNT) ++ return -EINVAL; ++ ++ dma = &pio->dma_configs[sm][dir]; ++ ++ if (!dma_addr) { ++ dxs = kmalloc(sizeof(*dxs), GFP_KERNEL); ++ dxs->dma = dma; ++ dxs->callback = callback; ++ dxs->callback_param = param; ++ callback = rp1_pio_sm_kernel_dma_callback; ++ param = dxs; ++ ++ if (!dma->buf_count || data_bytes > dma->buf_size) ++ return -EINVAL; ++ ++ /* Grab a dma buffer */ ++ if (dma->head_idx - dma->tail_idx == dma->buf_count) { ++ if (down_timeout(&dma->buf_sem, msecs_to_jiffies(1000))) { ++ dev_err(dev, "DMA wait timed out\n"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ dbi = &dma->bufs[dma->head_idx % dma->buf_count]; ++ dma_addr = dbi->dma_addr; ++ ++ if (dir == PIO_DIR_TO_SM) ++ memcpy(dbi->buf, data, data_bytes); ++ } ++ ++ sg_init_table(&sg, 1); ++ sg_dma_address(&sg) = dma_addr; ++ sg_dma_len(&sg) = data_bytes; ++ ++ desc = dmaengine_prep_slave_sg(dma->chan, &sg, 1, ++ (dir == PIO_DIR_TO_SM) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK | ++ DMA_PREP_FENCE); ++ if (!desc) { ++ dev_err(dev, "DMA preparation failed\n"); ++ return -EIO; ++ } ++ ++ desc->callback = callback; ++ desc->callback_param = param; ++ ++ ret = dmaengine_submit(desc); ++ if (ret < 0) { ++ dev_err(dev, "dmaengine_submit failed (%d)\n", ret); ++ return ret; ++ } ++ ++ dma_async_issue_pending(dma->chan); ++ ++ return 0; + } ++EXPORT_SYMBOL_GPL(rp1_pio_sm_xfer_data); + + struct handler_info { + const char *name; + int (*func)(struct rp1_pio_client *client, void *param); + int argsize; + } ioctl_handlers[] = { +- HANDLER(SM_CONFIG_XFER, sm_config_xfer), +- HANDLER(SM_XFER_DATA, sm_xfer_data), +- HANDLER(SM_XFER_DATA32, sm_xfer_data32), ++ HANDLER(SM_CONFIG_XFER, sm_config_xfer_user), ++ HANDLER(SM_XFER_DATA, sm_xfer_data_user), ++ HANDLER(SM_XFER_DATA32, sm_xfer_data32_user), + + HANDLER(CAN_ADD_PROGRAM, can_add_program), + HANDLER(ADD_PROGRAM, add_program), +@@ -902,7 +1010,7 @@ struct handler_info { + HANDLER(WRITE_HW, write_hw), + }; + +-struct rp1_pio_client *pio_open(void) ++struct rp1_pio_client *rp1_pio_open(void) + { + struct rp1_pio_client *client; + +@@ -914,9 +1022,9 @@ struct rp1_pio_client *pio_open(void) + + return client; + } +-EXPORT_SYMBOL_GPL(pio_open); ++EXPORT_SYMBOL_GPL(rp1_pio_open); + +-void pio_close(struct rp1_pio_client *client) ++void rp1_pio_close(struct rp1_pio_client *client) + { + struct rp1_pio_device *pio = client->pio; + uint claimed_dmas = client->claimed_dmas; +@@ -958,31 +1066,31 @@ void pio_close(struct rp1_pio_client *cl + + kfree(client); + } +-EXPORT_SYMBOL_GPL(pio_close); ++EXPORT_SYMBOL_GPL(rp1_pio_close); + +-void pio_set_error(struct rp1_pio_client *client, int err) ++void rp1_pio_set_error(struct rp1_pio_client *client, int err) + { + client->error = err; + } +-EXPORT_SYMBOL_GPL(pio_set_error); ++EXPORT_SYMBOL_GPL(rp1_pio_set_error); + +-int pio_get_error(const struct rp1_pio_client *client) ++int rp1_pio_get_error(const struct rp1_pio_client *client) + { + return client->error; + } +-EXPORT_SYMBOL_GPL(pio_get_error); ++EXPORT_SYMBOL_GPL(rp1_pio_get_error); + +-void pio_clear_error(struct rp1_pio_client *client) ++void rp1_pio_clear_error(struct rp1_pio_client *client) + { + client->error = 0; + } +-EXPORT_SYMBOL_GPL(pio_clear_error); ++EXPORT_SYMBOL_GPL(rp1_pio_clear_error); + +-static int rp1_pio_open(struct inode *inode, struct file *filp) ++static int rp1_pio_file_open(struct inode *inode, struct file *filp) + { + struct rp1_pio_client *client; + +- client = pio_open(); ++ client = rp1_pio_open(); + if (IS_ERR(client)) + return PTR_ERR(client); + +@@ -991,11 +1099,11 @@ static int rp1_pio_open(struct inode *in + return 0; + } + +-static int rp1_pio_release(struct inode *inode, struct file *filp) ++static int rp1_pio_file_release(struct inode *inode, struct file *filp) + { + struct rp1_pio_client *client = filp->private_data; + +- pio_close(client); ++ rp1_pio_close(client); + + return 0; + } +@@ -1082,7 +1190,7 @@ static long rp1_pio_compat_ioctl(struct + param.dir = compat_param.dir; + param.data_bytes = compat_param.data_bytes; + param.data = compat_ptr(compat_param.data); +- return rp1_pio_sm_xfer_data(client, ¶m); ++ return rp1_pio_sm_xfer_data_user(client, ¶m); + } + case PIO_IOC_SM_XFER_DATA32_COMPAT: + { +@@ -1095,7 +1203,7 @@ static long rp1_pio_compat_ioctl(struct + param.dir = compat_param.dir; + param.data_bytes = compat_param.data_bytes; + param.data = compat_ptr(compat_param.data); +- return rp1_pio_sm_xfer_data32(client, ¶m); ++ return rp1_pio_sm_xfer_data32_user(client, ¶m); + } + + case PIO_IOC_READ_HW_COMPAT: +@@ -1124,8 +1232,8 @@ static long rp1_pio_compat_ioctl(struct + + const struct file_operations rp1_pio_fops = { + .owner = THIS_MODULE, +- .open = rp1_pio_open, +- .release = rp1_pio_release, ++ .open = rp1_pio_file_open, ++ .release = rp1_pio_file_release, + .unlocked_ioctl = rp1_pio_ioctl, + .compat_ioctl = rp1_pio_compat_ioctl, + }; +--- a/include/linux/pio_rp1.h ++++ b/include/linux/pio_rp1.h +@@ -179,9 +179,17 @@ typedef rp1_pio_sm_config pio_sm_config; + + typedef struct rp1_pio_client *PIO; + +-void pio_set_error(struct rp1_pio_client *client, int err); +-int pio_get_error(const struct rp1_pio_client *client); +-void pio_clear_error(struct rp1_pio_client *client); ++int rp1_pio_init(void); ++PIO rp1_pio_open(void); ++void rp1_pio_close(struct rp1_pio_client *client); ++void rp1_pio_set_error(struct rp1_pio_client *client, int err); ++int rp1_pio_get_error(const struct rp1_pio_client *client); ++void rp1_pio_clear_error(struct rp1_pio_client *client); ++int rp1_pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count); ++int rp1_pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param); + + int rp1_pio_can_add_program(struct rp1_pio_client *client, void *param); + int rp1_pio_add_program(struct rp1_pio_client *client, void *param); +@@ -215,12 +223,48 @@ int rp1_pio_gpio_set_oeover(struct rp1_p + int rp1_pio_gpio_set_input_enabled(struct rp1_pio_client *client, void *param); + int rp1_pio_gpio_set_drive_strength(struct rp1_pio_client *client, void *param); + +-int pio_init(void); +-PIO pio_open(void); +-void pio_close(PIO pio); ++static inline int pio_init(void) ++{ ++ return rp1_pio_init(); ++} ++ ++static inline struct rp1_pio_client *pio_open(void) ++{ ++ return rp1_pio_open(); ++} ++ ++static inline void pio_close(struct rp1_pio_client *client) ++{ ++ rp1_pio_close(client); ++} ++ ++static inline void pio_set_error(struct rp1_pio_client *client, int err) ++{ ++ rp1_pio_set_error(client, err); ++} + +-int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); +-int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data); ++static inline int pio_get_error(const struct rp1_pio_client *client) ++{ ++ return rp1_pio_get_error(client); ++} ++ ++static inline void pio_clear_error(struct rp1_pio_client *client) ++{ ++ rp1_pio_clear_error(client); ++} ++ ++static inline int pio_sm_config_xfer(struct rp1_pio_client *client, uint sm, uint dir, ++ uint buf_size, uint buf_count) ++{ ++ return rp1_pio_sm_config_xfer(client, sm, dir, buf_size, buf_count); ++} ++ ++static inline int pio_sm_xfer_data(struct rp1_pio_client *client, uint sm, uint dir, ++ uint data_bytes, void *data, dma_addr_t dma_addr, ++ void (*callback)(void *param), void *param) ++{ ++ return rp1_pio_sm_xfer_data(client, sm, dir, data_bytes, data, dma_addr, callback, param); ++} + + static inline struct fp24_8 make_fp24_8(uint mul, uint div) + { diff --git a/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch b/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch new file mode 100644 index 000000000..b50bdea19 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1477-misc-Add-ws2812-pio-rp1-driver.patch @@ -0,0 +1,568 @@ +From d6d83ad3d9a3a594909a1ad1c82b735ab711cd12 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 3 Dec 2024 16:09:30 +0000 +Subject: [PATCH] misc: Add ws2812-pio-rp1 driver + +ws2812-pio-rp1 is a PIO-based driver for WS2812 LEDS. It creates a +character device in /dev, the default name of which is /dev/leds, +where is the instance number. The number of LEDS should be set +in the DT overlay, as should whether it is RGB or RGBW, and the default +brightness. + +Write data to the /dev/* entry in a 4 bytes-per-pixel format in RGBW +order: + + RR GG BB WW RR GG BB WW ... + +The white values are ignored unless the rgbw flag is set for the device. + +To change the brightness, write a single byte to offset 0, 255 being +full brightness and 0 being off. + +Signed-off-by: Phil Elwell +--- + drivers/misc/Kconfig | 10 + + drivers/misc/Makefile | 1 + + drivers/misc/ws2812-pio-rp1.c | 507 ++++++++++++++++++++++++++++++++++ + 3 files changed, 518 insertions(+) + create mode 100644 drivers/misc/ws2812-pio-rp1.c + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -25,6 +25,16 @@ config RP1_PIO + Driver providing control of the Raspberry Pi PIO block, as found in + RP1. + ++config WS2812_PIO_RP1 ++ tristate "Raspberry Pi PIO-base WS2812 driver" ++ depends on RP1_PIO || COMPILE_TEST ++ default n ++ help ++ Driver for the WS2812 (NeoPixel) LEDs using the RP1 PIO hardware. ++ The driver creates a character device to which rgbw pixels may be ++ written. Single-byte writes to offset 0 set the brightness at ++ runtime. ++ + config AD525X_DPOT + tristate "Analog Devices Digital Potentiometers" + depends on (I2C || SPI) && SYSFS +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -19,6 +19,7 @@ obj-$(CONFIG_PHANTOM) += phantom.o + obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o + obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o + obj-$(CONFIG_RP1_PIO) += rp1-pio.o ++obj-$(CONFIG_WS2812_PIO_RP1) += ws2812-pio-rp1.o + obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o + obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o + obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o +--- /dev/null ++++ b/drivers/misc/ws2812-pio-rp1.c +@@ -0,0 +1,507 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Raspberry Pi PIO-based WS2812 driver ++ * ++ * Copyright (C) 2014-2024 Raspberry Pi Ltd. ++ * ++ * Author: Phil Elwell (phil@raspberrypi.com) ++ * ++ * Based on the ws2812 driver by Gordon Hollingworth ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "ws2812-pio-rp1" ++#define MAX_INSTANCES 4 ++ ++#define RESET_US 50 ++#define PIXEL_BYTES 4 ++ ++struct ws2812_pio_rp1_state { ++ struct device *dev; ++ struct gpio_desc *gpiod; ++ struct gpio_desc *power_gpiod; ++ uint gpio; ++ PIO pio; ++ uint sm; ++ uint offset; ++ ++ u8 *buffer; ++ u8 *pixbuf; ++ u32 pixbuf_size; ++ u32 write_end; ++ ++ u8 brightness; ++ u32 invert; ++ u32 num_leds; ++ u32 xfer_end_us; ++ bool is_rgbw; ++ struct delayed_work deferred_work; ++ ++ struct completion dma_completion; ++ struct cdev cdev; ++ dev_t dev_num; ++ const char *dev_name; ++}; ++ ++static DEFINE_MUTEX(ws2812_pio_mutex); ++static DEFINE_IDA(ws2812_pio_ida); ++static long ws2812_pio_ref_count; ++static struct class *ws2812_pio_class; ++static dev_t ws2812_pio_dev_num; ++/* ++ * WS2812B gamma correction ++ * GammaE=255*(res/255).^(1/.45) ++ * From: http://rgb-123.com/ws2812-color-output/ ++ */ ++ ++static const u8 ws2812_gamma[] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, ++ 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, ++ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, ++ 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, ++ 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, ++ 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, ++ 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, ++ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, ++ 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89, ++ 90, 91, 93, 94, 95, 96, 98, 99, 100, 102, 103, 104, 106, 107, 109, 110, ++ 111, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126, 128, 129, 131, 132, 134, ++ 135, 137, 138, 140, 142, 143, 145, 146, 148, 150, 151, 153, 155, 157, 158, 160, ++ 162, 163, 165, 167, 169, 170, 172, 174, 176, 178, 179, 181, 183, 185, 187, 189, ++ 191, 193, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, ++ 222, 224, 227, 229, 231, 233, 235, 237, 239, 241, 244, 246, 248, 250, 252, 255 ++}; ++ ++// ------ // ++// ws2812 // ++// ------ // ++ ++#define ws2812_wrap_target 0 ++#define ws2812_wrap 3 ++ ++#define ws2812_T1 3 ++#define ws2812_T2 4 ++#define ws2812_T3 3 ++ ++static const uint16_t ws2812_program_instructions[] = { ++ // .wrap_target ++ 0x6221, // 0: out x, 1 side 0 [2] ++ 0x1223, // 1: jmp !x, 3 side 1 [2] ++ 0x1300, // 2: jmp 0 side 1 [3] ++ 0xa342, // 3: nop side 0 [3] ++ // .wrap ++}; ++ ++static const struct pio_program ws2812_program = { ++ .instructions = ws2812_program_instructions, ++ .length = 4, ++ .origin = -1, ++}; ++ ++static inline pio_sm_config ws2812_program_get_default_config(uint offset) ++{ ++ pio_sm_config c = pio_get_default_sm_config(); ++ ++ sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap); ++ sm_config_set_sideset(&c, 1, false, false); ++ return c; ++} ++ ++static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, uint freq, ++ bool rgbw) ++{ ++ int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; ++ struct fp24_8 div; ++ pio_sm_config c; ++ ++ pio_gpio_init(pio, pin); ++ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); ++ c = ws2812_program_get_default_config(offset); ++ sm_config_set_sideset_pins(&c, pin); ++ sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); ++ sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); ++ div = make_fp24_8(clock_get_hz(clk_sys), freq * cycles_per_bit); ++ sm_config_set_clkdiv(&c, div); ++ pio_sm_init(pio, sm, offset, &c); ++ pio_sm_set_enabled(pio, sm, true); ++} ++ ++static uint8_t ws2812_apply_gamma(uint8_t brightness, uint8_t val) ++{ ++ int bright; ++ ++ if (!val) ++ return 0; ++ bright = (val * brightness) / 255; ++ return ws2812_gamma[bright]; ++} ++ ++static inline uint8_t *rgbw_u32(const struct ws2812_pio_rp1_state *state, ++ uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint8_t *p) ++{ ++ p[0] = ws2812_apply_gamma(state->brightness, w); ++ p[1] = ws2812_apply_gamma(state->brightness, b); ++ p[2] = ws2812_apply_gamma(state->brightness, r); ++ p[3] = ws2812_apply_gamma(state->brightness, g); ++ return p + 4; ++} ++ ++static void ws2812_dma_complete(void *param) ++{ ++ struct ws2812_pio_rp1_state *state = param; ++ ++ complete(&state->dma_completion); ++} ++ ++static void ws2812_update_leds(struct ws2812_pio_rp1_state *state, uint length) ++{ ++ init_completion(&state->dma_completion); ++ if (!pio_sm_xfer_data(state->pio, state->sm, PIO_DIR_TO_SM, length, state->buffer, 0, ++ (void (*)(void *))ws2812_dma_complete, state)) { ++ wait_for_completion(&state->dma_completion); ++ usleep_range(RESET_US, RESET_US + 100); ++ } ++} ++ ++static void ws2812_clear_leds(struct ws2812_pio_rp1_state *state) ++{ ++ uint8_t *p_buffer; ++ uint length; ++ int i; ++ ++ p_buffer = state->buffer; ++ for (i = 0; i < state->num_leds; i++) ++ p_buffer = rgbw_u32(state, 0, 0, 0, 0, p_buffer); ++ ++ length = (void *)p_buffer - (void *)state->buffer; ++ ++ ws2812_update_leds(state, length); ++} ++ ++/* ++ * Function to write the RGB buffer to the WS2812 leds, the input buffer ++ * contains a sequence of up to num_leds RGB32 integers, these are then ++ * gamma-corrected before being sent to the PIO state machine. ++ */ ++ ++static ssize_t ws2812_pio_rp1_write(struct file *filp, const char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct ws2812_pio_rp1_state *state; ++ uint32_t pixbuf_size; ++ unsigned long delay; ++ loff_t pos = *ppos; ++ int err = 0; ++ ++ state = (struct ws2812_pio_rp1_state *)filp->private_data; ++ pixbuf_size = state->pixbuf_size; ++ ++ if (pos > pixbuf_size) ++ return -EFBIG; ++ ++ if (count > pixbuf_size) { ++ err = -EFBIG; ++ count = pixbuf_size; ++ } ++ ++ if (pos + count > pixbuf_size) { ++ if (!err) ++ err = -ENOSPC; ++ ++ count = pixbuf_size - pos; ++ } ++ ++ if (!pos && count == 1) { ++ if (copy_from_user(&state->brightness, buf, 1)) ++ return -EFAULT; ++ } else { ++ if (copy_from_user(state->pixbuf + pos, buf, count)) ++ return -EFAULT; ++ pos += count; ++ state->write_end = (u32)pos; ++ } ++ ++ *ppos = pos; ++ ++ delay = (state->write_end == pixbuf_size) ? 0 : HZ / 20; ++ schedule_delayed_work(&state->deferred_work, delay); ++ ++ return err ? err : count; ++} ++ ++static void ws2812_pio_rp1_deferred_work(struct work_struct *work) ++{ ++ struct ws2812_pio_rp1_state *state = ++ container_of(work, struct ws2812_pio_rp1_state, deferred_work.work); ++ uint8_t *p_buffer; ++ uint32_t *p_rgb; ++ int blank_bytes; ++ uint length; ++ int i; ++ ++ blank_bytes = state->pixbuf_size - state->write_end; ++ if (blank_bytes > 0) ++ memset(state->pixbuf + state->write_end, 0, blank_bytes); ++ ++ p_rgb = (uint32_t *)state->pixbuf; ++ p_buffer = state->buffer; ++ ++ for (i = 0; i < state->num_leds; i++) { ++ uint32_t rgbw_pix = *(p_rgb++); ++ ++ p_buffer = rgbw_u32(state, ++ (uint8_t)(rgbw_pix >> 0), ++ (uint8_t)(rgbw_pix >> 8), ++ (uint8_t)(rgbw_pix >> 16), ++ (uint8_t)(rgbw_pix >> 24), ++ p_buffer); ++ } ++ ++ length = (void *)p_buffer - (void *)state->buffer; ++ ++ ws2812_update_leds(state, length); ++} ++ ++static int ws2812_pio_rp1_open(struct inode *inode, struct file *file) ++{ ++ struct ws2812_pio_rp1_state *state; ++ ++ state = container_of(inode->i_cdev, struct ws2812_pio_rp1_state, cdev); ++ file->private_data = state; ++ ++ return 0; ++} ++ ++const struct file_operations ws2812_pio_rp1_fops = { ++ .owner = THIS_MODULE, ++ .write = ws2812_pio_rp1_write, ++ .open = ws2812_pio_rp1_open, ++}; ++ ++/* ++ * Probe function ++ */ ++static int ws2812_pio_rp1_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct of_phandle_args of_args = { 0 }; ++ struct ws2812_pio_rp1_state *state; ++ struct device *dev = &pdev->dev; ++ struct device *char_dev; ++ const char *dev_name; ++ uint32_t brightness; ++ bool is_rp1; ++ int minor; ++ int ret; ++ ++ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); ++ if (IS_ERR(state)) ++ return PTR_ERR(state); ++ ++ state->dev = dev; ++ ++ platform_set_drvdata(pdev, state); ++ ++ ret = of_property_read_u32(np, "rpi,num-leds", &state->num_leds); ++ if (ret) ++ return dev_err_probe(dev, ret, "Could not get num-leds\n"); ++ ++ brightness = 255; ++ of_property_read_u32(np, "rpi,brightness", &brightness); ++ state->brightness = min(brightness, 255); ++ ++ state->pixbuf_size = state->num_leds * PIXEL_BYTES; ++ ++ state->is_rgbw = of_property_read_bool(np, "rpi,rgbw"); ++ state->gpiod = devm_gpiod_get(dev, "leds", GPIOD_ASIS); ++ if (IS_ERR(state->gpiod)) ++ return dev_err_probe(dev, PTR_ERR(state->gpiod), ++ "Could not get a gpio\n"); ++ ++ /* This must be an RP1 GPIO in the first bank, and retrieve the offset. */ ++ /* Unfortunately I think this has to be done by parsing the gpios property */ ++ ++ /* This really shouldn't fail, given that we have a gpiod */ ++ if (of_parse_phandle_with_args(np, "leds-gpios", "#gpio-cells", 0, &of_args)) ++ return dev_err_probe(dev, -EINVAL, ++ "Can't find gpio declaration\n"); ++ ++ is_rp1 = of_device_is_compatible(of_args.np, "raspberrypi,rp1-gpio"); ++ of_node_put(of_args.np); ++ if (!is_rp1 || of_args.args_count != 2) ++ return dev_err_probe(dev, -EINVAL, ++ "Not an RP1 gpio\n"); ++ ++ state->gpio = of_args.args[0]; ++ ++ state->pixbuf = devm_kmalloc(dev, state->pixbuf_size, GFP_KERNEL); ++ if (state->pixbuf == NULL) ++ return -ENOMEM; ++ ++ state->buffer = devm_kmalloc(dev, state->num_leds * PIXEL_BYTES, GFP_KERNEL); ++ if (state->buffer == NULL) ++ return -ENOMEM; ++ ++ ret = of_property_read_string(np, "dev-name", &dev_name); ++ if (ret) { ++ pr_err("Failed to read 'dev-name' property\n"); ++ return ret; ++ } ++ ++ state->pio = pio_open(); ++ if (IS_ERR(state->pio)) ++ return dev_err_probe(dev, PTR_ERR(state->pio), ++ "Could not open PIO\n"); ++ ++ state->sm = pio_claim_unused_sm(state->pio, false); ++ if ((int)state->sm < 0) { ++ dev_err(dev, "No free PIO SM\n"); ++ ret = -EBUSY; ++ goto fail_pio; ++ } ++ ++ state->offset = pio_add_program(state->pio, &ws2812_program); ++ if (state->offset == PIO_ORIGIN_ANY) { ++ dev_err(dev, "Not enough PIO program space\n"); ++ ret = -EBUSY; ++ goto fail_pio; ++ } ++ ++ pio_sm_config_xfer(state->pio, state->sm, PIO_DIR_TO_SM, state->num_leds * sizeof(int), 1); ++ ++ pio_sm_clear_fifos(state->pio, state->sm); ++ pio_sm_set_clkdiv(state->pio, state->sm, make_fp24_8(1, 1)); ++ ws2812_program_init(state->pio, state->sm, state->offset, state->gpio, 800000, ++ state->is_rgbw); ++ ++ mutex_lock(&ws2812_pio_mutex); ++ ++ if (!ws2812_pio_ref_count) { ++ ret = alloc_chrdev_region(&ws2812_pio_dev_num, 0, MAX_INSTANCES, DRIVER_NAME); ++ if (ret < 0) { ++ dev_err(dev, "alloc_chrdev_region failed (rc=%d)\n", ret); ++ goto fail_mutex; ++ } ++ ++ ws2812_pio_class = class_create(DRIVER_NAME); ++ if (IS_ERR(ws2812_pio_class)) { ++ pr_err("Unable to create class " DRIVER_NAME "\n"); ++ ret = PTR_ERR(ws2812_pio_class); ++ goto fail_chrdev; ++ } ++ } ++ ++ ws2812_pio_ref_count++; ++ ++ minor = ida_alloc_range(&ws2812_pio_ida, 0, MAX_INSTANCES - 1, GFP_KERNEL); ++ if (minor < 0) { ++ pr_err("No free instances\n"); ++ ret = minor; ++ goto fail_class; ++ ++ } ++ ++ mutex_unlock(&ws2812_pio_mutex); ++ ++ state->dev_num = MKDEV(MAJOR(ws2812_pio_dev_num), minor); ++ state->dev_name = devm_kasprintf(dev, GFP_KERNEL, dev_name, minor); ++ ++ char_dev = device_create(ws2812_pio_class, NULL, state->dev_num, NULL, state->dev_name); ++ ++ if (IS_ERR(char_dev)) { ++ pr_err("Unable to create device %s\n", state->dev_name); ++ ret = PTR_ERR(char_dev); ++ goto fail_ida; ++ } ++ ++ state->cdev.owner = THIS_MODULE; ++ cdev_init(&state->cdev, &ws2812_pio_rp1_fops); ++ ++ ret = cdev_add(&state->cdev, state->dev_num, 1); ++ if (ret) { ++ pr_err("cdev_add failed\n"); ++ goto fail_device; ++ } ++ ++ INIT_DELAYED_WORK(&state->deferred_work, ws2812_pio_rp1_deferred_work); ++ ++ ws2812_clear_leds(state); ++ ++ dev_info(&pdev->dev, "Instantiated %d LEDs on GPIO %d as /dev/%s\n", ++ state->num_leds, state->gpio, state->dev_name); ++ ++ return 0; ++ ++fail_device: ++ device_destroy(ws2812_pio_class, state->dev_num); ++fail_ida: ++ mutex_lock(&ws2812_pio_mutex); ++ ida_free(&ws2812_pio_ida, minor); ++fail_class: ++ ws2812_pio_ref_count--; ++ if (ws2812_pio_ref_count) ++ goto fail_mutex; ++ class_destroy(ws2812_pio_class); ++fail_chrdev: ++ unregister_chrdev_region(ws2812_pio_dev_num, MAX_INSTANCES); ++fail_mutex: ++ mutex_unlock(&ws2812_pio_mutex); ++fail_pio: ++ pio_close(state->pio); ++ ++ return ret; ++} ++ ++static void ws2812_pio_rp1_remove(struct platform_device *pdev) ++{ ++ struct ws2812_pio_rp1_state *state = platform_get_drvdata(pdev); ++ ++ cancel_delayed_work(&state->deferred_work); ++ platform_set_drvdata(pdev, NULL); ++ ++ cdev_del(&state->cdev); ++ device_destroy(ws2812_pio_class, state->dev_num); ++ ++ mutex_lock(&ws2812_pio_mutex); ++ ida_free(&ws2812_pio_ida, MINOR(state->dev_num)); ++ ws2812_pio_ref_count--; ++ if (!ws2812_pio_ref_count) { ++ class_destroy(ws2812_pio_class); ++ unregister_chrdev_region(ws2812_pio_dev_num, MAX_INSTANCES); ++ } ++ mutex_unlock(&ws2812_pio_mutex); ++ ++ pio_close(state->pio); ++} ++ ++static const struct of_device_id ws2812_pio_rp1_match[] = { ++ { .compatible = "raspberrypi,ws2812-pio-rp1" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ws2812_pio_rp1_match); ++ ++static struct platform_driver ws2812_pio_rp1_driver = { ++ .driver = { ++ .name = "ws2812-pio-rp1", ++ .of_match_table = ws2812_pio_rp1_match, ++ }, ++ .probe = ws2812_pio_rp1_probe, ++ .remove_new = ws2812_pio_rp1_remove, ++}; ++module_platform_driver(ws2812_pio_rp1_driver); ++ ++MODULE_DESCRIPTION("WS2812 PIO RP1 driver"); ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch new file mode 100644 index 000000000..ab98fa139 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1478-overlays-Add-ws2812-pio-overlay.patch @@ -0,0 +1,107 @@ +From 4a8f2b39157825fefc505fe4b94f3a9ce101e170 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 12 Dec 2024 23:23:39 +0000 +Subject: [PATCH] overlays: Add ws2812-pio overlay + +Add an overlay to enable a WS2812 LED driver on a given GPIO. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/Makefile | 3 +- + arch/arm/boot/dts/overlays/README | 22 +++++++++ + .../boot/dts/overlays/ws2812-pio-overlay.dts | 46 +++++++++++++++++++ + 3 files changed, 70 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -342,7 +342,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + waveshare-can-fd-hat-mode-a.dtbo \ + waveshare-can-fd-hat-mode-b.dtbo \ + wittypi.dtbo \ +- wm8960-soundcard.dtbo ++ wm8960-soundcard.dtbo \ ++ ws2812-pio.dtbo + + targets += dtbs dtbs_install + targets += $(dtbo-y) +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -5599,6 +5599,28 @@ Params: alsaname Changes + compatible Changes the codec compatibility + + ++Name: ws2812-pio ++Info: Configures a GPIO pin to drive a string of WS2812 LEDS using pio. It ++ can be enabled on any RP1 GPIO in bank 0 (0-27). Up to 4 are supported, ++ assuming nothing else is using PIO. Pi 5 only. ++Load: dtoverlay=ws2812-pio,= ++Params: brightness Set the initial brightness for the LEDs. The ++ brightness can be changed at runtime by writing ++ a single byte to offset 0 of the device. Note ++ that brightness is a multiplier for the pixel ++ values, and only white pixels can reach the ++ maximum visible brightness. (range 0-255, ++ default 255) ++ dev_name The name for the /dev/ device entry. Note that ++ if the name includes '%d' it will be replaced ++ by the instance number. (default 'leds%d') ++ gpio Output GPIO (0-27, default 4) ++ num_leds Number of LEDs (default 60) ++ rgbw 'rgbw=on' (or 'rgbw') indicates that each pixel ++ includes a white LED as well as the usual red, ++ green and blue. (default 'off') ++ ++ + Troubleshooting + =============== + +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/ws2812-pio-overlay.dts +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Device tree overlay for RP1 PIO WS2812 driver. ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2712"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ ws2812_pio_pins: ws2812_pio_pins@4 { ++ brcm,pins = <4>; /* gpio 4 */ ++ function = "pio"; ++ bias-disable; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ ws2812_pio: ws2812_pio@4 { ++ compatible = "raspberrypi,ws2812-pio-rp1"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ws2812_pio_pins>; ++ dev-name = "leds%d"; ++ leds-gpios = <&gpio 4 0>; ++ rpi,num-leds = <60>; ++ rpi,brightness = <255>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ brightness = <&ws2812_pio>, "rpi,brightness:0"; ++ dev_name = <&ws2812_pio>, "dev-name"; ++ gpio = <&ws2812_pio>,"leds-gpios:4", ++ <&ws2812_pio_pins>,"brcm,pins:0", ++ /* modify reg values to allow multiple instantiation */ ++ <&ws2812_pio>,"reg:0", ++ <&ws2812_pio_pins>,"reg:0"; ++ num_leds = <&ws2812_pio>, "rpi,num-leds:0"; ++ rgbw = <&ws2812_pio>, "rpi,rgbw?"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch b/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch new file mode 100644 index 000000000..ab0f05113 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1480-overlays-Add-and-document-i2c_csi_dsi0-parameters.patch @@ -0,0 +1,342 @@ +From 489570796a5789f849683fc3fb034c55cb13e4c6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 19 Dec 2024 17:13:17 +0000 +Subject: [PATCH] overlays: Add and document i2c_csi_dsi0 parameters + +Add "i2c_csi_dsi0" parameters to overlays that already have an +"i2c_csi_dsi" parameter. + +The I2C bus and GPIO mapping of i2c_csi_dsi and i2c_csi_dsi0 varies +between platforms. Document the associations against the dtparams +"i2c_csi_dsi" and "i2c_csi_dsi0" - run "dtparam -h i2c_csi_dsi" +and "dtparam -h i2c_csi_dsi0" to read it. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 103 ++++++++++++++++-- + .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 + + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 + + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 + + .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 + + .../boot/dts/overlays/mcp23017-overlay.dts | 2 + + .../arm/boot/dts/overlays/pca953x-overlay.dts | 2 + + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 2 + + .../dts/overlays/sc16is750-i2c-overlay.dts | 2 + + .../dts/overlays/sc16is752-i2c-overlay.dts | 2 + + 12 files changed, 113 insertions(+), 12 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -301,10 +301,31 @@ Params: + i2c_baudrate An alias for i2c_arm_baudrate + + i2c_csi_dsi Set to "on" to enable the i2c_csi_dsi interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1: ++ i2c-1 on 2 & 3 ++ B rev 2, B+, CM, Zero, Zero W, 2B, CM2, CM3, ++ CM4S: ++ i2c-0 on 28 & 29 ++ 3B, 3B+, Zero 2W, 4B, 400, CM4: ++ i2c-0 on 44 & 45 ++ 5, 500: ++ i2c-11/i2c-4 on 40 & 41 ++ CM5 on CM5IO: ++ i2c-0 on 0 & 1 ++ CM5 on CM4IO: ++ i2c-10/i2c-6 on 38 & 39 + + i2c_csi_dsi0 Set to "on" to enable the i2c_csi_dsi0 interface ++ The I2C bus and GPIOs are platform specific: ++ B rev 1 & 2, B+, CM, Zero, Zero W, 2B, CM2, ++ CM3, CM4S, 3B, 3B+, Zero 2W, 4B, 400, CM4, ++ CM5 on CM4IO: ++ i2c-0 on 0 & 1 ++ 5, 500, CM5 on CM5IO: ++ i2c-10/i2c-6 on 38 & 39 + +- i2c_csi_dsi1 Set to "on" to enable the i2c_csi_dsi1 interface ++ i2c_csi_dsi1 A Pi 5 family-specific alias for i2c_csi_dsi. + + i2c_vc Set to "on" to enable the i2c interface + usually reserved for the VideoCore processor +@@ -546,7 +567,12 @@ Params: addr I2C bus + Amplifier for this channel. (Default 1 sets the + full scale of the channel to 4.096 Volts) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -2086,7 +2112,13 @@ Params: addr Sets the + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2158,7 +2190,13 @@ Params: pca9542 Select t + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2186,7 +2224,12 @@ Info: Adds support for an NXP PCA9685A + Load: dtoverlay=i2c-pwm-pca9685a,= + Params: addr I2C address of PCA9685A (default 0x40) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -2251,7 +2294,13 @@ Params: abx80x Select o + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -2517,7 +2566,12 @@ Params: addr Set the + + i2c0 Choose the I2C0 bus on GPIOs 0&1 + +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +@@ -3144,7 +3198,12 @@ Params: gpiopin Gpio pin + mcp23008 Configure an MCP23008 instead. + noints Disable the interrupt GPIO line. + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -3604,7 +3663,12 @@ Params: addr I2C addr + pca9654 Select the Onnn PCA9654 (8 bit) + xra1202 Select the Exar XRA1202 (8 bit) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -3626,7 +3690,12 @@ Params: addr I2C addr + pcf8575 Select the NXP PCF8575 (16 bit) + pca8574 Select the NXP PCA8574 (8 bit) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C3 bus (configure with the i2c3 +@@ -4296,7 +4365,12 @@ Params: int_pin GPIO use + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +@@ -4325,7 +4399,12 @@ Params: int_pin GPIO use + addr Address (default 0x48) + xtal On-board crystal frequency (default 14745600) + i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45 ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) + i2c4 Choose the I2C4 bus (configure with the i2c4 +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -123,6 +123,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -85,6 +85,8 @@ + i2c0 = <&frag100>,"target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts +@@ -167,6 +167,8 @@ + <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts +@@ -49,6 +49,8 @@ + <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -30,6 +30,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -30,6 +30,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -90,6 +90,8 @@ + i2c0 = <&frag100>, "target:0=",<&i2c0>; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; ++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+101+102"; + i2c3 = <&frag100>, "target?=0", + <&frag100>, "target-path=i2c3"; + i2c4 = <&frag100>, "target?=0", +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -254,6 +254,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -46,6 +46,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -71,6 +71,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -71,6 +71,8 @@ + <0>,"+100+101"; + i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, + <0>,"+100+101"; ++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, ++ <0>,"+100+101"; + i2c3 = <&frag0>, "target?=0", + <&frag0>, "target-path=i2c3"; + i2c4 = <&frag0>, "target?=0", diff --git a/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch b/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch new file mode 100644 index 000000000..483031057 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1481-dts-Add-noanthogs-parameter-to-CM4-and-CM5.patch @@ -0,0 +1,56 @@ +From 147ddfdaf626fe5484596235bba8bdc6dcfde501 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 20 Dec 2024 15:08:52 +0000 +Subject: [PATCH] dts: Add noanthogs parameter to CM4 and CM5 + +By default, the antenna selection on CM4 and CM5 is fixed at boot time, +with the dtparams ant1, ant2 and noant selecting which should be +enabled. Add a new dtparam - noanthogs - which leaves the GPIOs free +to be controlled at runtime by the OS. + +N.B. Using this parameter without suitable OS support will leave both +antennae disabled, resulting in attenuated WiFi and Bluetooth signals. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 2 ++ + arch/arm/boot/dts/overlays/README | 6 ++++++ + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 ++ + 3 files changed, 10 insertions(+) + +--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts ++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts +@@ -493,6 +493,8 @@ i2c_csi_dsi0: &i2c0 { + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; + + pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0"; + }; +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -153,6 +153,12 @@ Params: + + noant Disable both antennas. CM4/5 only. + ++ noanthogs Disable the GPIO hogs on the antenna controls ++ so they can be controlled at runtime. Note that ++ using this parameter without suitable OS ++ support will result in attenuated WiFi and ++ Bluetooth signals. CM4/5 only. ++ + audio Set to "on" to enable the onboard ALSA audio + interface (default "off") + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -750,5 +750,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + <&ant1>, "output-low?=on", + <&ant2>, "output-high?=off", + <&ant2>, "output-low?=on"; ++ noanthogs = <&ant1>,"status=disabled", ++ <&ant2>, "status=disabled"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1484-Add-and-update-files-for-pwm-gpio-fan-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1484-Add-and-update-files-for-pwm-gpio-fan-overlay.patch new file mode 100644 index 000000000..521b34073 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1484-Add-and-update-files-for-pwm-gpio-fan-overlay.patch @@ -0,0 +1,246 @@ +From 5fa4b1ff4db7635da04b1b8bf33b42a941064718 Mon Sep 17 00:00:00 2001 +From: Kai-Uwe Herbing <155751635+herbingk@users.noreply.github.com> +Date: Tue, 31 Dec 2024 19:44:31 +0100 +Subject: [PATCH] Add and update files for pwm-gpio-fan overlay + +Add and update files for pwm-gpio-fan overlay +Signed-off-by: Kai-Uwe Herbing <155751635+herbingk@users.noreply.github.com> +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 40 +++++ + .../dts/overlays/pwm-gpio-fan-overlay.dts | 170 ++++++++++++++++++ + 3 files changed, 211 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -219,6 +219,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + pwm.dtbo \ + pwm-2chan.dtbo \ + pwm-gpio.dtbo \ ++ pwm-gpio-fan.dtbo \ + pwm-ir-tx.dtbo \ + pwm-pio.dtbo \ + pwm1.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4036,6 +4036,46 @@ Load: dtoverlay=pwm-gpio,= + Params: gpio Output pin (default 4) + + ++Name: pwm-gpio-fan ++Info: Configure a GPIO connected PWM cooling fan controlled by the ++ software-based GPIO PWM kernel module ++Load: dtoverlay=pwm-gpio-fan,= ++Params: fan_gpio BCM number of the pin driving the fan, ++ default 18 (GPIO 18) ++ fan_temp0 CPU temperature at which fan is started with ++ low speed in millicelsius, ++ default 55000 (55 °C) ++ fan_temp1 CPU temperature at which fan is switched ++ to medium speed in millicelsius, ++ default 60000 (60 °C) ++ fan_temp2 CPU temperature at which fan is switched ++ to high speed in millicelsius, ++ default 67500 (67.5 °C) ++ fan_temp3 CPU temperature at which fan is switched ++ to max speed in millicelsius, ++ default 75000 (75 °C) ++ fan_temp0_hyst Temperature hysteris at which fan is stopped ++ in millicelsius,default 5000 (resulting ++ in 50 °C) ++ fan_temp1_hyst Temperature hysteris at which fan is switched ++ back to low speed in millicelsius, ++ default 5000 (resulting in 55 °C) ++ fan_temp2_hyst Temperature hysteris at which fan is switched ++ back to medium speed in millicelsius, ++ default 5000 (resulting in 62.5 °C) ++ fan_temp3_hyst Temperature hysteris at which fan is switched ++ back to high speed in millicelsius, ++ default 5000 (resulting in 70 °C) ++ fan_temp0_speed Fan speed for low cooling state in range ++ 0 to 255, default 114 (45% PWM duty cycle) ++ fan_temp1_speed Fan speed for medium cooling state in range ++ 0 to 255, default 152 (60% PWM duty cycle) ++ fan_temp2_speed Fan speed for high cooling state in range ++ 0 to 255, default 204 (80% PWM duty cycle) ++ fan_temp3_speed Fan speed for max cooling state in range ++ 0 to 255, default 255 (100% PWM duty cycle) ++ ++ + Name: pwm-ir-tx + Info: Use GPIO pin as pwm-assisted infrared transmitter output. + This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/pwm-gpio-fan-overlay.dts +@@ -0,0 +1,170 @@ ++/* ++ * Overlay for a GPIO connected PWM cooling fan controlled by software GPIO PWM ++ * ++ * Optional parameters: ++ * - "fan_gpio" BCM number of the pin driving the fan, default 18 (GPIO18) ++ * ++ * - "fan_temp0" CPU temperature at which fan is started with low speed in millicelsius, ++ * default 55000 (55 °C) ++ * - "fan_temp1" CPU temperature at which fan is switched to medium speed in millicelsius, ++ * default 60000 (60 °C) ++ * - "fan_temp2" CPU temperature at which fan is switched to high speed in millicelsius, ++ * default 67500 (67.5 °C) ++ * - "fan_temp3" CPU temperature at which fan is switched to max speed in millicelsius, ++ * default 75000 (75 °C) ++ * - "fan_temp0_hyst" Temperature hysteris at which fan is stopped in millicelsius, ++ * default 5000 (resulting in 50 °C) ++ * - "fan_temp1_hyst" Temperature hysteris at which fan is switched back to low speed ++ * in millicelsius, default 5000 (resulting in 55 °C) ++ * - "fan_temp2_hyst" Temperature hysteris at which fan is switched back to medium speed ++ * in millicelsius, default 5000 (resulting in 62.5 °C) ++ * - "fan_temp3_hyst" Temperature hysteris at which fan is switched back to high speed ++ * in millicelsius, default 5000 (resulting in 70 °C) ++ * - "fan_temp0_speed" Fan speed for low cooling state in range 0 to 255, ++ * default 114 (45% PWM duty cycle) ++ * - "fan_temp1_speed" Fan speed for medium cooling state in range 0 to 255, ++ * default 152 (60% PWM duty cycle) ++ * - "fan_temp2_speed" Fan speed for high cooling state in range 0 to 255, ++ * default 204 (80% PWM duty cycle) ++ * - "fan_temp3_speed" Fan speed for max cooling state in range 0 to 255, ++ * default 255 (100% PWM duty cycle) ++ * ++ * N.B. ++ * - Uses the software GPIO PWM kernel module instead of the Pis hardware PWMs (PWM0/PWM1). ++ * This will allow for an undisturbed concurrent usage of the Pis analogue audio output. ++ * ++ * Requires: ++ * - A PWM controlled cooling fan connected to the GPIO, such as an ++ * Argon mini-fan, HighPi Pro Fan or Waveshare FAN-4020-PWM-5V ++ * - Raspberry Pi OS Bookworm with kernel 6.6.62 or above ++ * ++ * Build: ++ * - sudo dtc -I dts -O dtb -o /boot/firmware/overlays/pwm-gpiofan.dtbo pwm-gpiofan-overlay.dts ++ * ++ * Activate: ++ * - sudo nano /boot/firmware/config.txt add "dtoverlay=pwm-gpiofan" ++ * ++ */ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&gpio>; ++ __overlay__ { ++ pwm_gpio_pins: pwm_gpio_pins { ++ brcm,pins = <18>; /* gpio-pin = 18 */ ++ brcm,function = <1>; /* gpio function = output */ ++ brcm,pull = <0>; /* gpio pull up/down = off */ ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target-path = "/"; ++ __overlay__ { ++ pwm_gpio: pwm_gpio { ++ compatible="pwm-gpio"; ++ #pwm-cells = <2>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_gpio_pins>; ++ gpios = <&gpio 18 0>; /* gpio-pin = 18 */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ fan0: pwm-fan { ++ compatible = "pwm-fan"; ++ #cooling-cells = <2>; ++ /* in ns = 20ms = 50 Hz */ ++ pwms = <&pwm_gpio 0 20000000 0>; ++ ++ cooling-min-state = <0>; ++ cooling-max-state = <4>; ++ /* PWM duty cycle values in a range from 0 to 255 */ ++ /* which correspond to thermal cooling states 0 to 4 */ ++ cooling-levels = <0 114 152 204 255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&cpu_thermal>; ++ __overlay__ { ++ /* in ms = poll every 2s */ ++ polling-delay = <2000>; ++ }; ++ }; ++ ++ fragment@4 { ++ target = <&thermal_trips>; ++ __overlay__ { ++ /* below temperatures in millicelsius */ ++ trip0: trip0 { ++ temperature = <55000>; /* 55 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip1: trip1 { ++ temperature = <60000>; /* 60 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip2: trip2 { ++ temperature = <67500>; /* 67.5 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ trip3: trip3 { ++ temperature = <75000>; /* 75 °C */ ++ hysteresis = <5000>; /* 5 °C */ ++ type = "active"; ++ }; ++ }; ++ }; ++ ++ fragment@5 { ++ target = <&cooling_maps>; ++ __overlay__ { ++ map0 { ++ cooling-device = <&fan0 0 1>; ++ trip = <&trip0>; ++ }; ++ map1 { ++ cooling-device = <&fan0 1 2>; ++ trip = <&trip1>; ++ }; ++ map2 { ++ cooling-device = <&fan0 2 3>; ++ trip = <&trip2>; ++ }; ++ map3 { ++ cooling-device = <&fan0 3 4>; ++ trip = <&trip3>; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ fan_gpio = <&pwm_gpio>,"gpios:4", ++ <&pwm_gpio_pins>,"brcm,pins:0"; ++ fan_temp0 = <&trip0>,"temperature:0"; ++ fan_temp0_hyst = <&trip0>,"hysteresis:0"; ++ fan_temp0_speed = <&fan0>,"cooling-levels:4"; ++ fan_temp1 = <&trip1>,"temperature:0"; ++ fan_temp1_hyst = <&trip1>,"hysteresis:0"; ++ fan_temp1_speed = <&fan0>,"cooling-levels:8"; ++ fan_temp2 = <&trip2>,"temperature:0"; ++ fan_temp2_hyst = <&trip2>,"hysteresis:0"; ++ fan_temp2_speed = <&fan0>,"cooling-levels:12"; ++ fan_temp3 = <&trip3>,"temperature:0"; ++ fan_temp3_hyst = <&trip3>,"hysteresis:0"; ++ fan_temp3_speed = <&fan0>,"cooling-levels:16"; ++ }; ++ ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1486-media-i2c-imx290-Limit-analogue-gain-according-to-mo.patch b/target/linux/bcm27xx/patches-6.6/950-1486-media-i2c-imx290-Limit-analogue-gain-according-to-mo.patch new file mode 100644 index 000000000..818f95f7c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1486-media-i2c-imx290-Limit-analogue-gain-according-to-mo.patch @@ -0,0 +1,71 @@ +From b3dd7e8947cddec41864e8d3ce5f5d8b81033d6a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 20 Nov 2024 19:17:03 +0000 +Subject: [PATCH] media: i2c: imx290: Limit analogue gain according to module + +Commit ec75fd952b0b5cdab7b606cdacba237c57c1fdda upstream. + +The imx327 only supports up to 29.4dB of analogue gain, vs +the imx290 going up to 30dB. Both are in 0.3dB steps. + +As we now have model specific config, fix this mismatch, +and delete the comment referencing it. + +Signed-off-by: Dave Stevenson +Reviewed-by: Laurent Pinchart +Reviewed-by: Alexander Stein +Signed-off-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/i2c/imx290.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/media/i2c/imx290.c ++++ b/drivers/media/i2c/imx290.c +@@ -178,6 +178,7 @@ struct imx290_model_info { + enum imx290_colour_variant colour_variant; + const struct cci_reg_sequence *init_regs; + size_t init_regs_num; ++ unsigned int max_analog_gain; + const char *name; + }; + +@@ -879,14 +880,10 @@ static int imx290_ctrl_init(struct imx29 + * up to 72.0dB (240) add further digital gain. Limit the range to + * analog gain only, support for digital gain can be added separately + * if needed. +- * +- * The IMX327 and IMX462 are largely compatible with the IMX290, but +- * have an analog gain range of 0.0dB to 29.4dB and 42dB of digital +- * gain. When support for those sensors gets added to the driver, the +- * gain control should be adjusted accordingly. + */ + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, +- V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); ++ V4L2_CID_ANALOGUE_GAIN, 0, ++ imx290->model->max_analog_gain, 1, 0); + + /* + * Correct range will be determined through imx290_ctrl_update setting +@@ -1437,18 +1434,21 @@ static const struct imx290_model_info im + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), ++ .max_analog_gain = 100, + .name = "imx290", + }, + [IMX290_MODEL_IMX290LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), ++ .max_analog_gain = 100, + .name = "imx290", + }, + [IMX290_MODEL_IMX327LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_327, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), ++ .max_analog_gain = 98, + .name = "imx327", + }, + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1488-media-dt-bindings-sony-imx290-Add-IMX462-to-the-IMX2.patch b/target/linux/bcm27xx/patches-6.6/950-1488-media-dt-bindings-sony-imx290-Add-IMX462-to-the-IMX2.patch new file mode 100644 index 000000000..7cf477c3c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1488-media-dt-bindings-sony-imx290-Add-IMX462-to-the-IMX2.patch @@ -0,0 +1,31 @@ +From f4f17c3fe223b3d8ad65f5420abbcd69ef901186 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 20 Nov 2024 19:17:05 +0000 +Subject: [PATCH] media: dt-bindings: sony,imx290: Add IMX462 to the IMX290 + binding + +Commit e4faac99d5bb4b6c80f2495c40fcd71a67c40b27 upstream. + +IMX462 is the successor to IMX290, which is supportable by +the existing IMX290 driver via a new compatible string. + +Signed-off-by: Dave Stevenson +Acked-by: Conor Dooley +Reviewed-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +--- + Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml ++++ b/Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml +@@ -30,6 +30,8 @@ properties: + - sony,imx290lqr # Colour + - sony,imx290llr # Monochrome + - sony,imx327lqr # Colour ++ - sony,imx462lqr # Colour ++ - sony,imx462llr # Monochrome + - const: sony,imx290 + deprecated: true + diff --git a/target/linux/bcm27xx/patches-6.6/950-1489-media-i2c-imx290-Add-configuration-for-IMX462.patch b/target/linux/bcm27xx/patches-6.6/950-1489-media-i2c-imx290-Add-configuration-for-IMX462.patch new file mode 100644 index 000000000..027f4571d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1489-media-i2c-imx290-Add-configuration-for-IMX462.patch @@ -0,0 +1,116 @@ +From 23037697914a6d1220768a752c6358d35ca03737 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 20 Nov 2024 19:17:06 +0000 +Subject: [PATCH] media: i2c: imx290: Add configuration for IMX462 + +Commit c699b6c7c857baba1375a1ed090bf71f695e2971 upstream. + +IMX462 is the successor to IMX290, and wants very minor +changes to the register setup. + +Add the relevant configuration to support it. + +Signed-off-by: Dave Stevenson +Reviewed-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/i2c/imx290.c | 66 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + +--- a/drivers/media/i2c/imx290.c ++++ b/drivers/media/i2c/imx290.c +@@ -172,6 +172,8 @@ enum imx290_model { + IMX290_MODEL_IMX290LQR, + IMX290_MODEL_IMX290LLR, + IMX290_MODEL_IMX327LQR, ++ IMX290_MODEL_IMX462LQR, ++ IMX290_MODEL_IMX462LLR, + }; + + struct imx290_model_info { +@@ -318,6 +320,50 @@ static const struct cci_reg_sequence imx + { CCI_REG8(0x33b3), 0x04 }, + }; + ++static const struct cci_reg_sequence imx290_global_init_settings_462[] = { ++ { CCI_REG8(0x300f), 0x00 }, ++ { CCI_REG8(0x3010), 0x21 }, ++ { CCI_REG8(0x3011), 0x02 }, ++ { CCI_REG8(0x3016), 0x09 }, ++ { CCI_REG8(0x3070), 0x02 }, ++ { CCI_REG8(0x3071), 0x11 }, ++ { CCI_REG8(0x309b), 0x10 }, ++ { CCI_REG8(0x309c), 0x22 }, ++ { CCI_REG8(0x30a2), 0x02 }, ++ { CCI_REG8(0x30a6), 0x20 }, ++ { CCI_REG8(0x30a8), 0x20 }, ++ { CCI_REG8(0x30aa), 0x20 }, ++ { CCI_REG8(0x30ac), 0x20 }, ++ { CCI_REG8(0x30b0), 0x43 }, ++ { CCI_REG8(0x3119), 0x9e }, ++ { CCI_REG8(0x311c), 0x1e }, ++ { CCI_REG8(0x311e), 0x08 }, ++ { CCI_REG8(0x3128), 0x05 }, ++ { CCI_REG8(0x313d), 0x83 }, ++ { CCI_REG8(0x3150), 0x03 }, ++ { CCI_REG8(0x317e), 0x00 }, ++ { CCI_REG8(0x32b8), 0x50 }, ++ { CCI_REG8(0x32b9), 0x10 }, ++ { CCI_REG8(0x32ba), 0x00 }, ++ { CCI_REG8(0x32bb), 0x04 }, ++ { CCI_REG8(0x32c8), 0x50 }, ++ { CCI_REG8(0x32c9), 0x10 }, ++ { CCI_REG8(0x32ca), 0x00 }, ++ { CCI_REG8(0x32cb), 0x04 }, ++ { CCI_REG8(0x332c), 0xd3 }, ++ { CCI_REG8(0x332d), 0x10 }, ++ { CCI_REG8(0x332e), 0x0d }, ++ { CCI_REG8(0x3358), 0x06 }, ++ { CCI_REG8(0x3359), 0xe1 }, ++ { CCI_REG8(0x335a), 0x11 }, ++ { CCI_REG8(0x3360), 0x1e }, ++ { CCI_REG8(0x3361), 0x61 }, ++ { CCI_REG8(0x3362), 0x10 }, ++ { CCI_REG8(0x33b0), 0x50 }, ++ { CCI_REG8(0x33b2), 0x1a }, ++ { CCI_REG8(0x33b3), 0x04 }, ++}; ++ + #define IMX290_NUM_CLK_REGS 2 + static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { + [IMX290_CLK_37_125] = { +@@ -1451,6 +1497,20 @@ static const struct imx290_model_info im + .max_analog_gain = 98, + .name = "imx327", + }, ++ [IMX290_MODEL_IMX462LQR] = { ++ .colour_variant = IMX290_VARIANT_COLOUR, ++ .init_regs = imx290_global_init_settings_462, ++ .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), ++ .max_analog_gain = 98, ++ .name = "imx462", ++ }, ++ [IMX290_MODEL_IMX462LLR] = { ++ .colour_variant = IMX290_VARIANT_MONO, ++ .init_regs = imx290_global_init_settings_462, ++ .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_462), ++ .max_analog_gain = 98, ++ .name = "imx462", ++ }, + }; + + static int imx290_parse_dt(struct imx290 *imx290) +@@ -1646,6 +1706,12 @@ static const struct of_device_id imx290_ + }, { + .compatible = "sony,imx327lqr", + .data = &imx290_models[IMX290_MODEL_IMX327LQR], ++ }, { ++ .compatible = "sony,imx462lqr", ++ .data = &imx290_models[IMX290_MODEL_IMX462LQR], ++ }, { ++ .compatible = "sony,imx462llr", ++ .data = &imx290_models[IMX290_MODEL_IMX462LLR], + }, + { /* sentinel */ }, + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1490-media-imx290-Add-module-parameter-to-allow-selection.patch b/target/linux/bcm27xx/patches-6.6/950-1490-media-imx290-Add-module-parameter-to-allow-selection.patch new file mode 100644 index 000000000..219cfce67 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1490-media-imx290-Add-module-parameter-to-allow-selection.patch @@ -0,0 +1,60 @@ +From c633c2e93e460925120e0817c14bbfc444a70226 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 15 May 2024 12:43:15 +0100 +Subject: [PATCH] media: imx290: Add module parameter to allow selection of HCG + mode + +The sensor has Low Conversion Gain (HCG) and High Conversion Gain (HCG) +modes, with the supposedly the HCG mode having better noise performance +at high gains. + +As this parameter changes the gain range of the sensor, it isn't +possible to make this an automatic property, and there is no +suitable V4L2 control to set it, so just add it as a module parameter. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx290.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx290.c ++++ b/drivers/media/i2c/imx290.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -41,6 +42,9 @@ + #define IMX290_WINMODE_720P (1 << 4) + #define IMX290_WINMODE_CROP (4 << 4) + #define IMX290_FR_FDG_SEL CCI_REG8(0x3009) ++#define IMX290_FDG_HCG BIT(4) ++#define IMX290_FRSEL_60FPS BIT(0) ++#define IMX290_FDG_LCG 0 + #define IMX290_BLKLEVEL CCI_REG16_LE(0x300a) + #define IMX290_GAIN CCI_REG8(0x3014) + #define IMX290_VMAX CCI_REG24_LE(0x3018) +@@ -162,6 +166,10 @@ + + #define IMX290_NUM_SUPPLIES 3 + ++static bool hcg_mode; ++module_param(hcg_mode, bool, 0664); ++MODULE_PARM_DESC(hcg_mode, "Enable HCG mode"); ++ + enum imx290_colour_variant { + IMX290_VARIANT_COLOUR, + IMX290_VARIANT_MONO, +@@ -697,7 +705,8 @@ static int imx290_set_data_lanes(struct + &ret); + cci_write(imx290->regmap, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, + &ret); +- cci_write(imx290->regmap, IMX290_FR_FDG_SEL, 0x01, &ret); ++ cci_write(imx290->regmap, IMX290_FR_FDG_SEL, IMX290_FRSEL_60FPS | ++ (hcg_mode ? IMX290_FDG_HCG : IMX290_FDG_LCG), &ret); + + return ret; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1491-dtoverlays-Switch-imx462-overlay-to-use-the-new-comp.patch b/target/linux/bcm27xx/patches-6.6/950-1491-dtoverlays-Switch-imx462-overlay-to-use-the-new-comp.patch new file mode 100644 index 000000000..8e894791c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1491-dtoverlays-Switch-imx462-overlay-to-use-the-new-comp.patch @@ -0,0 +1,47 @@ +From eec7048c4e3aec1aadc21fcffcf6be9f5385f72a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 12 Nov 2024 14:15:30 +0000 +Subject: [PATCH] dtoverlays: Switch imx462 overlay to use the new compatible + +Now that imx462 has a separate compatible string, make use of it. + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/imx462-overlay.dts | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +--- a/arch/arm/boot/dts/overlays/imx462-overlay.dts ++++ b/arch/arm/boot/dts/overlays/imx462-overlay.dts +@@ -1,9 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + // Definitions for IMX462 camera module on VC I2C bus + +-// IMX462 is the successor to IMX290. The drivers currently don't support +-// any additional feature of IMX462, so use the IMX290 compatible strings +-// for now. ++// IMX462 is the successor to IMX290. + + /dts-v1/; + /plugin/; +@@ -17,19 +15,17 @@ + // Fragment numbers deliberately high to avoid conflicts with the + // included imx290_327 overlay file. + +- //IMX462 is not defined in the bindings, so use IMX290 for now. +- + fragment@101 { + target = <&cam_node>; + __overlay__ { +- compatible = "sony,imx290lqr"; ++ compatible = "sony,imx462lqr"; + }; + }; + + fragment@102 { + target = <&cam_node>; + __dormant__ { +- compatible = "sony,imx290llr"; ++ compatible = "sony,imx462llr"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1492-media-i2c-imx415-Add-more-clock-configurations.patch b/target/linux/bcm27xx/patches-6.6/950-1492-media-i2c-imx415-Add-more-clock-configurations.patch new file mode 100644 index 000000000..0b04bf0c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1492-media-i2c-imx415-Add-more-clock-configurations.patch @@ -0,0 +1,327 @@ +From 0a0814f830829b1a377273ddb09c156c84e1a8ca Mon Sep 17 00:00:00 2001 +From: Alexander Stein +Date: Wed, 17 Jan 2024 08:39:36 +0100 +Subject: [PATCH] media: i2c: imx415: Add more clock configurations + +Commit b814b5b2ec2d327b79e415c1baa5eecdf9aa786b upstream. + +Complete the list from "INCK Setting" section in IMX415-AAQR-C +(Rev. E19504, 2019/05/21). For consistency suffix all lane rate values by +UL, which is needed for 2376000000 anyway. + +Signed-off-by: Alexander Stein +Signed-off-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/i2c/imx415.c | 265 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 260 insertions(+), 5 deletions(-) + +--- a/drivers/media/i2c/imx415.c ++++ b/drivers/media/i2c/imx415.c +@@ -124,7 +124,7 @@ struct imx415_clk_params { + /* INCK Settings - includes all lane rate and INCK dependent registers */ + static const struct imx415_clk_params imx415_clk_params[] = { + { +- .lane_rate = 594000000, ++ .lane_rate = 594000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -139,7 +139,37 @@ static const struct imx415_clk_params im + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, + { +- .lane_rate = 720000000, ++ .lane_rate = 594000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x7 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x080 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0984 }, ++ }, ++ { ++ .lane_rate = 594000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x7 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x080 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 720000000UL, + .inck = 24000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x054 }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x03B }, +@@ -154,7 +184,22 @@ static const struct imx415_clk_params im + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 }, + }, + { +- .lane_rate = 891000000, ++ .lane_rate = 720000000UL, ++ .inck = 72000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0F8 }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B0 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x9 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1200 }, ++ }, ++ { ++ .lane_rate = 891000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -169,7 +214,37 @@ static const struct imx415_clk_params im + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, + { +- .lane_rate = 1440000000, ++ .lane_rate = 891000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x5 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 891000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x5 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x0 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x1 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 1440000000UL, + .inck = 24000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x054 }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x03B }, +@@ -184,7 +259,22 @@ static const struct imx415_clk_params im + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0600 }, + }, + { +- .lane_rate = 1485000000, ++ .lane_rate = 1440000000UL, ++ .inck = 72000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0F8 }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B0 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1200 }, ++ }, ++ { ++ .lane_rate = 1485000000UL, + .inck = 27000000, + .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, + .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, +@@ -198,6 +288,171 @@ static const struct imx415_clk_params im + .regs[9] = { IMX415_INCKSEL7, 0x0 }, + .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, + }, ++ { ++ .lane_rate = 1485000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 1485000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x8 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0A0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C6 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 1782000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x4 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0C0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E7 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 2079000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x2 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x0E0 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 27000000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x05D }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x042 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x23 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x108 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E7 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x23 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x06C0 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 37125000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x07F }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x05B }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x24 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x100 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x24 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x0948 }, ++ }, ++ { ++ .lane_rate = 2376000000UL, ++ .inck = 74250000, ++ .regs[0] = { IMX415_BCWAIT_TIME, 0x0FF }, ++ .regs[1] = { IMX415_CPWAIT_TIME, 0x0B6 }, ++ .regs[2] = { IMX415_SYS_MODE, 0x0 }, ++ .regs[3] = { IMX415_INCKSEL1, 0x00 }, ++ .regs[4] = { IMX415_INCKSEL2, 0x28 }, ++ .regs[5] = { IMX415_INCKSEL3, 0x100 }, ++ .regs[6] = { IMX415_INCKSEL4, 0x0E0 }, ++ .regs[7] = { IMX415_INCKSEL5, 0x28 }, ++ .regs[8] = { IMX415_INCKSEL6, 0x1 }, ++ .regs[9] = { IMX415_INCKSEL7, 0x0 }, ++ .regs[10] = { IMX415_TXCLKESC_FREQ, 0x1290 }, ++ }, + }; + + /* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1494-dtoverlays-Add-overlay-for-Sony-IMX415-image-sensor.patch b/target/linux/bcm27xx/patches-6.6/950-1494-dtoverlays-Add-overlay-for-Sony-IMX415-image-sensor.patch new file mode 100644 index 000000000..139cd5828 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1494-dtoverlays-Add-overlay-for-Sony-IMX415-image-sensor.patch @@ -0,0 +1,214 @@ +From 384e58f63a3a328f7f656052b6357f3408aac6c3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 31 Dec 2024 20:33:46 +0000 +Subject: [PATCH] dtoverlays: Add overlay for Sony IMX415 image sensor + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 31 +++++ + arch/arm/boot/dts/overlays/imx415-overlay.dts | 116 ++++++++++++++++++ + arch/arm/boot/dts/overlays/imx415.dtsi | 27 ++++ + 4 files changed, 175 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/imx415-overlay.dts + create mode 100644 arch/arm/boot/dts/overlays/imx415.dtsi + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -135,6 +135,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + imx296.dtbo \ + imx327.dtbo \ + imx378.dtbo \ ++ imx415.dtbo \ + imx462.dtbo \ + imx477.dtbo \ + imx500.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2859,6 +2859,37 @@ Params: rotation Mounting + 450000000 (default), 453000000, 456000000. + + ++Name: imx415 ++Info: Sony IMX415 camera module. ++ Uses Unicam 1, which is the standard camera connector on most Pi ++ variants. By default this uses 4 CSI2 data lanes, so requires a ++ Compute Module or Pi5. ++Load: dtoverlay=imx415, ++Params: addr Set I2C address of sensor. Valid values are ++ 0x10, 0x1a, 0x36 and 0x37. Default is 0x37. ++ 4lane Enable 4 CSI2 data lanes. ++ clock-frequency Sets the clock frequency to match that used on ++ the board. ++ Valid values are 24, 27, 37.125, 72, or ++ 74.25MHz. ++ The default is 24MHz. ++ Note that the link frequencies permitted vary ++ based on the oscillator used. ++ link-frequency Confgures the link frequency to be used. Note ++ that the permitted values vary based on ++ clock-frequency and number of lanes. ++ The default is 360MHz for 720Mbit/s. ++ orientation Sensor orientation (0 = front, 1 = rear, ++ 2 = external, default external) ++ rotation Mounting rotation of the camera sensor (0 or ++ 180, default 0) ++ media-controller Configure use of Media Controller API for ++ configuring the sensor (default on) ++ cam0 Adopt the default configuration for CAM0 on a ++ Compute Module (CSI0, i2c_vc, and cam0_reg). ++ vcm Enable ad5398 VCM associated with the sensor. ++ ++ + Name: imx462 + Info: Sony IMX462 camera module. + Uses Unicam 1, which is the standard camera connector on most Pi +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/imx415-overlay.dts +@@ -0,0 +1,116 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// Definitions for IMX415 camera module on VC I2C bus ++/dts-v1/; ++/plugin/; ++ ++#include ++ ++/{ ++ compatible = "brcm,bcm2835"; ++ ++ fragment@0 { ++ target = <&i2c0if>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ clk_frag: fragment@1 { ++ target = <&cam1_clk>; ++ cam_clk: __overlay__ { ++ status = "okay"; ++ clock-frequency = <24000000>; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c0mux>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ reg_frag: fragment@3 { ++ target = <&cam1_reg>; ++ cam_reg: __overlay__ { ++ startup-delay-us = <100000>; ++ }; ++ }; ++ ++ i2c_frag: fragment@100 { ++ target = <&i2c_csi_dsi>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ #include "imx415.dtsi" ++ ++ vcm: ad5398@c { ++ compatible = "adi,ad5398"; ++ reg = <0x0c>; ++ status = "disabled"; ++ VANA-supply = <&cam1_reg>; ++ }; ++ }; ++ }; ++ ++ 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; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@201 { ++ target = <&cam_endpoint>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ fragment@202 { ++ target = <&csi_ep>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ ++ __overrides__ { ++ addr = <&cam_node>, "reg:0"; ++ 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>, ++ <®_frag>, "target:0=",<&cam0_reg>, ++ <&clk_frag>, "target:0=",<&cam0_clk>, ++ <&cam_node>, "clocks:0=",<&cam0_clk>, ++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>, ++ <&vcm>, "VANA-supply:0=", <&cam0_reg>; ++ vcm = <&vcm>, "status=okay", ++ <&cam_node>,"lens-focus:0=", <&vcm>; ++ clock-frequency = <&cam_clk>, "clock-frequency:0"; ++ link-frequency = <&cam_endpoint>,"link-frequencies#0"; ++ 4lane = <0>, "+201+202"; ++ }; ++}; ++ ++&cam_node { ++ status = "okay"; ++}; ++ ++&cam_endpoint { ++ remote-endpoint = <&csi_ep>; ++}; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/imx415.dtsi +@@ -0,0 +1,27 @@ ++// Fragment that configures an imx415 ++ ++cam_node: imx415@37 { ++ compatible = "sony,imx415"; ++ reg = <0x37>; ++ status = "disabled"; ++ ++ clocks = <&cam1_clk>; ++ clock-names = "inck"; ++ ++ avdd-supply = <&cam1_reg>; /* 2.8v */ ++ dvdd-supply = <&cam_dummy_reg>; /* 1.8v */ ++ ovdd-supply = <&cam_dummy_reg>; /* 1.2v */ ++ ++ rotation = <180>; ++ orientation = <2>; ++ ++ port { ++ cam_endpoint: endpoint { ++ clock-lanes = <0>; ++ data-lanes = <1 2>; ++ //clock-noncontinuous; ++ link-frequencies = ++ /bits/ 64 <360000000>; ++ }; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1495-media-i2c-imx415-Add-read-write-control-of-VBLANK.patch b/target/linux/bcm27xx/patches-6.6/950-1495-media-i2c-imx415-Add-read-write-control-of-VBLANK.patch new file mode 100644 index 000000000..aee483b97 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1495-media-i2c-imx415-Add-read-write-control-of-VBLANK.patch @@ -0,0 +1,146 @@ +From 4e8d73ce89c6dd6fdcb8dd7df8310762707c5b1a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 1 Jan 2025 14:18:25 +0000 +Subject: [PATCH] media: i2c: imx415: Add read/write control of VBLANK + +This also requires that the ranges for the exposure control +are updated. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx415.c | 52 +++++++++++++++++++++++++------------- + 1 file changed, 34 insertions(+), 18 deletions(-) + +--- a/drivers/media/i2c/imx415.c ++++ b/drivers/media/i2c/imx415.c +@@ -25,6 +25,7 @@ + #define IMX415_PIXEL_ARRAY_WIDTH 3864 + #define IMX415_PIXEL_ARRAY_HEIGHT 2192 + #define IMX415_PIXEL_ARRAY_VBLANK 58 ++#define IMX415_EXPOSURE_OFFSET 8 + + #define IMX415_NUM_CLK_PARAM_REGS 11 + +@@ -56,6 +57,7 @@ + #define IMX415_OUTSEL IMX415_REG_8BIT(0x30C0) + #define IMX415_DRV IMX415_REG_8BIT(0x30C1) + #define IMX415_VMAX IMX415_REG_24BIT(0x3024) ++#define IMX415_VMAX_MAX 0xfffff + #define IMX415_HMAX IMX415_REG_16BIT(0x3028) + #define IMX415_SHR0 IMX415_REG_24BIT(0x3050) + #define IMX415_GAIN_PCG_0 IMX415_REG_16BIT(0x3090) +@@ -457,7 +459,6 @@ static const struct imx415_clk_params im + + /* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ + static const struct imx415_reg imx415_mode_2_720[] = { +- { IMX415_VMAX, 0x08CA }, + { IMX415_HMAX, 0x07F0 }, + { IMX415_LANEMODE, IMX415_LANEMODE_2 }, + { IMX415_TCLKPOST, 0x006F }, +@@ -473,7 +474,6 @@ static const struct imx415_reg imx415_mo + + /* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ + static const struct imx415_reg imx415_mode_2_1440[] = { +- { IMX415_VMAX, 0x08CA }, + { IMX415_HMAX, 0x042A }, + { IMX415_LANEMODE, IMX415_LANEMODE_2 }, + { IMX415_TCLKPOST, 0x009F }, +@@ -489,7 +489,6 @@ static const struct imx415_reg imx415_mo + + /* all-pixel 4-lane 891 Mbps 30 Hz mode */ + static const struct imx415_reg imx415_mode_4_891[] = { +- { IMX415_VMAX, 0x08CA }, + { IMX415_HMAX, 0x044C }, + { IMX415_LANEMODE, IMX415_LANEMODE_4 }, + { IMX415_TCLKPOST, 0x007F }, +@@ -617,6 +616,7 @@ struct imx415 { + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *exposure; + + unsigned int cur_mode; + unsigned int num_data_lanes; +@@ -795,16 +795,37 @@ static int imx415_s_ctrl(struct v4l2_ctr + ctrls); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; ++ u32 exposure_max; + unsigned int vmax; + unsigned int flip; +- +- if (!sensor->streaming) +- return 0; ++ int ret; + + state = v4l2_subdev_get_locked_active_state(&sensor->subdev); + format = v4l2_subdev_get_pad_format(&sensor->subdev, state, 0); + ++ if (ctrl->id == V4L2_CID_VBLANK) { ++ exposure_max = format->height + ctrl->val - ++ IMX415_EXPOSURE_OFFSET; ++ __v4l2_ctrl_modify_range(sensor->exposure, ++ sensor->exposure->minimum, ++ exposure_max, sensor->exposure->step, ++ sensor->exposure->default_value); ++ } ++ ++ if (!sensor->streaming) ++ return 0; ++ + switch (ctrl->id) { ++ case V4L2_CID_VBLANK: ++ ret = imx415_write(sensor, IMX415_VMAX, ++ format->height + ctrl->val); ++ if (ret) ++ return ret; ++ /* ++ * Deliberately fall through as exposure is set based on VMAX ++ * which has just changed. ++ */ ++ fallthrough; + case V4L2_CID_EXPOSURE: + /* clamp the exposure value to VMAX. */ + vmax = format->height + sensor->vblank->cur.val; +@@ -840,7 +861,8 @@ static int imx415_ctrls_init(struct imx4 + u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate; + u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate; + u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT + +- IMX415_PIXEL_ARRAY_VBLANK - 8; ++ IMX415_PIXEL_ARRAY_VBLANK - ++ IMX415_EXPOSURE_OFFSET; + u32 hblank; + unsigned int i; + int ret; +@@ -869,8 +891,9 @@ static int imx415_ctrls_init(struct imx4 + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + +- v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, V4L2_CID_EXPOSURE, +- 4, exposure_max, 1, exposure_max); ++ sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, ++ V4L2_CID_EXPOSURE, 4, ++ exposure_max, 1, exposure_max); + + v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX415_AGAIN_MIN, +@@ -887,16 +910,9 @@ static int imx415_ctrls_init(struct imx4 + sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_VBLANK, + IMX415_PIXEL_ARRAY_VBLANK, +- IMX415_PIXEL_ARRAY_VBLANK, 1, +- IMX415_PIXEL_ARRAY_VBLANK); +- if (sensor->vblank) +- sensor->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; +- +- /* +- * The pixel rate used here is a virtual value and can be used for +- * calculating the frame rate together with hblank. It may not +- * necessarily be the physically correct pixel clock. +- */ ++ IMX415_VMAX_MAX - IMX415_PIXEL_ARRAY_HEIGHT, ++ 1, IMX415_PIXEL_ARRAY_VBLANK); ++ + v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1496-media-i2c-imx415-Make-HBLANK-controllable-and-in-con.patch b/target/linux/bcm27xx/patches-6.6/950-1496-media-i2c-imx415-Make-HBLANK-controllable-and-in-con.patch new file mode 100644 index 000000000..7ad9e1195 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1496-media-i2c-imx415-Make-HBLANK-controllable-and-in-con.patch @@ -0,0 +1,231 @@ +From 6722e1358768671c1e5761aa092efb4ae62b2c46 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 1 Jan 2025 17:01:34 +0000 +Subject: [PATCH] media: i2c: imx415: Make HBLANK controllable and in + consistent units + +The control of HMAX documented in the datasheet is consistent +with being in terms of a scaled INCK, being always 72MHz or +74.25MHz. It is NOT link frequency dependent, but the minimum +value for HMAX is dictated by the link frequency. + +If PIXEL_RATE is defined as being 12 times the 72 or 74.25MHz, +and all values are scaled down again when writing HMAX, then +the numbers all work out regardless of INCK or link frequency. +Retain an hmax_min (set to the same value as the previous fixed +hmax register value) to set as the default value to avoid changing +the behaviour for existing users. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx415.c | 86 +++++++++++++++++--------------------- + 1 file changed, 38 insertions(+), 48 deletions(-) + +--- a/drivers/media/i2c/imx415.c ++++ b/drivers/media/i2c/imx415.c +@@ -27,6 +27,9 @@ + #define IMX415_PIXEL_ARRAY_VBLANK 58 + #define IMX415_EXPOSURE_OFFSET 8 + ++#define IMX415_PIXEL_RATE_74_25MHZ 891000000 ++#define IMX415_PIXEL_RATE_72MHZ 864000000 ++ + #define IMX415_NUM_CLK_PARAM_REGS 11 + + #define IMX415_REG_8BIT(n) ((1 << 16) | (n)) +@@ -59,6 +62,8 @@ + #define IMX415_VMAX IMX415_REG_24BIT(0x3024) + #define IMX415_VMAX_MAX 0xfffff + #define IMX415_HMAX IMX415_REG_16BIT(0x3028) ++#define IMX415_HMAX_MAX 0xffff ++#define IMX415_HMAX_MULTIPLIER 12 + #define IMX415_SHR0 IMX415_REG_24BIT(0x3050) + #define IMX415_GAIN_PCG_0 IMX415_REG_16BIT(0x3090) + #define IMX415_AGAIN_MIN 0 +@@ -459,7 +464,6 @@ static const struct imx415_clk_params im + + /* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ + static const struct imx415_reg imx415_mode_2_720[] = { +- { IMX415_HMAX, 0x07F0 }, + { IMX415_LANEMODE, IMX415_LANEMODE_2 }, + { IMX415_TCLKPOST, 0x006F }, + { IMX415_TCLKPREPARE, 0x002F }, +@@ -474,7 +478,6 @@ static const struct imx415_reg imx415_mo + + /* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ + static const struct imx415_reg imx415_mode_2_1440[] = { +- { IMX415_HMAX, 0x042A }, + { IMX415_LANEMODE, IMX415_LANEMODE_2 }, + { IMX415_TCLKPOST, 0x009F }, + { IMX415_TCLKPREPARE, 0x0057 }, +@@ -489,7 +492,6 @@ static const struct imx415_reg imx415_mo + + /* all-pixel 4-lane 891 Mbps 30 Hz mode */ + static const struct imx415_reg imx415_mode_4_891[] = { +- { IMX415_HMAX, 0x044C }, + { IMX415_LANEMODE, IMX415_LANEMODE_4 }, + { IMX415_TCLKPOST, 0x007F }, + { IMX415_TCLKPREPARE, 0x0037 }, +@@ -507,39 +509,10 @@ struct imx415_mode_reg_list { + const struct imx415_reg *regs; + }; + +-/* +- * Mode : number of lanes, lane rate and frame rate dependent settings +- * +- * pixel_rate and hmax_pix are needed to calculate hblank for the v4l2 ctrl +- * interface. These values can not be found in the data sheet and should be +- * treated as virtual values. Use following table when adding new modes. +- * +- * lane_rate lanes fps hmax_pix pixel_rate +- * +- * 594 2 10.000 4400 99000000 +- * 891 2 15.000 4400 148500000 +- * 720 2 15.748 4064 144000000 +- * 1782 2 30.000 4400 297000000 +- * 2079 2 30.000 4400 297000000 +- * 1440 2 30.019 4510 304615385 +- * +- * 594 4 20.000 5500 247500000 +- * 594 4 25.000 4400 247500000 +- * 720 4 25.000 4400 247500000 +- * 720 4 30.019 4510 304615385 +- * 891 4 30.000 4400 297000000 +- * 1440 4 30.019 4510 304615385 +- * 1440 4 60.038 4510 609230769 +- * 1485 4 60.000 4400 594000000 +- * 1782 4 60.000 4400 594000000 +- * 2079 4 60.000 4400 594000000 +- * 2376 4 90.164 4392 891000000 +- */ + struct imx415_mode { + u64 lane_rate; + u32 lanes; +- u32 hmax_pix; +- u64 pixel_rate; ++ u32 hmax_min; + struct imx415_mode_reg_list reg_list; + }; + +@@ -548,8 +521,7 @@ static const struct imx415_mode supporte + { + .lane_rate = 720000000, + .lanes = 2, +- .hmax_pix = 4064, +- .pixel_rate = 144000000, ++ .hmax_min = 2032, + .reg_list = { + .num_of_regs = ARRAY_SIZE(imx415_mode_2_720), + .regs = imx415_mode_2_720, +@@ -558,8 +530,7 @@ static const struct imx415_mode supporte + { + .lane_rate = 1440000000, + .lanes = 2, +- .hmax_pix = 4510, +- .pixel_rate = 304615385, ++ .hmax_min = 1066, + .reg_list = { + .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440), + .regs = imx415_mode_2_1440, +@@ -568,8 +539,7 @@ static const struct imx415_mode supporte + { + .lane_rate = 891000000, + .lanes = 4, +- .hmax_pix = 4400, +- .pixel_rate = 297000000, ++ .hmax_min = 1100, + .reg_list = { + .num_of_regs = ARRAY_SIZE(imx415_mode_4_891), + .regs = imx415_mode_4_891, +@@ -601,6 +571,7 @@ static const char *const imx415_test_pat + struct imx415 { + struct device *dev; + struct clk *clk; ++ unsigned long pixel_rate; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx415_supply_names)]; + struct gpio_desc *reset; + struct regmap *regmap; +@@ -614,6 +585,7 @@ struct imx415 { + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *exposure; +@@ -845,6 +817,11 @@ static int imx415_s_ctrl(struct v4l2_ctr + case V4L2_CID_TEST_PATTERN: + return imx415_set_testpattern(sensor, ctrl->val); + ++ case V4L2_CID_HBLANK: ++ return imx415_write(sensor, IMX415_HMAX, ++ (format->width + ctrl->val) / ++ IMX415_HMAX_MULTIPLIER); ++ + default: + return -EINVAL; + } +@@ -858,12 +835,11 @@ static int imx415_ctrls_init(struct imx4 + { + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *ctrl; +- u64 pixel_rate = supported_modes[sensor->cur_mode].pixel_rate; + u64 lane_rate = supported_modes[sensor->cur_mode].lane_rate; + u32 exposure_max = IMX415_PIXEL_ARRAY_HEIGHT + + IMX415_PIXEL_ARRAY_VBLANK - + IMX415_EXPOSURE_OFFSET; +- u32 hblank; ++ u32 hblank_min, hblank_max; + unsigned int i; + int ret; + +@@ -900,12 +876,14 @@ static int imx415_ctrls_init(struct imx4 + IMX415_AGAIN_MAX, IMX415_AGAIN_STEP, + IMX415_AGAIN_MIN); + +- hblank = supported_modes[sensor->cur_mode].hmax_pix - +- IMX415_PIXEL_ARRAY_WIDTH; ++ hblank_min = (supported_modes[sensor->cur_mode].hmax_min * ++ IMX415_HMAX_MULTIPLIER) - IMX415_PIXEL_ARRAY_WIDTH; ++ hblank_max = (IMX415_HMAX_MAX * IMX415_HMAX_MULTIPLIER) - ++ IMX415_PIXEL_ARRAY_WIDTH; + ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, +- V4L2_CID_HBLANK, hblank, hblank, 1, hblank); +- if (ctrl) +- ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ V4L2_CID_HBLANK, hblank_min, ++ hblank_max, IMX415_HMAX_MULTIPLIER, ++ hblank_min); + + sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_VBLANK, +@@ -913,8 +891,9 @@ static int imx415_ctrls_init(struct imx4 + IMX415_VMAX_MAX - IMX415_PIXEL_ARRAY_HEIGHT, + 1, IMX415_PIXEL_ARRAY_VBLANK); + +- v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, pixel_rate, +- pixel_rate, 1, pixel_rate); ++ v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, ++ sensor->pixel_rate, sensor->pixel_rate, 1, ++ sensor->pixel_rate); + + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &imx415_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); +@@ -1410,6 +1389,17 @@ static int imx415_parse_hw_config(struct + "no valid sensor mode defined\n"); + goto done_endpoint_free; + } ++ switch (inck) { ++ case 27000000: ++ case 37125000: ++ case 74250000: ++ sensor->pixel_rate = IMX415_PIXEL_RATE_74_25MHZ; ++ break; ++ case 24000000: ++ case 72000000: ++ sensor->pixel_rate = IMX415_PIXEL_RATE_72MHZ; ++ break; ++ } + + lane_rate = supported_modes[sensor->cur_mode].lane_rate; + for (i = 0; i < ARRAY_SIZE(imx415_clk_params); ++i) { diff --git a/target/linux/bcm27xx/patches-6.6/950-1497-media-i2c-imx415-Link-frequencies-are-not-exclusive-.patch b/target/linux/bcm27xx/patches-6.6/950-1497-media-i2c-imx415-Link-frequencies-are-not-exclusive-.patch new file mode 100644 index 000000000..d0375058c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1497-media-i2c-imx415-Link-frequencies-are-not-exclusive-.patch @@ -0,0 +1,136 @@ +From 0292614f8cd061f71f975dae7d74fe5324545321 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 2 Jan 2025 12:43:01 +0000 +Subject: [PATCH] media: i2c: imx415: Link frequencies are not exclusive to num + lanes + +The link frequencies are equally valid in 2 or 4 lane modes, but +they change the hmax_min value for the mode as the MIPI block +has to have sufficient time to send the pixel data for each line. + +Remove the association with number of lanes, and add hmax_min +configuration for both lane options. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx415.c | 49 +++++++++++++++++--------------------- + 1 file changed, 22 insertions(+), 27 deletions(-) + +--- a/drivers/media/i2c/imx415.c ++++ b/drivers/media/i2c/imx415.c +@@ -462,9 +462,8 @@ static const struct imx415_clk_params im + }, + }; + +-/* all-pixel 2-lane 720 Mbps 15.74 Hz mode */ +-static const struct imx415_reg imx415_mode_2_720[] = { +- { IMX415_LANEMODE, IMX415_LANEMODE_2 }, ++/* 720 Mbps CSI configuration */ ++static const struct imx415_reg imx415_linkrate_720mbps[] = { + { IMX415_TCLKPOST, 0x006F }, + { IMX415_TCLKPREPARE, 0x002F }, + { IMX415_TCLKTRAIL, 0x002F }, +@@ -476,9 +475,8 @@ static const struct imx415_reg imx415_mo + { IMX415_TLPX, 0x0027 }, + }; + +-/* all-pixel 2-lane 1440 Mbps 30.01 Hz mode */ +-static const struct imx415_reg imx415_mode_2_1440[] = { +- { IMX415_LANEMODE, IMX415_LANEMODE_2 }, ++/* 1440 Mbps CSI configuration */ ++static const struct imx415_reg imx415_linkrate_1440mbps[] = { + { IMX415_TCLKPOST, 0x009F }, + { IMX415_TCLKPREPARE, 0x0057 }, + { IMX415_TCLKTRAIL, 0x0057 }, +@@ -490,9 +488,8 @@ static const struct imx415_reg imx415_mo + { IMX415_TLPX, 0x004F }, + }; + +-/* all-pixel 4-lane 891 Mbps 30 Hz mode */ +-static const struct imx415_reg imx415_mode_4_891[] = { +- { IMX415_LANEMODE, IMX415_LANEMODE_4 }, ++/* 891 Mbps */ ++static const struct imx415_reg imx415_linkrate_891mbps[] = { + { IMX415_TCLKPOST, 0x007F }, + { IMX415_TCLKPREPARE, 0x0037 }, + { IMX415_TCLKTRAIL, 0x0037 }, +@@ -511,8 +508,7 @@ struct imx415_mode_reg_list { + + struct imx415_mode { + u64 lane_rate; +- u32 lanes; +- u32 hmax_min; ++ u32 hmax_min[2]; + struct imx415_mode_reg_list reg_list; + }; + +@@ -520,29 +516,26 @@ struct imx415_mode { + static const struct imx415_mode supported_modes[] = { + { + .lane_rate = 720000000, +- .lanes = 2, +- .hmax_min = 2032, ++ .hmax_min = { 2032, 1066 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_2_720), +- .regs = imx415_mode_2_720, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_720mbps), ++ .regs = imx415_linkrate_720mbps, + }, + }, + { + .lane_rate = 1440000000, +- .lanes = 2, +- .hmax_min = 1066, ++ .hmax_min = { 1066, 533 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_2_1440), +- .regs = imx415_mode_2_1440, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_1440mbps), ++ .regs = imx415_linkrate_1440mbps, + }, + }, + { + .lane_rate = 891000000, +- .lanes = 4, +- .hmax_min = 1100, ++ .hmax_min = { 1100, 550 }, + .reg_list = { +- .num_of_regs = ARRAY_SIZE(imx415_mode_4_891), +- .regs = imx415_mode_4_891, ++ .num_of_regs = ARRAY_SIZE(imx415_linkrate_891mbps), ++ .regs = imx415_linkrate_891mbps, + }, + }, + }; +@@ -876,7 +869,7 @@ static int imx415_ctrls_init(struct imx4 + IMX415_AGAIN_MAX, IMX415_AGAIN_STEP, + IMX415_AGAIN_MIN); + +- hblank_min = (supported_modes[sensor->cur_mode].hmax_min * ++ hblank_min = (supported_modes[sensor->cur_mode].hmax_min[sensor->num_data_lanes == 2 ? 0 : 1] * + IMX415_HMAX_MULTIPLIER) - IMX415_PIXEL_ARRAY_WIDTH; + hblank_max = (IMX415_HMAX_MAX * IMX415_HMAX_MULTIPLIER) - + IMX415_PIXEL_ARRAY_WIDTH; +@@ -944,7 +937,11 @@ static int imx415_set_mode(struct imx415 + return ret; + } + +- return 0; ++ ret = imx415_write(sensor, IMX415_LANEMODE, ++ sensor->num_data_lanes == 2 ? IMX415_LANEMODE_2 : ++ IMX415_LANEMODE_4); ++ ++ return ret; + } + + static int imx415_setup(struct imx415 *sensor, struct v4l2_subdev_state *state) +@@ -1373,8 +1370,6 @@ static int imx415_parse_hw_config(struct + } + + for (j = 0; j < ARRAY_SIZE(supported_modes); ++j) { +- if (sensor->num_data_lanes != supported_modes[j].lanes) +- continue; + if (bus_cfg.link_frequencies[i] * 2 != + supported_modes[j].lane_rate) + continue; diff --git a/target/linux/bcm27xx/patches-6.6/950-1499-dts-bcm2712-PL011-UARTs-are-actually-r1p5.patch b/target/linux/bcm27xx/patches-6.6/950-1499-dts-bcm2712-PL011-UARTs-are-actually-r1p5.patch new file mode 100644 index 000000000..520d1d642 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1499-dts-bcm2712-PL011-UARTs-are-actually-r1p5.patch @@ -0,0 +1,43 @@ +From 602be52637ecca0b247cf832c8a4ec345844d325 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 7 Jan 2025 12:09:48 +0000 +Subject: [PATCH] dts: bcm2712: PL011 UARTs are actually r1p5 + +The ARM PL011 UART instances in BCM2712 are r1p5 spec, which means they +have 32-entry FIFOs. The correct periphid value for this is 0x00341011. +Thanks to N Buchwitz for pointing this out. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +@@ -190,7 +190,7 @@ + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + +@@ -201,7 +201,7 @@ + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + +@@ -212,7 +212,7 @@ + clocks = <&clk_uart>, + <&clk_vpu>; + clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; ++ arm,primecell-periphid = <0x00341011>; + status = "disabled"; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1500-dts-rp1-PL011-UARTs-are-actually-r1p5.patch b/target/linux/bcm27xx/patches-6.6/950-1500-dts-rp1-PL011-UARTs-are-actually-r1p5.patch new file mode 100644 index 000000000..9363036cc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1500-dts-rp1-PL011-UARTs-are-actually-r1p5.patch @@ -0,0 +1,70 @@ +From 7a4972f36a63eba4d862115265a7bf77ac1e4129 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 7 Jan 2025 12:11:10 +0000 +Subject: [PATCH] dts: rp1: PL011 UARTs are actually r1p5 + +The ARM PL011 UART instances in RP1 are r1p5 spec, which means they +have 32-entry FIFOs. The correct periphid value for this is 0x00341011. +Thanks to N Buchwitz for pointing this out. + +Signed-off-by: Phil Elwell +--- + arch/arm64/boot/dts/broadcom/rp1.dtsi | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi ++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi +@@ -69,7 +69,7 @@ + <&rp1_dma RP1_DMA_UART0_RX>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; +@@ -86,7 +86,7 @@ + // <&rp1_dma RP1_DMA_UART1_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; +@@ -103,7 +103,7 @@ + // <&rp1_dma RP1_DMA_UART2_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; +@@ -120,7 +120,7 @@ + // <&rp1_dma RP1_DMA_UART3_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; +@@ -137,7 +137,7 @@ + // <&rp1_dma RP1_DMA_UART4_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; +@@ -154,7 +154,7 @@ + // <&rp1_dma RP1_DMA_UART5_RX>; + // dma-names = "tx", "rx"; + pinctrl-names = "default"; +- arm,primecell-periphid = <0x00541011>; ++ arm,primecell-periphid = <0x00341011>; + uart-has-rtscts; + cts-event-workaround; + skip-init; diff --git a/target/linux/bcm27xx/patches-6.6/950-1501-ASoC-pcm512x-Demote-No-SCLK-to-debug-level.patch b/target/linux/bcm27xx/patches-6.6/950-1501-ASoC-pcm512x-Demote-No-SCLK-to-debug-level.patch new file mode 100644 index 000000000..02857ce6d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1501-ASoC-pcm512x-Demote-No-SCLK-to-debug-level.patch @@ -0,0 +1,25 @@ +From 4f7341263ebd6ab2ae805c8f27191d24abc05a62 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 8 Jan 2025 15:48:35 +0000 +Subject: [PATCH] ASoC: pcm512x: Demote "No SCLK" to debug level + +Designing a PCM512X-based soundcard with no external SCLK is a valid +choice supported by the driver. Don't alarm users with messages that +say "No SCLK, using BCLK: -2" - reclassify them as debug information. + +Signed-off-by: Phil Elwell +--- + sound/soc/codecs/pcm512x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/soc/codecs/pcm512x.c ++++ b/sound/soc/codecs/pcm512x.c +@@ -630,7 +630,7 @@ static int pcm512x_dai_startup_slave(str + struct regmap *regmap = pcm512x->regmap; + + if (IS_ERR(pcm512x->sclk)) { +- dev_info(dev, "No SCLK, using BCLK: %ld\n", ++ dev_dbg(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1502-ASoC-allo-piano-dac-plus-Fix-volume-limiting.patch b/target/linux/bcm27xx/patches-6.6/950-1502-ASoC-allo-piano-dac-plus-Fix-volume-limiting.patch new file mode 100644 index 000000000..f08083463 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1502-ASoC-allo-piano-dac-plus-Fix-volume-limiting.patch @@ -0,0 +1,58 @@ +From 4111f4ede92b1f5bf869f87c66fc39151999c42f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 8 Jan 2025 15:46:30 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Fix volume limiting + +Controls which only exist when snd_soc_register_card returns can't be +modified before then. Move the setting of volume limits to just before +the end of the probe function. + +Link: https://github.com/raspberrypi/linux/issues/6527 + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -734,18 +734,6 @@ static int snd_allo_piano_dac_init(struc + if (digital_gain_0db_limit) { + int ret; + +- ret = snd_soc_limit_volume(card, "Master Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set master volume limit: %d\n", +- ret); +- +- ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", +- 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n", +- ret); +- + //Set volume limit on both dacs + for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { + char cname[256]; +@@ -1000,6 +988,20 @@ static int snd_allo_piano_dac_probe(stru + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); + ++ if (digital_gain_0db_limit) { ++ ret = snd_soc_limit_volume(card, "Master Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set master volume limit: %d\n", ++ ret); ++ ++ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume", ++ 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n", ++ ret); ++ } ++ + if ((mute_gpio[0]) && (mute_gpio[1])) + snd_allo_piano_gpio_mute(&snd_allo_piano_dac); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1503-ASoC-allo-piano-dac-plus-Remove-pointless-code.patch b/target/linux/bcm27xx/patches-6.6/950-1503-ASoC-allo-piano-dac-plus-Remove-pointless-code.patch new file mode 100644 index 000000000..bb5c21d42 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1503-ASoC-allo-piano-dac-plus-Remove-pointless-code.patch @@ -0,0 +1,53 @@ +From 62c33972e3eb724d80179fb71b05e923920f0b0d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 8 Jan 2025 16:05:02 +0000 +Subject: [PATCH] ASoC: allo-piano-dac-plus: Remove pointless code + +The codec control Digital Playback Volume is one of the controls deleted +by the allo-piano-dac-plus driver. It is effectively replaced by the +soundcard controls Master Playback Volume and Subwoofer Playback Volume. + +Delete the code that sets the volume limit on those codec controls - the +limits on the soundcard volume controls are sufficient. + +Signed-off-by: Phil Elwell +--- + sound/soc/bcm/allo-piano-dac-plus.c | 20 +------------------- + 1 file changed, 1 insertion(+), 19 deletions(-) + +--- a/sound/soc/bcm/allo-piano-dac-plus.c ++++ b/sound/soc/bcm/allo-piano-dac-plus.c +@@ -731,21 +731,6 @@ static int snd_allo_piano_dac_init(struc + + mutex_init(&glb_ptr->lock); + +- if (digital_gain_0db_limit) { +- int ret; +- +- //Set volume limit on both dacs +- for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { +- char cname[256]; +- +- sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]); +- ret = snd_soc_limit_volume(card, cname, 207); +- if (ret < 0) +- dev_warn(card->dev, "Failed to set %s volume limit: %d\n", +- cname, ret); +- } +- } +- + // Remove codec controls + for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) { + for (j = 0; j < ARRAY_SIZE(codec_ctl_name); j++) { +@@ -753,10 +738,7 @@ static int snd_allo_piano_dac_init(struc + + sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[j]); + kctl = snd_soc_card_get_kcontrol(card, cname); +- if (!kctl) { +- dev_err(rtd->card->dev, "Control %s not found\n", +- cname); +- } else { ++ if (kctl) { + kctl->vd[0].access = + SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_ctl_remove(card->snd_card, kctl); diff --git a/target/linux/bcm27xx/patches-6.6/950-1505-DT-bcm2712-override-supports-cqe-to-a-cell.patch b/target/linux/bcm27xx/patches-6.6/950-1505-DT-bcm2712-override-supports-cqe-to-a-cell.patch new file mode 100644 index 000000000..c95c1d432 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1505-DT-bcm2712-override-supports-cqe-to-a-cell.patch @@ -0,0 +1,90 @@ +From 301420a8cc44ee457670e015adc3a08cfc4531a9 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 8 Jan 2025 14:58:37 +0000 +Subject: [PATCH] DT: bcm2712: override supports-cqe to a cell + +We want to be able to control the interop surface exposed by Command +Queueing across bcm2712 products to a more restrictive default, with +selectable disable and permissive behaviour. + +Changing the bool to a cell lets it relay a tristate value. + +Also add the override parameter to CM5 as CM5-lite may interface with +arbitrary eMMC or SD cards. + +Signed-off-by: Jonathan Bell +--- + arch/arm/boot/dts/overlays/README | 11 ++++++++--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 4 ++-- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 3 ++- + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2 +- + 4 files changed, 13 insertions(+), 7 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -405,9 +405,14 @@ Params: + non-lite SKU of CM4). + (default "on") + +- sd_cqe Set to "off" to disable Command Queueing if you +- have an incompatible Class A2 SD card +- (Pi 5 only, default "on") ++ sd_cqe Modify Command Queuing behaviour on the main SD ++ interface. Legal values are: ++ 0: disable CQ ++ 1: allow CQ for known-good SD A2 cards, and all ++ eMMC cards ++ 2: allow CQ for all SD A2 cards that aren't ++ known-bad, and all eMMC cards. ++ (2712 only, default "1") + + 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,7 +365,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g + sd-uhs-sdr50; + sd-uhs-ddr50; + sd-uhs-sdr104; +- supports-cqe; ++ supports-cqe = <1>; + cd-gpios = <&gio_aon 5 GPIO_ACTIVE_LOW>; + //no-1-8-v; + status = "okay"; +@@ -745,6 +745,6 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + + / { + __overrides__ { +- sd_cqe = <&sdio1>, "supports-cqe?"; ++ sd_cqe = <&sdio1>, "supports-cqe:0"; + }; + }; +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -339,7 +339,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g + mmc-hs400-1_8v; + mmc-hs400-enhanced-strobe; + broken-cd; +- supports-cqe; ++ supports-cqe = <1>; + status = "okay"; + }; + +@@ -752,5 +752,6 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + <&ant2>, "output-low?=on"; + noanthogs = <&ant1>,"status=disabled", + <&ant2>, "status=disabled"; ++ sd_cqe = <&sdio1>, "supports-cqe:0"; + }; + }; +--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +@@ -1210,7 +1210,7 @@ + clocks = <&clk_emmc2>; + sdhci-caps-mask = <0x0000C000 0x0>; + sdhci-caps = <0x0 0x0>; +- supports-cqe; ++ supports-cqe = <1>; + mmc-ddr-3_3v; + status = "disabled"; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1506-mmc-sd-filter-card-CQ-support-based-on-an-allow-list.patch b/target/linux/bcm27xx/patches-6.6/950-1506-mmc-sd-filter-card-CQ-support-based-on-an-allow-list.patch new file mode 100644 index 000000000..e5148707b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1506-mmc-sd-filter-card-CQ-support-based-on-an-allow-list.patch @@ -0,0 +1,57 @@ +From d70a60eb03ae4fc687b91b5f6c4684000be21c5f Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 8 Jan 2025 15:09:53 +0000 +Subject: [PATCH] mmc: sd: filter card CQ support based on an allow-list + +We have found that many SD cards in the field, even of the same make and +model, have latent bugs in their CQ implementation. Some product lines +have fewer bugs with newer manufacture dates, but this is not a +guarantee that a particular card is at a particular firmware revision +level. + +Many of these bugs lead to card hangs or data corruption. Add a quirk to +mark a card as having a tested, working CQ implementation and ignore the +capability if absent. + +Signed-off-by: Jonathan Bell +--- + drivers/mmc/core/card.h | 5 +++++ + drivers/mmc/core/sd.c | 4 ++++ + include/linux/mmc/card.h | 1 + + 3 files changed, 10 insertions(+) + +--- a/drivers/mmc/core/card.h ++++ b/drivers/mmc/core/card.h +@@ -292,4 +292,9 @@ static inline int mmc_card_broken_sd_pow + return c->quirks & MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY; + } + ++static inline int mmc_card_working_sd_cq(const struct mmc_card *c) ++{ ++ return c->quirks & MMC_QUIRK_WORKING_SD_CQ; ++} ++ + #endif +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -1506,6 +1506,10 @@ cont: + goto free_card; + } + ++ /* Disallow command queueing on unvetted cards */ ++ if (!mmc_card_working_sd_cq(card)) ++ card->ext_csd.cmdq_support = false; ++ + /* Enable command queueing if supported */ + if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) { + /* +--- a/include/linux/mmc/card.h ++++ b/include/linux/mmc/card.h +@@ -297,6 +297,7 @@ struct mmc_card { + #define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */ + #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ + #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ ++#define MMC_QUIRK_WORKING_SD_CQ (1<<30) /* SD card has known-good CQ implementation */ + #define MMC_QUIRK_ERASE_BROKEN (1<<31) /* Skip erase */ + + bool written_flag; /* Indicates eMMC has been written since power on */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1507-mmc-set-MMC_QUIRK_KNOWN_WORKING_SD_CQ-on-Raspberry-P.patch b/target/linux/bcm27xx/patches-6.6/950-1507-mmc-set-MMC_QUIRK_KNOWN_WORKING_SD_CQ-on-Raspberry-P.patch new file mode 100644 index 000000000..0dd1ffae7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1507-mmc-set-MMC_QUIRK_KNOWN_WORKING_SD_CQ-on-Raspberry-P.patch @@ -0,0 +1,41 @@ +From eb4d8ffb2b007963662be7eca88baf0e5c358bd6 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 8 Jan 2025 15:18:33 +0000 +Subject: [PATCH] mmc: set MMC_QUIRK_KNOWN_WORKING_SD_CQ on Raspberry Pi class + A2 cards + +These cards have a known-good CQ implementation and are based on a +Longsys product. Add the MANFID for Longsys SD, and the particular CID +details for the Pi card. + +Signed-off-by: Jonathan Bell +--- + drivers/mmc/core/card.h | 1 + + drivers/mmc/core/quirks.h | 6 ++++++ + 2 files changed, 7 insertions(+) + +--- a/drivers/mmc/core/card.h ++++ b/drivers/mmc/core/card.h +@@ -90,6 +90,7 @@ struct mmc_fixup { + #define CID_MANFID_KINGSTON 0x70 + #define CID_MANFID_HYNIX 0x90 + #define CID_MANFID_KINGSTON_SD 0x9F ++#define CID_MANFID_LONGSYS_SD 0xAD + #define CID_MANFID_NUMONYX 0xFE + + #define END_FIXUP { NULL } +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -66,6 +66,12 @@ 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), + ++ /* SD A2 allow-list - only trust CQ on these cards */ ++ /* Raspberry Pi A2 cards */ ++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_LONGSYS_SD, 0x4c53, CID_YEAR_ANY, CID_MONTH_ANY, ++ cid_rev(1, 0, 0, 0), -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, ++ MMC_QUIRK_WORKING_SD_CQ, EXT_CSD_REV_ANY), ++ + END_FIXUP + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1508-mmc-use-downstream-DT-property-to-modify-CQE-and-or-.patch b/target/linux/bcm27xx/patches-6.6/950-1508-mmc-use-downstream-DT-property-to-modify-CQE-and-or-.patch new file mode 100644 index 000000000..2139eef8b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1508-mmc-use-downstream-DT-property-to-modify-CQE-and-or-.patch @@ -0,0 +1,94 @@ +From e72f42ebc9a236c023f8027a37c9351d58e28b05 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +Date: Wed, 8 Jan 2025 16:02:27 +0000 +Subject: [PATCH] mmc: use downstream DT property to modify CQE and/or SD CQ + behaviour + +Implement a tristate-style option for "supports-cqe". If the property is +absent or zero, disable CQ completely. For 1, enable CQ unconditionally +for eMMC cards, and known-good SD cards. For 2, enable for eMMC cards, +and all SD cards that are not known-bad. + +The sdhci-brcmstb driver needs to know about the tristate as its probe +sequence would otherwise override a disable in mmc_of_parse(). + +Signed-off-by: Jonathan Bell +--- + drivers/mmc/core/host.c | 11 ++++++++++- + drivers/mmc/core/sd.c | 4 ++-- + drivers/mmc/host/sdhci-brcmstb.c | 6 ++++-- + include/linux/mmc/host.h | 1 + + 4 files changed, 17 insertions(+), 5 deletions(-) + +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -275,7 +275,7 @@ EXPORT_SYMBOL(mmc_of_parse_clk_phase); + int mmc_of_parse(struct mmc_host *host) + { + struct device *dev = host->parent; +- u32 bus_width, drv_type, cd_debounce_delay_ms; ++ u32 bus_width, drv_type, cd_debounce_delay_ms, cq_allow; + int ret; + + if (!dev || !dev_fwnode(dev)) +@@ -410,6 +410,15 @@ int mmc_of_parse(struct mmc_host *host) + host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V | + MMC_CAP2_HS400_ES); + ++ cq_allow = 0; ++ /* ++ * Downstream property - if a u32 and 2 instead of a bool, ++ * trust most A2 SD cards claiming CQ support. ++ */ ++ device_property_read_u32(dev, "supports-cqe", &cq_allow); ++ if (cq_allow == 2) ++ host->caps2 |= MMC_CAP2_SD_CQE_PERMISSIVE; ++ + /* Must be after "non-removable" check */ + if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) { + if (host->caps & MMC_CAP_NONREMOVABLE) +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -1506,8 +1506,8 @@ cont: + goto free_card; + } + +- /* Disallow command queueing on unvetted cards */ +- if (!mmc_card_working_sd_cq(card)) ++ /* Disallow command queueing on unvetted cards unless overridden */ ++ if (!(host->caps2 & MMC_CAP2_SD_CQE_PERMISSIVE) && !mmc_card_working_sd_cq(card)) + card->ext_csd.cmdq_support = false; + + /* Enable command queueing if supported */ +--- a/drivers/mmc/host/sdhci-brcmstb.c ++++ b/drivers/mmc/host/sdhci-brcmstb.c +@@ -511,7 +511,7 @@ static int sdhci_brcmstb_probe(struct pl + struct sdhci_pltfm_host *pltfm_host; + const struct of_device_id *match; + struct sdhci_brcmstb_priv *priv; +- u32 actual_clock_mhz; ++ u32 actual_clock_mhz, cqe; + struct sdhci_host *host; + struct resource *iomem; + bool no_pinctrl = false; +@@ -540,7 +540,9 @@ static int sdhci_brcmstb_probe(struct pl + pltfm_host->clk = clk; + + priv = sdhci_pltfm_priv(pltfm_host); +- if (device_property_read_bool(&pdev->dev, "supports-cqe")) { ++ cqe = 0; ++ device_property_read_u32(&pdev->dev, "supports-cqe", &cqe); ++ if (cqe > 0) { + priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; + match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; + } +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -427,6 +427,7 @@ struct mmc_host { + #define MMC_CAP2_CRYPTO 0 + #endif + #define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */ ++#define MMC_CAP2_SD_CQE_PERMISSIVE (1 << 31) /* Ignore allow-list for CQ capable SD card detection */ + + int fixed_drv_type; /* fixed driver type for non-removable media */ + diff --git a/target/linux/bcm27xx/patches-6.6/950-1509-misc-rp1-pio-Handle-probe-errors.patch b/target/linux/bcm27xx/patches-6.6/950-1509-misc-rp1-pio-Handle-probe-errors.patch new file mode 100644 index 000000000..0aca41f74 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1509-misc-rp1-pio-Handle-probe-errors.patch @@ -0,0 +1,38 @@ +From e56aa0bd2b552daa4349a7eb2e6b0dec81d3e5cc Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 9 Jan 2025 16:40:25 +0000 +Subject: [PATCH] misc: rp1-pio: Handle probe errors + +Ensure that rp1_pio_open fails if the device failed to probe. + +Link: https://github.com/raspberrypi/linux/issues/6593 + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1014,6 +1014,9 @@ struct rp1_pio_client *rp1_pio_open(void + { + struct rp1_pio_client *client; + ++ if (!g_pio) ++ return ERR_PTR(-ENOENT); ++ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); +@@ -1265,9 +1268,8 @@ static int rp1_pio_probe(struct platform + return dev_err_probe(dev, pdev->id, "alias is missing\n"); + + fw = devm_rp1_firmware_get(dev, dev->of_node); +- if (IS_ERR(fw)) +- return PTR_ERR(fw); +- ++ if (IS_ERR_OR_NULL(fw)) ++ return dev_err_probe(dev, -ENOENT, "failed to contact RP1 firmware\n"); + ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); + if (ret < 0) + return ret; diff --git a/target/linux/bcm27xx/patches-6.6/950-1510-firmware-rp1-Simplify-rp1_firmware_get.patch b/target/linux/bcm27xx/patches-6.6/950-1510-firmware-rp1-Simplify-rp1_firmware_get.patch new file mode 100644 index 000000000..71a64da95 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1510-firmware-rp1-Simplify-rp1_firmware_get.patch @@ -0,0 +1,84 @@ +From dafde0ac8b6d4b21578a677c8afad8714af47aaf Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 9 Jan 2025 16:33:37 +0000 +Subject: [PATCH] firmware: rp1: Simplify rp1_firmware_get + +Simplify the implementation of rp1_firmware_get, requiring its clients +to have a valid 'firmware' property. Also make it return NULL on error. + +Link: https://github.com/raspberrypi/linux/issues/6593 + +Signed-off-by: Phil Elwell +--- + drivers/firmware/rp1.c | 36 +++++++++++++++--------------------- + 1 file changed, 15 insertions(+), 21 deletions(-) + +--- a/drivers/firmware/rp1.c ++++ b/drivers/firmware/rp1.c +@@ -159,42 +159,36 @@ struct rp1_firmware *rp1_firmware_get(st + struct device_node *fwnode; + struct rp1_firmware *fw; + +- if (client) { +- fwnode = of_parse_phandle(client, "firmware", 0); +- if (!fwnode) +- fwnode = of_get_parent(client); +- if (fwnode && !of_device_is_compatible(fwnode, match)) { +- of_node_put(fwnode); +- fwnode = NULL; +- } +- } +- +- if (!fwnode) +- fwnode = of_find_matching_node(NULL, rp1_firmware_of_match); +- ++ if (!client) ++ return NULL; ++ fwnode = of_parse_phandle(client, "firmware", 0); + if (!fwnode) +- return ERR_PTR(-ENOENT); ++ return NULL; ++ if (!of_device_is_compatible(fwnode, match)) { ++ of_node_put(fwnode); ++ return NULL; ++ } + + pdev = of_find_device_by_node(fwnode); + of_node_put(fwnode); + + if (!pdev) +- return ERR_PTR(-EPROBE_DEFER); ++ goto err_exit; + + fw = platform_get_drvdata(pdev); + if (!fw) +- goto err_defer; ++ goto err_exit; + + if (!kref_get_unless_zero(&fw->consumers)) +- goto err_defer; ++ goto err_exit; + + put_device(&pdev->dev); + + return fw; + +-err_defer: ++err_exit: + put_device(&pdev->dev); +- return ERR_PTR(-EPROBE_DEFER); ++ return NULL; + } + EXPORT_SYMBOL_GPL(rp1_firmware_get); + +@@ -210,8 +204,8 @@ struct rp1_firmware *devm_rp1_firmware_g + int ret; + + fw = rp1_firmware_get(client); +- if (IS_ERR(fw)) +- return fw; ++ if (!fw) ++ return NULL; + + ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); + if (ret) diff --git a/target/linux/bcm27xx/patches-6.6/950-1512-dts-bcm2711-Don-t-mark-timer-regs-unconfigured.patch b/target/linux/bcm27xx/patches-6.6/950-1512-dts-bcm2711-Don-t-mark-timer-regs-unconfigured.patch new file mode 100644 index 000000000..168dd19f1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1512-dts-bcm2711-Don-t-mark-timer-regs-unconfigured.patch @@ -0,0 +1,33 @@ +From d06cb3534b6553a1f76bef2ddaf833e23dc12a4c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 10 Jan 2025 15:45:11 +0000 +Subject: [PATCH] dts: bcm2711: Don't mark timer regs unconfigured + +The DT property arm,cpu-registers-not-fw-configured tells the kernel +that the ARM architectural timer has not been configured by the +firmware. This prevents the use of a vDSO - a faster alternative to a +syscall for some common kernel operations. + +However, on Pi 4 the firmware does configure the timer, so this property +is unnecessary. Delete it. + +Also remove it from the bcm2712.dtsi where it should never have been, +since it is only relevant to 32-bit ARM kernels. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/broadcom/bcm2711.dtsi | 2 -- + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2 -- + 2 files changed, 4 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +@@ -741,8 +741,6 @@ + IRQ_TYPE_LEVEL_LOW)>, + ; +- /* This only applies to the ARMv7 stub */ +- arm,cpu-registers-not-fw-configured; + }; + + cpus: cpus { diff --git a/target/linux/bcm27xx/patches-6.6/950-1513-mmc-bcm2835-sdhost-Observe-SWIOTLB-memory-limit.patch b/target/linux/bcm27xx/patches-6.6/950-1513-mmc-bcm2835-sdhost-Observe-SWIOTLB-memory-limit.patch new file mode 100644 index 000000000..08a58f2ed --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1513-mmc-bcm2835-sdhost-Observe-SWIOTLB-memory-limit.patch @@ -0,0 +1,29 @@ +From dfff38316c1284c30c68d02cc424bad0562cf253 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 10 Jan 2025 16:33:13 +0000 +Subject: [PATCH] mmc: bcm2835-sdhost Observe SWIOTLB memory limit + +Make sure the sdhost driver doesn't use requests bigger than SWIOTLB +can handle. + +Copied from [1]. + +Link: https://github.com/raspberrypi/linux/issues/6589 +Signed-off-by: Phil Elwell +[1] d4dd9bccf485 ("mmc: bcm2835: Take SWIOTLB memory size limitation +into account") +--- + drivers/mmc/host/bcm2835-sdhost.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mmc/host/bcm2835-sdhost.c ++++ b/drivers/mmc/host/bcm2835-sdhost.c +@@ -1971,7 +1971,7 @@ int bcm2835_sdhost_add_host(struct platf + } + + mmc->max_segs = 128; +- mmc->max_req_size = 524288; ++ mmc->max_req_size = min_t(size_t, 524288, dma_max_mapping_size(&pdev->dev)); + mmc->max_seg_size = mmc->max_req_size; + mmc->max_blk_size = 512; + mmc->max_blk_count = 65535; diff --git a/target/linux/bcm27xx/patches-6.6/950-1514-drivers-media-pisp_be-Add-support-for-YUV422-planar-.patch b/target/linux/bcm27xx/patches-6.6/950-1514-drivers-media-pisp_be-Add-support-for-YUV422-planar-.patch new file mode 100644 index 000000000..554c9307c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1514-drivers-media-pisp_be-Add-support-for-YUV422-planar-.patch @@ -0,0 +1,31 @@ +From a73ecafc5532e31b184220149cc2863f625700bf Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Wed, 15 Jan 2025 09:46:25 +0000 +Subject: [PATCH] drivers: media: pisp_be: Add support for YUV422 planar format + +List V4L2_PIX_FMT_YUV422P as supported by the PiSP backend hardware. + +Signed-off-by: Naushir Patuck +--- + .../platform/raspberrypi/pisp_be/pisp_be_formats.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h ++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h +@@ -129,6 +129,16 @@ static const struct pisp_be_format suppo + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, ++ { ++ .fourcc = V4L2_PIX_FMT_YUV422P, ++ /* 128 alignment to ensure U/V planes are 64 byte aligned. */ ++ .align = 128, ++ .bit_depth = 8, ++ .plane_factor = { P3(1), P3(0.5), P3(0.5) }, ++ .num_planes = 1, ++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, ++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M, ++ }, + /* Multiplane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420M, diff --git a/target/linux/bcm27xx/patches-6.6/950-1515-drivers-media-pisp_be-Remove-unused-fields-in-struct.patch b/target/linux/bcm27xx/patches-6.6/950-1515-drivers-media-pisp_be-Remove-unused-fields-in-struct.patch new file mode 100644 index 000000000..51b8f7e7e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1515-drivers-media-pisp_be-Remove-unused-fields-in-struct.patch @@ -0,0 +1,82 @@ +From a452251cc286f2799969f047698c76fe3d7862b9 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Thu, 16 Jan 2025 10:13:57 +0000 +Subject: [PATCH] drivers: media: pisp_be: Remove unused fields in struct + pisp_be_config + +These fields should not be set by either the user or the kernel driver +so remove them. Replace them with padding bytes to maintain backward +compatibility with existing userland applications. + +Signed-off-by: Naushir Patuck +--- + .../linux/media/raspberrypi/pisp_be_config.h | 42 ++----------------- + 1 file changed, 4 insertions(+), 38 deletions(-) + +--- a/include/uapi/linux/media/raspberrypi/pisp_be_config.h ++++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h +@@ -716,13 +716,6 @@ struct pisp_be_hog_buffer_config { + /** + * struct pisp_be_config - RaspberryPi PiSP Back End Processing configuration + * +- * @input_buffer: Input buffer addresses +- * @tdn_input_buffer: TDN input buffer addresses +- * @stitch_input_buffer: Stitch input buffer addresses +- * @tdn_output_buffer: TDN output buffer addresses +- * @stitch_output_buffer: Stitch output buffer addresses +- * @output_buffer: Output buffers addresses +- * @hog_buffer: HOG buffer addresses + * @global: Global PiSP configuration + * @input_format: Input image format + * @decompress: Decompress configuration +@@ -761,28 +754,10 @@ struct pisp_be_hog_buffer_config { + * @output_format: Output format configuration + * @hog: HOG configuration + * @axi: AXI bus configuration +- * @lsc_extra: LSC extra info +- * @cac_extra: CAC extra info +- * @downscale_extra: Downscaler extra info +- * @resample_extra: Resample extra info +- * @crop: Crop configuration +- * @hog_format: HOG format info +- * @dirty_flags_bayer: Bayer enable dirty flags +- * (:c:type:`pisp_be_bayer_enable`) +- * @dirty_flags_rgb: RGB enable dirty flags +- * (:c:type:`pisp_be_rgb_enable`) +- * @dirty_flags_extra: Extra dirty flags + */ + struct pisp_be_config { +- /* I/O configuration: */ +- struct pisp_be_input_buffer_config input_buffer; +- struct pisp_be_tdn_input_buffer_config tdn_input_buffer; +- struct pisp_be_stitch_input_buffer_config stitch_input_buffer; +- struct pisp_be_tdn_output_buffer_config tdn_output_buffer; +- struct pisp_be_stitch_output_buffer_config stitch_output_buffer; +- struct pisp_be_output_buffer_config +- output_buffer[PISP_BACK_END_NUM_OUTPUTS]; +- struct pisp_be_hog_buffer_config hog_buffer; ++ /* For backward compatibility */ ++ uint8_t pad0[112]; + /* Processing configuration: */ + struct pisp_be_global_config global; + struct pisp_image_format_config input_format; +@@ -823,17 +798,8 @@ struct pisp_be_config { + output_format[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_hog_config hog; + struct pisp_be_axi_config axi; +- /* Non-register fields: */ +- struct pisp_be_lsc_extra lsc_extra; +- struct pisp_be_cac_extra cac_extra; +- struct pisp_be_downscale_extra +- downscale_extra[PISP_BACK_END_NUM_OUTPUTS]; +- struct pisp_be_resample_extra resample_extra[PISP_BACK_END_NUM_OUTPUTS]; +- struct pisp_be_crop_config crop; +- struct pisp_image_format_config hog_format; +- __u32 dirty_flags_bayer; /* these use pisp_be_bayer_enable */ +- __u32 dirty_flags_rgb; /* use pisp_be_rgb_enable */ +- __u32 dirty_flags_extra; /* these use pisp_be_dirty_t */ ++ /* For backward compatibility */ ++ uint8_t pad1[84]; + } __attribute__((packed)); + + /** diff --git a/target/linux/bcm27xx/patches-6.6/950-1516-media-imx219-Adjust-PLL-settings-based-on-the-number.patch b/target/linux/bcm27xx/patches-6.6/950-1516-media-imx219-Adjust-PLL-settings-based-on-the-number.patch new file mode 100644 index 000000000..227ada434 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1516-media-imx219-Adjust-PLL-settings-based-on-the-number.patch @@ -0,0 +1,163 @@ +From 5b958efc20d381ee103103df5df0c88dc02ada18 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 16 Jan 2025 11:08:25 +0000 +Subject: [PATCH] media: imx219: Adjust PLL settings based on the number of + MIPI lanes + +Commit ceddfd4493b3 ("media: i2c: imx219: Support four-lane operation") +added support for device tree to allow configuration of the sensor to +use 4 lanes with a link frequency of 363MHz, and amended the advertised +pixel rate to 280.8MPix/s. + +However it didn't change any of the PLL settings, so actually it would +have been running effectively overclocked in the MIPI block, and with +the frame rate and exposure calculations being wrong. + +The pixel rate and link frequency advertised were taken from the "Clock +Setting Example" section of the datasheet. However those are based on an +external clock of 12MHz, and are unachievable with a clock of 24MHz (it +seems PREPLLCLK_VT_DIV and PREPLLCK_OP_DIV can ONLY be set via the +automatic configuration doumented in "9-1-2 EXCK_FREQ setting depend on +INCK frequency). + +Dropping all support for the 363MHz link frequency would cause problems +for existing users, so allow it from device tree, but log a warning that +the requested value is not being truly applied. + +Fixes: ceddfd4493b3 ("media: i2c: imx219: Support four-lane operation") +Co-developed-by: Peyton Howe +Signed-off-by: Peyton Howe +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx219.c | 83 +++++++++++++++++++++++++++++--------- + 1 file changed, 64 insertions(+), 19 deletions(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -148,10 +148,11 @@ + + /* Pixel rate is fixed for all the modes */ + #define IMX219_PIXEL_RATE 182400000 +-#define IMX219_PIXEL_RATE_4LANE 280800000 ++#define IMX219_PIXEL_RATE_4LANE 281600000 + + #define IMX219_DEFAULT_LINK_FREQ 456000000 +-#define IMX219_DEFAULT_LINK_FREQ_4LANE 363000000 ++#define IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED 363000000 ++#define IMX219_DEFAULT_LINK_FREQ_4LANE 364000000 + + /* IMX219 native and active pixel array size. */ + #define IMX219_NATIVE_WIDTH 3296U +@@ -224,15 +225,6 @@ static const struct cci_reg_sequence imx + { CCI_REG8(0x30eb), 0x05 }, + { CCI_REG8(0x30eb), 0x09 }, + +- /* PLL Clock Table */ +- { IMX219_REG_VTPXCK_DIV, 5 }, +- { IMX219_REG_VTSYCK_DIV, 1 }, +- { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ +- { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ +- { IMX219_REG_PLL_VT_MPY, 57 }, +- { IMX219_REG_OPSYCK_DIV, 1 }, +- { IMX219_REG_PLL_OP_MPY, 114 }, +- + /* Undocumented registers */ + { CCI_REG8(0x455e), 0x00 }, + { CCI_REG8(0x471e), 0x4b }, +@@ -316,6 +308,34 @@ static const struct cci_reg_sequence raw + { IMX219_REG_OPPXCK_DIV, 10 }, + }; + ++static const struct cci_reg_sequence imx219_2lane_regs[] = { ++ /* PLL Clock Table */ ++ { IMX219_REG_VTPXCK_DIV, 5 }, ++ { IMX219_REG_VTSYCK_DIV, 1 }, ++ { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PLL_VT_MPY, 57 }, ++ { IMX219_REG_OPSYCK_DIV, 1 }, ++ { IMX219_REG_PLL_OP_MPY, 114 }, ++ ++ /* 2-Lane CSI Mode */ ++ { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_2_LANE_MODE }, ++}; ++ ++static const struct cci_reg_sequence imx219_4lane_regs[] = { ++ /* PLL Clock Table */ ++ { IMX219_REG_VTPXCK_DIV, 5 }, ++ { IMX219_REG_VTSYCK_DIV, 1 }, ++ { IMX219_REG_PREPLLCK_VT_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PREPLLCK_OP_DIV, 3 }, /* 0x03 = AUTO set */ ++ { IMX219_REG_PLL_VT_MPY, 88 }, ++ { IMX219_REG_OPSYCK_DIV, 1 }, ++ { IMX219_REG_PLL_OP_MPY, 91 }, ++ ++ /* 4-Lane CSI Mode */ ++ { IMX219_REG_CSI_LANE_MODE, IMX219_CSI_4_LANE_MODE }, ++}; ++ + static const s64 imx219_link_freq_menu[] = { + IMX219_DEFAULT_LINK_FREQ, + }; +@@ -941,9 +961,11 @@ static int imx219_get_selection(struct v + + static int imx219_configure_lanes(struct imx219 *imx219) + { +- return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE, +- imx219->lanes == 2 ? IMX219_CSI_2_LANE_MODE : +- IMX219_CSI_4_LANE_MODE, NULL); ++ /* Write the appropriate PLL settings for the number of MIPI lanes */ ++ return cci_multi_reg_write(imx219->regmap, ++ imx219->lanes == 2 ? imx219_2lane_regs : imx219_4lane_regs, ++ imx219->lanes == 2 ? ARRAY_SIZE(imx219_2lane_regs) : ++ ARRAY_SIZE(imx219_4lane_regs), NULL); + }; + + static int imx219_start_streaming(struct imx219 *imx219, +@@ -1334,6 +1356,7 @@ static int imx219_check_hwcfg(struct dev + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; ++ bool link_frequency_valid = false; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { +@@ -1360,11 +1383,33 @@ static int imx219_check_hwcfg(struct dev + goto error_out; + } + +- if (ep_cfg.nr_of_link_frequencies != 1 || +- (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ? +- IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) { +- dev_err(dev, "Link frequency not supported: %lld\n", +- ep_cfg.link_frequencies[0]); ++ if (ep_cfg.nr_of_link_frequencies == 1) { ++ switch (imx219->lanes) { ++ case 2: ++ if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ) ++ link_frequency_valid = true; ++ break; ++ case 4: ++ if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ_4LANE) ++ link_frequency_valid = true; ++ else if (ep_cfg.link_frequencies[0] == ++ IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED) { ++ dev_warn(dev, "Link frequency of %d not supported, but has been incorrectly advertised previously\n", ++ IMX219_DEFAULT_LINK_FREQ_4LANE_UNSUPPORTED); ++ dev_warn(dev, "Using link frequency of %d\n", ++ IMX219_DEFAULT_LINK_FREQ_4LANE); ++ link_frequency_valid = true; ++ } ++ break; ++ } ++ } ++ ++ if (!link_frequency_valid) { ++ dev_err_probe(dev, -EINVAL, ++ "Link frequency not supported: %lld\n", ++ ep_cfg.link_frequencies[0]); + goto error_out; + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1517-IMX219-Add-4-lane-option-to-the-device-tree-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1517-IMX219-Add-4-lane-option-to-the-device-tree-overlay.patch new file mode 100644 index 000000000..2872f1842 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1517-IMX219-Add-4-lane-option-to-the-device-tree-overlay.patch @@ -0,0 +1,55 @@ +From e3297f3fbffdaec8076c00167261f504bb2c64b6 Mon Sep 17 00:00:00 2001 +From: Peyton Howe +Date: Sat, 4 Jan 2025 15:15:33 -0500 +Subject: [PATCH] IMX219: Add 4-lane option to the device tree overlay + +Signed-off-by: Peyton Howe +--- + arch/arm/boot/dts/overlays/README | 2 ++ + arch/arm/boot/dts/overlays/imx219-overlay.dts | 17 +++++++++++++++++ + 2 files changed, 19 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2757,6 +2757,8 @@ Params: rotation Mounting + cam0 Adopt the default configuration for CAM0 on a + Compute Module (CSI0, i2c_vc, and cam0_reg). + vcm Configure a VCM focus drive on the sensor. ++ 4lane Enable 4 CSI2 lanes. This requires a Compute ++ Module (1, 3, 4, or 5) or Pi 5. + + + Name: imx258 +--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts ++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts +@@ -65,6 +65,22 @@ + }; + }; + ++ fragment@201 { ++ target = <&csi_ep>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ ++ fragment@202 { ++ target = <&cam_endpoint>; ++ __dormant__ { ++ data-lanes = <1 2 3 4>; ++ link-frequencies = ++ /bits/ 64 <363000000>; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; +@@ -77,6 +93,7 @@ + <&vcm>, "VANA-supply:0=", <&cam0_reg>; + vcm = <&vcm>, "status=okay", + <&cam_node>,"lens-focus:0=", <&vcm>; ++ 4lane = <0>, "+201+202"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1518-dtoverlays-waveshare-panel-Disable-new-touch-control.patch b/target/linux/bcm27xx/patches-6.6/950-1518-dtoverlays-waveshare-panel-Disable-new-touch-control.patch new file mode 100644 index 000000000..61534982d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1518-dtoverlays-waveshare-panel-Disable-new-touch-control.patch @@ -0,0 +1,40 @@ +From 11381ac246576bc84dfc28f6cdd8030305c605aa Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 20 Jan 2025 11:53:04 +0000 +Subject: [PATCH] dtoverlays: waveshare-panel: Disable new touch controller by + default + +Commit e442e5c1ab6b ("arch:arm:boot:dts:overlays: Added waveshare 13.3inch +panel support") added an extra touch controller for the new panels. +On systems with old panels, it ends up spamming the kernel log as that +touch controller isn't there to respond. + +Fixes: e442e5c1ab6b ("arch:arm:boot:dts:overlays: Added waveshare 13.3inch panel support") +Signed-off-by: Dave Stevenson +--- + .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts +@@ -55,6 +55,7 @@ + touch2: ilitek@41 { + compatible = "ilitek,ili251x"; + reg = <0x41>; ++ status = "disabled"; + }; + }; + }; +@@ -125,8 +126,10 @@ + <&touch>, "touchscreen-inverted-x?", + <&touch>, "touchscreen-inverted-y?"; + 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel"; +- 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel"; +- 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel"; ++ 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel", ++ <&touch2>, "status=okay"; ++ 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel", ++ <&touch2>, "status=okay"; + i2c1 = <&i2c_frag>, "target:0=",<&i2c1>, + <0>, "-3-4+5"; + disable_touch = <&touch>, "status=disabled"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1519-drm-rp1-rp1-dpi-Add-rgb_order-property-to-match-VC4-.patch b/target/linux/bcm27xx/patches-6.6/950-1519-drm-rp1-rp1-dpi-Add-rgb_order-property-to-match-VC4-.patch new file mode 100644 index 000000000..4892c271f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1519-drm-rp1-rp1-dpi-Add-rgb_order-property-to-match-VC4-.patch @@ -0,0 +1,325 @@ +From ba9883e3b667e4f1fbeacc4346f6e9179a3a5479 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +Date: Mon, 20 Jan 2025 11:20:48 +0000 +Subject: [PATCH] drm: rp1: rp1-dpi: Add "rgb_order" property (to match VC4 + DPI) + +As on VC4, the OF property overrides the order implied by media +bus format. Only 4 of the 6 possible orders are supported. New +add-on hardware designs should not rely on this "legacy" feature. + +Signed-off-by: Nick Hollinghurst +--- + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 15 ++ + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 8 + + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 191 ++++++++++++++--------- + 3 files changed, 143 insertions(+), 71 deletions(-) + +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c +@@ -292,6 +292,7 @@ static int rp1dpi_platform_probe(struct + struct device *dev = &pdev->dev; + struct rp1_dpi *dpi; + struct drm_bridge *bridge = NULL; ++ const char *rgb_order = NULL; + struct drm_panel *panel; + int i, j, ret; + +@@ -353,6 +354,20 @@ static int rp1dpi_platform_probe(struct + if (ret) + goto done_err; + ++ dpi->rgb_order_override = RP1DPI_ORDER_UNCHANGED; ++ if (!of_property_read_string(dev->of_node, "rgb_order", &rgb_order)) { ++ if (!strcmp(rgb_order, "rgb")) ++ dpi->rgb_order_override = RP1DPI_ORDER_RGB; ++ else if (!strcmp(rgb_order, "bgr")) ++ dpi->rgb_order_override = RP1DPI_ORDER_BGR; ++ else if (!strcmp(rgb_order, "grb")) ++ dpi->rgb_order_override = RP1DPI_ORDER_GRB; ++ else if (!strcmp(rgb_order, "brg")) ++ dpi->rgb_order_override = RP1DPI_ORDER_BRG; ++ else ++ DRM_ERROR("Invalid dpi order %s - ignored\n", rgb_order); ++ } ++ + /* Check if PIO can snoop on or override DPI's GPIO1 */ + dpi->gpio1_used = false; + for (i = 0; !dpi->gpio1_used; i++) { +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h +@@ -25,6 +25,13 @@ + #define RP1DPI_CLK_PLLCORE 2 + #define RP1DPI_NUM_CLOCKS 3 + ++/* Codes (in LE byte order) used for S/W permutation */ ++#define RP1DPI_ORDER_UNCHANGED 0 ++#define RP1DPI_ORDER_RGB 0x020100 ++#define RP1DPI_ORDER_BGR 0x000102 ++#define RP1DPI_ORDER_GRB 0x020001 ++#define RP1DPI_ORDER_BRG 0x010002 ++ + /* ---------------------------------------------------------------------- */ + + struct rp1_dpi { +@@ -45,6 +52,7 @@ struct rp1_dpi { + u32 bus_fmt; + bool de_inv, clk_inv; + bool dpi_running, pipe_enabled; ++ unsigned int rgb_order_override; + struct completion finished; + + /* Experimental stuff for interlace follows */ +--- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c ++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c +@@ -223,12 +223,90 @@ int rp1dpi_hw_busy(struct rp1_dpi *dpi) + return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0; + } + +-/* Table of supported input (in-memory/DMA) pixel formats. */ ++/* ++ * Table of supported input (in-memory/DMA) pixel formats. ++ * ++ * RP1 DPI describes RGB components in terms of their MS bit position, a 10-bit ++ * left-aligned bit-mask, and an optional right-shift-and-OR used for scaling. ++ * To make it easier to permute R, G and B components, we re-pack these fields ++ * into 32-bit code-words, which don't themselves correspond to any register. ++ */ ++ ++#define RGB_CODE(scale, shift, mask) (((scale) << 24) | ((shift) << 16) | (mask)) ++#define RGB_SCALE(c) ((c) >> 24) ++#define RGB_SHIFT(c) (((c) >> 16) & 31) ++#define RGB_MASK(c) ((c) & 0x3ff) ++ + struct rp1dpi_ipixfmt { +- u32 format; /* DRM format code */ +- u32 mask; /* RGB masks (10 bits each, left justified) */ +- u32 shift; /* RGB MSB positions in the memory word */ +- u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */ ++ u32 format; /* DRM format code */ ++ u32 rgb_code[3]; /* (width&7), MS bit position, 10-bit mask */ ++ u32 bpp; /* Bytes per pixel minus one */ ++}; ++ ++static const struct rp1dpi_ipixfmt my_formats[] = { ++ { ++ .format = DRM_FORMAT_XRGB8888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 3, ++ }, ++ { ++ .format = DRM_FORMAT_XBGR8888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 3, ++ }, ++ { ++ .format = DRM_FORMAT_ARGB8888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 3, ++ }, ++ { ++ .format = DRM_FORMAT_ABGR8888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 3, ++ }, ++ { ++ .format = DRM_FORMAT_RGB888, ++ .rgb_code = { ++ RGB_CODE(0, 23, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 7, 0x3fc), ++ }, ++ .bpp = 2, ++ }, ++ { ++ .format = DRM_FORMAT_BGR888, ++ .rgb_code = { ++ RGB_CODE(0, 7, 0x3fc), ++ RGB_CODE(0, 15, 0x3fc), ++ RGB_CODE(0, 23, 0x3fc), ++ }, ++ .bpp = 2, ++ }, ++ { ++ .format = DRM_FORMAT_RGB565, ++ .rgb_code = { ++ RGB_CODE(5, 15, 0x3e0), ++ RGB_CODE(6, 10, 0x3f0), ++ RGB_CODE(5, 4, 0x3e0), ++ }, ++ .bpp = 1, ++ }, + }; + + #define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \ +@@ -244,63 +322,13 @@ struct rp1dpi_ipixfmt { + FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \ + FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b)) + +-static const struct rp1dpi_ipixfmt my_formats[] = { +- { +- .format = DRM_FORMAT_XRGB8888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), +- }, +- { +- .format = DRM_FORMAT_XBGR8888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), +- }, +- { +- .format = DRM_FORMAT_ARGB8888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), +- }, +- { +- .format = DRM_FORMAT_ABGR8888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), +- }, +- { +- .format = DRM_FORMAT_RGB888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(23, 15, 7), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), +- }, +- { +- .format = DRM_FORMAT_BGR888, +- .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), +- .shift = ISHIFT_RGB(7, 15, 23), +- .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), +- }, +- { +- .format = DRM_FORMAT_RGB565, +- .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), +- .shift = ISHIFT_RGB(15, 10, 4), +- .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), +- }, +- { +- .format = DRM_FORMAT_BGR565, +- .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), +- .shift = ISHIFT_RGB(4, 10, 15), +- .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | +- FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), +- } +-}; +- ++/* ++ * Function to update *shift with output positions, and return output RGB masks. ++ * By the time we get here, RGB order has been normalized to RGB (R most significant). ++ * Note that an internal bus is 30 bits wide: bits [21:20], [11:10], [1:0] are dropped. ++ * This makes the packed RGB5656 and RGB666 formats problematic, as colour components ++ * need to straddle the gaps; we mitigate this by hijacking input masks and scaling. ++ */ + static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz) + { + switch (bus_format) { +@@ -308,6 +336,7 @@ static u32 set_output_format(u32 bus_for + if (*shift == ISHIFT_RGB(15, 10, 4)) { + /* When framebuffer is RGB565, we can output RGB565 */ + *shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0); ++ *imask = IMASK_RGB(0x3fc, 0x3fc, 0); + *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK; + return OMASK_RGB(0x3fc, 0x3fc, 0); + } +@@ -322,7 +351,7 @@ static u32 set_output_format(u32 bus_for + case MEDIA_BUS_FMT_BGR666_1X18: + /* due to a HW limitation, bit-depth is effectively RGB444 */ + *shift |= OSHIFT_RGB(23, 15, 7); +- *imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0); ++ *imask = IMASK_RGB(0x3c0, 0x3c0, 0x3c0); + *rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK); + return OMASK_RGB(0x330, 0x3c0, 0x3c0); + +@@ -359,7 +388,8 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi + struct drm_display_mode const *mode) + { + u32 shift, imask, omask, rgbsz, vctrl; +- int i; ++ u32 rgb_code[3]; ++ int order, i; + + drm_info(&dpi->drm, + "in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d%s %dkHz %cH%cV%cD%cC", +@@ -373,26 +403,45 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi + de_inv ? '-' : '+', + dpi->clk_inv ? '-' : '+'); + +- /* +- * Configure all DPI/DMA block registers, except base address. +- * DMA will not actually start until a FB base address is specified +- * using rp1dpi_hw_update(). +- */ ++ /* Look up the input (in-memory) pixel format */ + for (i = 0; i < ARRAY_SIZE(my_formats); ++i) { + if (my_formats[i].format == in_format) + break; + } + if (i >= ARRAY_SIZE(my_formats)) { + pr_err("%s: bad input format\n", __func__); +- i = 4; ++ i = ARRAY_SIZE(my_formats) - 1; + } +- if (BUS_FMT_IS_BGR(bus_format)) +- i ^= 1; +- shift = my_formats[i].shift; +- imask = my_formats[i].mask; +- rgbsz = my_formats[i].rgbsz; ++ ++ /* ++ * Although these RGB orderings refer to the output (DPI bus) format, ++ * here we permute the *input* components. After this point, "Red" ++ * will be most significant (highest numbered GPIOs), regardless ++ * of rgb_order or bus_format. This simplifies later workarounds. ++ */ ++ order = dpi->rgb_order_override; ++ if (order == RP1DPI_ORDER_UNCHANGED) ++ order = BUS_FMT_IS_BGR(bus_format) ? RP1DPI_ORDER_BGR : RP1DPI_ORDER_RGB; ++ rgb_code[0] = my_formats[i].rgb_code[order & 3]; ++ rgb_code[1] = my_formats[i].rgb_code[(order >> 8) & 3]; ++ rgb_code[2] = my_formats[i].rgb_code[(order >> 16) & 3]; ++ rgbsz = FIELD_PREP(DPI_DMA_RGBSZ_BPP_MASK, my_formats[i].bpp) | ++ FIELD_PREP(DPI_DMA_RGBSZ_R_MASK, RGB_SCALE(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_RGBSZ_G_MASK, RGB_SCALE(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_RGBSZ_B_MASK, RGB_SCALE(rgb_code[2])); ++ shift = FIELD_PREP(DPI_DMA_SHIFT_IR_MASK, RGB_SHIFT(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_SHIFT_IG_MASK, RGB_SHIFT(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_SHIFT_IB_MASK, RGB_SHIFT(rgb_code[2])); ++ imask = FIELD_PREP(DPI_DMA_IMASK_R_MASK, RGB_MASK(rgb_code[0])) | ++ FIELD_PREP(DPI_DMA_IMASK_G_MASK, RGB_MASK(rgb_code[1])) | ++ FIELD_PREP(DPI_DMA_IMASK_B_MASK, RGB_MASK(rgb_code[2])); + omask = set_output_format(bus_format, &shift, &imask, &rgbsz); + ++ /* ++ * Configure all DPI/DMA block registers, except base address. ++ * DMA will not actually start until a FB base address is specified ++ * using rp1dpi_hw_update(). ++ */ + rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask); + rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask); + rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift); diff --git a/target/linux/bcm27xx/patches-6.6/950-1522-add-ina238-to-i2c-sensors.patch b/target/linux/bcm27xx/patches-6.6/950-1522-add-ina238-to-i2c-sensors.patch new file mode 100644 index 000000000..cc0f31d08 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1522-add-ina238-to-i2c-sensors.patch @@ -0,0 +1,102 @@ +From 94de19285cc9bd781394bf1aa4fb8d401aac5195 Mon Sep 17 00:00:00 2001 +From: James Sarrett +Date: Sun, 26 Jan 2025 11:29:31 -0800 +Subject: [PATCH] add ina238 to i2c-sensors + +This patch adds the ina238 device tree parameters to the i2c-sensors +overlay. The ina238 driver needs 2 configuration parameters, shut_resistor +and ti,shunt-gain in addition to it's address, so they are added as well. +--- + arch/arm/boot/dts/overlays/README | 13 ++++++++++ + .../boot/dts/overlays/i2c-sensor-common.dtsi | 26 ++++++++++++++++++- + 2 files changed, 38 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2486,11 +2486,20 @@ Params: addr Set the + ds1621 Select the Dallas Semiconductors DS1621 temp + sensor. Valid addresses 0x48-0x4f, default 0x48 + ++ gain Gain used for measuring shunt resistor current. ++ Valid values 1 or 4, default 1. (ina238 only, ++ disabled by default) ++ + hdc100x Select the Texas Instruments HDC100x temp sensor + Valid addresses 0x40-0x43, default 0x40 + + htu21 Select the HTU21 temperature and humidity sensor + ++ ina238 Select the TI INA238 power monitor. Valid ++ addresses 0x40-0x4F, default 0x40. ++ Uses parameters shunt-resistor and ++ ti,shunt-gain for configuration ++ + int_pin Set the GPIO to use for interrupts (max30102, + mpu6050 and mpu9250 only) + +@@ -2549,6 +2558,10 @@ Params: addr Set the + reset_pin GPIO to be used to reset the device (bno055 + only, disabled by default) + ++ shunt_resistor Value of shunt resistor used for current ++ measurement in uOhms. (ina238 only, disabled ++ by default) ++ + sht3x Select the Sensirion SHT3x temperature and + humidity sensors. Valid addresses 0x44-0x45, + default 0x44 +--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi +@@ -526,6 +526,27 @@ + }; + }; + ++ fragment@35 { ++ target = <&i2cbus>; ++ __dormant__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ ina238: ina238@48 { ++ compatible = "ti,ina238"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x40>; ++ /* uOhms, uint32_t */ ++ shunt-resistor = <1000>; ++ /* 1 or 4, (±40.96 mV or ±163.84 mV) */ ++ ti,shunt-gain = <1>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ + fragment@99 { + target = <&gpio>; + __dormant__ { +@@ -573,6 +594,7 @@ + bno055 = <0>,"+31"; + sht4x = <0>,"+32"; + adt7410 = <0>,"+34"; ++ ina238 = <0>,"+35"; + + addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0", + <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0", +@@ -582,7 +604,7 @@ + <&ms5837>,"reg:0", <&ms8607>,"reg:0", + <&mpu6050>,"reg:0", <&mpu9250>,"reg:0", + <&bno055>,"reg:0", <&sht4x>,"reg:0", +- <&bmp380>,"reg:0", <&adt7410>,"reg:0"; ++ <&bmp380>,"reg:0", <&adt7410>,"reg:0", <&ina238>,"reg:0"; + int_pin = <&int_pins>, "brcm,pins:0", + <&int_pins>, "reg:0", + <&max30102>, "interrupts:0", +@@ -590,5 +612,7 @@ + <&mpu9250>, "interrupts:0"; + no_timeout = <&jc42>, "smbus-timeout-disable?"; + reset_pin = <&bno055>,"reset-gpios:4", <0>,"+30"; ++ shunt_resistor = <&ina238>,"shunt-resistor:0"; ++ gain = <&ina238>,"ti,shunt-gain:0"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1524-add-shtc3-to-i2c-sensors.patch b/target/linux/bcm27xx/patches-6.6/950-1524-add-shtc3-to-i2c-sensors.patch new file mode 100644 index 000000000..575d52241 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1524-add-shtc3-to-i2c-sensors.patch @@ -0,0 +1,57 @@ +From aabe16b3cdc6172618803ed7a6002d1099d306e5 Mon Sep 17 00:00:00 2001 +From: James Sarrett +Date: Sun, 26 Jan 2025 22:07:02 -0800 +Subject: [PATCH] add shtc3 to i2c-sensors + +This patch adds the shtc3 device tree parameters to the i2c-sensors +overlay. The shtc3 driver needs no other configuration parameters, as the +i2c address is permanently baked in to the silicon. +--- + arch/arm/boot/dts/overlays/README | 3 +++ + .../arm/boot/dts/overlays/i2c-sensor-common.dtsi | 16 ++++++++++++++++ + 2 files changed, 19 insertions(+) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -2570,6 +2570,9 @@ Params: addr Set the + humidity sensors. Valid addresses 0x44-0x45, + default 0x44 + ++ shtc3 Select the Sensirion SHTC3 temperature and ++ humidity sensors. ++ + si7020 Select the Silicon Labs Si7013/20/21 humidity/ + temperature sensor + +--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi +@@ -547,6 +547,21 @@ + }; + }; + ++ fragment@36 { ++ target = <&i2cbus>; ++ __dormant__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ shtc3: shtc3@70 { ++ compatible = "sensirion,shtc3"; ++ reg = <0x70>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ + fragment@99 { + target = <&gpio>; + __dormant__ { +@@ -595,6 +610,7 @@ + sht4x = <0>,"+32"; + adt7410 = <0>,"+34"; + ina238 = <0>,"+35"; ++ shtc3 = <0>,"+36"; + + addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0", + <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0", diff --git a/target/linux/bcm27xx/patches-6.6/950-1525-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch b/target/linux/bcm27xx/patches-6.6/950-1525-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch new file mode 100644 index 000000000..72d73089e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1525-drivers-media-pci-Update-Hailo-accelerator-device-dr.patch @@ -0,0 +1,2137 @@ +From a18d9ced4965462cb7b3b4252ada440395105308 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Thu, 23 Jan 2025 14:14:47 +0000 +Subject: [PATCH] drivers: media: pci: Update Hailo accelerator device driver + to v4.20 + +Sourced from https://github.com/hailo-ai/hailort-drivers + +Signed-off-by: Naushir Patuck +--- + .../media/pci/hailo/common/fw_validation.c | 4 +- + .../media/pci/hailo/common/fw_validation.h | 14 +- + .../pci/hailo/common/hailo_ioctl_common.h | 10 +- + drivers/media/pci/hailo/common/pcie_common.c | 175 +++-- + drivers/media/pci/hailo/common/pcie_common.h | 69 +- + drivers/media/pci/hailo/common/vdma_common.c | 70 +- + drivers/media/pci/hailo/common/vdma_common.h | 27 +- + drivers/media/pci/hailo/src/fops.c | 90 ++- + drivers/media/pci/hailo/src/nnc.c | 4 +- + drivers/media/pci/hailo/src/pcie.c | 630 ++++++++++++++++-- + drivers/media/pci/hailo/src/pcie.h | 39 +- + drivers/media/pci/hailo/src/soc.c | 18 +- + drivers/media/pci/hailo/utils/compact.h | 8 + + drivers/media/pci/hailo/vdma/memory.c | 86 ++- + drivers/media/pci/hailo/vdma/memory.h | 2 + + 15 files changed, 997 insertions(+), 249 deletions(-) + +--- a/drivers/media/pci/hailo/common/fw_validation.c ++++ b/drivers/media/pci/hailo/common/fw_validation.c +@@ -41,8 +41,8 @@ int FW_VALIDATION__validate_fw_header(ui + 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; ++ case HAILO_BOARD_TYPE_HAILO15L: ++ expected_firmware_magic = FIRMWARE_HEADER_MAGIC_HAILO15L; + break; + default: + err = -EINVAL; +--- a/drivers/media/pci/hailo/common/fw_validation.h ++++ b/drivers/media/pci/hailo/common/fw_validation.h +@@ -9,15 +9,9 @@ + #include "hailo_ioctl_common.h" + #include + +-#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0) +-#define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB) +-#define FIRMWARE_HEADER_MAGIC_PLUTO (0xF94739AB) +- +-#ifndef HAILO_EMULATOR +-#define FIRMWARE_WAIT_TIMEOUT_MS (5000) +-#else /* ifndef HAILO_EMULATOR */ +-#define FIRMWARE_WAIT_TIMEOUT_MS (500000) +-#endif /* ifndef HAILO_EMULATOR */ ++#define FIRMWARE_HEADER_MAGIC_HAILO8 (0x1DD89DE0) ++#define FIRMWARE_HEADER_MAGIC_HAILO15 (0xE905DAAB) ++#define FIRMWARE_HEADER_MAGIC_HAILO15L (0xF94739AB) + + typedef enum { + FIRMWARE_HEADER_VERSION_INITIAL = 0, +@@ -61,4 +55,4 @@ int FW_VALIDATION__validate_fw_header(ui + int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address, + size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert); + +-#endif +\ No newline at end of file ++#endif +--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h ++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h +@@ -7,7 +7,7 @@ + #define _HAILO_IOCTL_COMMON_H_ + + #define HAILO_DRV_VER_MAJOR 4 +-#define HAILO_DRV_VER_MINOR 19 ++#define HAILO_DRV_VER_MINOR 20 + #define HAILO_DRV_VER_REVISION 0 + + #define _STRINGIFY_EXPANDED( x ) #x +@@ -22,6 +22,7 @@ + #define MAX_VDMA_ENGINES (3) + #define SIZE_OF_VDMA_DESCRIPTOR (16) + #define VDMA_DEST_CHANNELS_START (16) ++#define MAX_SG_DESCS_COUNT (64 * 1024u) + + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128) + #define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1) +@@ -38,6 +39,10 @@ + #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) ++// HRT-15790 TODO: separate nnc interrupts and soc interrupts ++#define FW_ACCESS_SOFT_RESET_SHIFT (3) ++#define FW_ACCESS_SOFT_RESET_MASK (1 << FW_ACCESS_SOFT_RESET_SHIFT) ++ + #define FW_ACCESS_SOC_CONTROL_SHIFT (3) + #define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT) + +@@ -184,7 +189,6 @@ enum hailo_dma_data_direction { + }; + + // 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, +@@ -399,7 +403,7 @@ struct hailo_d2h_notification { + enum hailo_board_type { + HAILO_BOARD_TYPE_HAILO8 = 0, + HAILO_BOARD_TYPE_HAILO15, +- HAILO_BOARD_TYPE_PLUTO, ++ HAILO_BOARD_TYPE_HAILO15L, + HAILO_BOARD_TYPE_HAILO10H, + HAILO_BOARD_TYPE_HAILO10H_LEGACY, + HAILO_BOARD_TYPE_COUNT, +--- a/drivers/media/pci/hailo/common/pcie_common.c ++++ b/drivers/media/pci/hailo/common/pcie_common.c +@@ -30,6 +30,8 @@ + + #define ATR0_PCIE_BRIDGE_OFFSET (0x700) + ++#define ATR_PCIE_BRIDGE_OFFSET(atr_index) (ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20)) ++ + #define MAXIMUM_APP_FIRMWARE_CODE_SIZE (0x40000) + #define MAXIMUM_CORE_FIRMWARE_CODE_SIZE (0x20000) + +@@ -40,17 +42,21 @@ + + #define PCIE_CONFIG_VENDOR_OFFSET (0x0098) + +-#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) + + #define HAILO_PCIE_MAX_ATR_TABLE_INDEX (3) + +-#define MAX_FILES_PER_STAGE (4) +- + #define BOOT_STATUS_UNINITIALIZED (0x1) + ++#define PCIE_CONTROL_SECTION_ADDRESS_H8 (0x60000000) ++#define PCIE_BLOCK_ADDRESS_ATR1 (0x200000) ++ ++#define PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_offset) \ ++ (reg_offset) = (((reg_offset) & ~0x00000004L) | ((uint32_t)(1) << 2)) ++ ++ + struct hailo_fw_addresses { + u32 boot_fw_header; + u32 app_fw_code_ram_base; +@@ -58,19 +64,14 @@ struct hailo_fw_addresses { + u32 boot_cont_cert; + u32 core_code_ram_base; + u32 core_fw_header; +- u32 atr0_trsl_addr1; + u32 raise_ready_offset; + u32 boot_status; +-}; +- +-struct loading_stage { +- const struct hailo_file_batch *batch; +- u32 trigger_address; ++ u32 pcie_cfg_regs; + }; + + struct hailo_board_compatibility { + struct hailo_fw_addresses fw_addresses; +- const struct loading_stage stages[MAX_LOADING_STAGES]; ++ const struct hailo_pcie_loading_stage stages[MAX_LOADING_STAGES]; + }; + + static const struct hailo_file_batch hailo10h_files_stg1[] = { +@@ -134,13 +135,18 @@ static const struct hailo_file_batch hai + .has_core = false + }, + { +- .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz", ++ .filename = "hailo/hailo10h/image-fs", ++#ifndef HAILO_EMULATOR + .address = 0x88000000, ++#else ++ // TODO : HRT-15692 - merge two cases ++ .address = 0x89000000, ++#endif /* ifndef HAILO_EMULATOR */ + .max_size = 0x20000000, // Max size 512MB + .is_mandatory = true, + .has_header = false, + .has_core = false +- }, ++ } + }; + + // If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb) +@@ -173,7 +179,7 @@ static const struct hailo_file_batch hai + + static const struct hailo_file_batch hailo8_files_stg1[] = { + { +- .filename = "hailo/hailo8_fw.4.19.0.bin", ++ .filename = "hailo/hailo8_fw.bin", + .address = 0x20000, + .max_size = 0x50000, + .is_mandatory = true, +@@ -225,9 +231,10 @@ static const struct hailo_file_batch hai + } + }; + +-static const struct hailo_file_batch pluto_files_stg1[] = { ++// TODO HRT-15014 - Fix names for hailo15l legacy accelerator ++static const struct hailo_file_batch hailo15l_files_stg1[] = { + { +- .filename = "hailo/pluto_fw.bin", ++ .filename = "hailo/hailo15l_fw.bin", + .address = 0x20000, + .max_size = 0x100000, + .is_mandatory = true, +@@ -253,14 +260,15 @@ static const struct hailo_board_compatib + .app_fw_code_ram_base = 0x60000, + .core_code_ram_base = 0xC0000, + .core_fw_header = 0xA0000, +- .atr0_trsl_addr1 = 0x60000000, + .raise_ready_offset = 0x1684, + .boot_status = 0xe0000, + }, + .stages = { + { + .batch = hailo8_files_stg1, +- .trigger_address = 0xE0980 ++ .trigger_address = 0xE0980, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 3 + }, + }, + }, +@@ -272,14 +280,15 @@ static const struct hailo_board_compatib + .app_fw_code_ram_base = 0x20000, + .core_code_ram_base = 0x60000, + .core_fw_header = 0xC0000, +- .atr0_trsl_addr1 = 0x000BE000, + .raise_ready_offset = 0x1754, + .boot_status = 0x80000, + }, + .stages = { + { + .batch = hailo10h_legacy_files_stg1, +- .trigger_address = 0x88c98 ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 1 + }, + }, + }, +@@ -291,28 +300,34 @@ static const struct hailo_board_compatib + .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, ++ .pcie_cfg_regs = 0x002009dc, + }, + .stages = { + { + .batch = hailo10h_files_stg1, +- .trigger_address = 0x88c98 ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 3 + }, + { + .batch = hailo10h_files_stg2, +- .trigger_address = 0x84000000 ++ .trigger_address = 0x84000000, ++ .timeout = PCI_EP_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 4 + }, + { + .batch = hailo10h_files_stg2_linux_in_emmc, +- .trigger_address = 0x84000000 ++ .trigger_address = 0x84000000, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 2 + }, + }, + }, + // 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] = { ++ [HAILO_BOARD_TYPE_HAILO15L] = { + .fw_addresses = { + .boot_fw_header = 0x88000, + .boot_key_cert = 0x88018, +@@ -320,44 +335,54 @@ static const struct hailo_board_compatib + .app_fw_code_ram_base = 0x20000, + .core_code_ram_base = 0x60000, + .core_fw_header = 0xC0000, +- .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, + }, + .stages = { + { +- .batch = pluto_files_stg1, +- .trigger_address = 0x88c98 ++ .batch = hailo15l_files_stg1, ++ .trigger_address = 0x88c98, ++ .timeout = FIRMWARE_WAIT_TIMEOUT_MS, ++ .amount_of_files_in_stage = 1 + }, + }, + } + }; + ++const struct hailo_pcie_loading_stage *hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, ++ enum loading_stages stage) ++{ ++ return &compat[board_type].stages[stage]; ++} ++ ++static u32 read_and_clear_reg(struct hailo_resource *resource, u32 offset) ++{ ++ u32 value = hailo_resource_read32(resource, offset); ++ if (value != 0) { ++ hailo_resource_write32(resource, offset, value); ++ } ++ return value; ++} + + bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source) + { +- u32 channel_data_source = 0; +- u32 channel_data_dest = 0; ++ u32 istatus_host = 0; + memset(source, 0, sizeof(*source)); + +- source->interrupt_bitmask = hailo_resource_read32(&resources->config, BCS_ISTATUS_HOST); +- if (0 == source->interrupt_bitmask) { ++ istatus_host = read_and_clear_reg(&resources->config, BCS_ISTATUS_HOST); ++ if (0 == istatus_host) { + return false; + } + +- // clear signal +- hailo_resource_write32(&resources->config, BCS_ISTATUS_HOST, source->interrupt_bitmask); ++ source->sw_interrupts = (istatus_host >> BCS_ISTATUS_HOST_SW_IRQ_SHIFT); + +- if (source->interrupt_bitmask & BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK) { +- channel_data_source = hailo_resource_read32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL); +- hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, channel_data_source); +- } +- if (source->interrupt_bitmask & BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK) { +- channel_data_dest = hailo_resource_read32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL); +- hailo_resource_write32(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL, channel_data_dest); ++ if (istatus_host & BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK) { ++ source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL); ++ } ++ if (istatus_host & BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK) { ++ source->vdma_channels_bitmap |= read_and_clear_reg(&resources->config, BCS_DESTINATION_INTERRUPT_PER_CHANNEL); + } +- source->vdma_channels_bitmap = channel_data_source | channel_data_dest; + + return true; + } +@@ -419,6 +444,15 @@ void hailo_pcie_write_firmware_driver_sh + hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); + } + ++void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources) ++{ ++ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses); ++ const u32 fw_access_value = FW_ACCESS_SOFT_RESET_MASK; ++ ++ // Write shutdown flag to FW ++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value); ++} ++ + int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index) + { + size_t offset = 0; +@@ -431,7 +465,7 @@ int hailo_pcie_configure_atr_table(struc + }; + + BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); +- offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20); ++ offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); + + return hailo_resource_write_buffer(bridge_config, offset, sizeof(atr), (void*)&atr); + } +@@ -441,7 +475,7 @@ void hailo_pcie_read_atr_table(struct ha + size_t offset = 0; + + BUG_ON(HAILO_PCIE_MAX_ATR_TABLE_INDEX < atr_index); +- offset = ATR0_PCIE_BRIDGE_OFFSET + (atr_index * 0x20); ++ offset = ATR_PCIE_BRIDGE_OFFSET(atr_index); + + hailo_resource_read_buffer(bridge_config, offset, sizeof(*atr), (void*)atr); + } +@@ -510,7 +544,7 @@ static void read_memory(struct hailo_pci + hailo_pcie_read_atr_table(&resources->config, &previous_atr, ATR_INDEX); + + if (base_address != src) { +- // Data is not aligned, write the first chunk ++ // Data is not aligned, read the first chunk + chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len); + read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len); + offset += chunk_len; +@@ -526,6 +560,18 @@ static void read_memory(struct hailo_pci + (((u64)(previous_atr.atr_trsl_addr_2) << 32) | previous_atr.atr_trsl_addr_1), ATR_INDEX); + } + ++// Note: This function use for enabling the vDMA transaction host<->device by read modify write of the EP registers in the SOC - for fast boot over vDMA. ++void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources) ++{ ++ u32 reg_routing_mercury = 0; ++ ++ BUG_ON(compat[resources->board_type].fw_addresses.pcie_cfg_regs == 0); ++ ++ read_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); ++ PCIE_CONFIG_PCIE_CFG_QM_ROUTING_MODE_SET(reg_routing_mercury); ++ write_memory(resources, compat[resources->board_type].fw_addresses.pcie_cfg_regs, ®_routing_mercury, sizeof(reg_routing_mercury)); ++} ++ + static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header, + secure_boot_certificate_header_t *fw_cert) + { +@@ -551,11 +597,11 @@ static void hailo_write_core_firmware(st + write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t)); + } + +-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address) ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage) + { + u32 pcie_finished = 1; + +- write_memory(resources, address, (void*)&pcie_finished, sizeof(pcie_finished)); ++ write_memory(resources, compat[resources->board_type].stages[stage].trigger_address, (void*)&pcie_finished, sizeof(pcie_finished)); + } + + u32 hailo_get_boot_status(struct hailo_pcie_resources *resources) +@@ -673,16 +719,14 @@ static int write_single_file(struct hail + + 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; ++ const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); ++ const struct hailo_file_batch *files_batch = stage_info->batch; ++ const u8 amount_of_files = stage_info->amount_of_files_in_stage; + int file_index = 0; + int err = 0; + +- for (file_index = 0; file_index < MAX_FILES_PER_STAGE; file_index++) ++ for (file_index = 0; file_index < amount_of_files; 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); +@@ -696,31 +740,29 @@ int hailo_pcie_write_firmware_batch(stru + dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename); + } + +- hailo_trigger_firmware_boot(resources, compat[resources->board_type].stages[stage].trigger_address); ++ hailo_trigger_firmware_boot(resources, stage); + + return 0; + } + +-// TODO: HRT-14147 - remove this function +-static 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; + u32 atr_value; + +- // TODO: HRT-14147 +- if (HAILO_BOARD_TYPE_HAILO10H == resources->board_type) { +- return !hailo_pcie_is_device_ready_for_boot(resources); ++ if (HAILO_BOARD_TYPE_HAILO8 == resources->board_type) { ++ offset = ATR_PCIE_BRIDGE_OFFSET(0) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); ++ atr_value = hailo_resource_read32(&resources->config, offset); ++ ++ return (PCIE_CONTROL_SECTION_ADDRESS_H8 == atr_value); + } ++ else { ++ offset = ATR_PCIE_BRIDGE_OFFSET(1) + offsetof(struct hailo_atr_config, atr_trsl_addr_1); ++ atr_value = hailo_resource_read32(&resources->config, offset); + +- offset = ATR0_PCIE_BRIDGE_OFFSET + offsetof(struct hailo_atr_config, atr_trsl_addr_1); +- atr_value = hailo_resource_read32(&resources->config, offset); ++ return (PCIE_BLOCK_ADDRESS_ATR1 == atr_value); ++ } + +- return atr_value == compat[resources->board_type].fw_addresses.atr0_trsl_addr1; + } + + bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources) +@@ -764,8 +806,7 @@ 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 | BCS_ISTATUS_SOC_CONNECT_ACCEPTED | BCS_ISTATUS_SOC_CLOSED_IRQ); ++ mask |= BCS_ISTATUS_HOST_SW_IRQ_MASK; + hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask); + } + +@@ -822,7 +863,7 @@ int hailo_set_device_type(struct hailo_p + switch(resources->board_type) { + case HAILO_BOARD_TYPE_HAILO8: + case HAILO_BOARD_TYPE_HAILO10H_LEGACY: +- case HAILO_BOARD_TYPE_PLUTO: ++ case HAILO_BOARD_TYPE_HAILO15L: + resources->accelerator_type = HAILO_ACCELERATOR_TYPE_NNC; + break; + case HAILO_BOARD_TYPE_HAILO10H: +--- a/drivers/media/pci/hailo/common/pcie_common.h ++++ b/drivers/media/pci/hailo/common/pcie_common.h +@@ -18,11 +18,8 @@ + #include + + +-#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_SOC_CLOSED_IRQ (0x20000000) ++#define BCS_ISTATUS_HOST_SW_IRQ_MASK (0xFF000000) ++#define BCS_ISTATUS_HOST_SW_IRQ_SHIFT (24) + #define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF) + #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00) + +@@ -36,13 +33,19 @@ + #define HAILO_PCIE_FW_ACCESS_BAR (4) + + #define HAILO_PCIE_DMA_ENGINES_COUNT (1) ++#define PCI_VDMA_ENGINE_INDEX (0) ++ ++#define MAX_FILES_PER_STAGE (4) ++ ++#define HAILO_PCIE_HOST_DMA_DATA_ID (0) ++#define HAILO_PCI_EP_HOST_DMA_DATA_ID (6) + + #define DRIVER_NAME "hailo" + + #define PCI_VENDOR_ID_HAILO 0x1e60 + #define PCI_DEVICE_ID_HAILO_HAILO8 0x2864 +-#define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4 +-#define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2 ++#define PCI_DEVICE_ID_HAILO_HAILO10H 0x45C4 ++#define PCI_DEVICE_ID_HAILO_HAILO15L 0x43a2 + + typedef u64 hailo_ptr_t; + +@@ -69,18 +72,24 @@ enum loading_stages { + MAX_LOADING_STAGES = 3 + }; + +-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, +- SOC_CLOSED_IRQ = BCS_ISTATUS_SOC_CLOSED_IRQ, +- VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK, +- VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK ++enum hailo_pcie_nnc_sw_interrupt_masks { ++ HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ = 0x2, ++ HAILO_PCIE_NNC_FW_CONTROL_IRQ = 0x4, ++ HAILO_PCIE_NNC_DRIVER_DOWN_IRQ = 0x8, ++}; ++ ++enum hailo_pcie_soc_sw_interrupt_masks { ++ HAILO_PCIE_SOC_CONTROL_IRQ = 0x10, ++ HAILO_PCIE_SOC_CLOSE_IRQ = 0x20, ++}; ++ ++enum hailo_pcie_boot_interrupt_masks { ++ HAILO_PCIE_BOOT_SOFT_RESET_IRQ = 0x1, ++ HAILO_PCIE_BOOT_IRQ = 0x2, + }; + + struct hailo_pcie_interrupt_source { +- u32 interrupt_bitmask; ++ u32 sw_interrupts; + u32 vdma_channels_bitmap; + }; + +@@ -93,6 +102,13 @@ struct hailo_file_batch { + bool has_core; + }; + ++struct hailo_pcie_loading_stage { ++ const struct hailo_file_batch *batch; ++ u32 trigger_address; ++ u32 timeout; ++ u8 amount_of_files_in_stage; ++}; ++ + // TODO: HRT-6144 - Align Windows/Linux to QNX + #ifdef __QNX__ + enum hailo_bar_index { +@@ -117,8 +133,23 @@ enum hailo_bar_index { + extern "C" { + #endif + ++ ++#ifndef HAILO_EMULATOR ++#define TIME_UNTIL_REACH_BOOTLOADER (10) ++#define PCI_EP_WAIT_TIMEOUT_MS (40000) ++#define FIRMWARE_WAIT_TIMEOUT_MS (5000) ++#else /* ifndef HAILO_EMULATOR */ ++// PCI EP timeout is defined to 50000000 because on Emulator the boot time + linux init time can be very long (4+ hours) ++#define TIME_UNTIL_REACH_BOOTLOADER (10000) ++#define PCI_EP_WAIT_TIMEOUT_MS (50000000) ++#define FIRMWARE_WAIT_TIMEOUT_MS (5000000) ++#endif /* ifndef HAILO_EMULATOR */ ++ + extern struct hailo_vdma_hw hailo_pcie_vdma_hw; + ++const struct hailo_pcie_loading_stage* hailo_pcie_get_loading_stage_info(enum hailo_board_type board_type, ++ enum loading_stages stage); ++ + // Reads the interrupt source from BARs, return false if there is no interrupt. + // note - this function clears the interrupt signals. + bool hailo_pcie_read_interrupt(struct hailo_pcie_resources *resources, struct hailo_pcie_interrupt_source *source); +@@ -137,7 +168,9 @@ 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 hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address); ++void hailo_pcie_write_firmware_soft_reset(struct hailo_pcie_resources *resources); ++void hailo_pcie_configure_ep_registers_for_dma_transaction(struct hailo_pcie_resources *resources); ++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 stage); + + int hailo_set_device_type(struct hailo_pcie_resources *resources); + +@@ -157,4 +190,4 @@ void hailo_pcie_soc_read_response(struct + } + #endif + +-#endif /* _HAILO_COMMON_PCIE_COMMON_H_ */ +\ No newline at end of file ++#endif /* _HAILO_COMMON_PCIE_COMMON_H_ */ +--- a/drivers/media/pci/hailo/common/vdma_common.c ++++ b/drivers/media/pci/hailo/common/vdma_common.c +@@ -15,16 +15,6 @@ + #include + #include + +- +-#define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5) +- +-#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) +@@ -160,17 +150,17 @@ static u8 get_channel_id(u8 channel_inde + return (channel_index < MAX_VDMA_CHANNELS_PER_ENGINE) ? (channel_index & 0xF) : INVALID_VDMA_CHANNEL; + } + +-static int program_descriptors_in_chunk( ++int hailo_vdma_program_descriptors_in_chunk( + struct hailo_vdma_hw *vdma_hw, + dma_addr_t chunk_addr, + unsigned int chunk_size, + struct hailo_vdma_descriptors_list *desc_list, + u32 desc_index, + u32 max_desc_index, +- u8 channel_id) ++ u8 channel_index, ++ u8 data_id) + { + const u16 page_size = desc_list->desc_page_size; +- const u8 ddr_data_id = vdma_hw->ddr_data_id; + const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size); + const u32 starting_desc_index = desc_index; + const u32 residue_size = chunk_size % page_size; +@@ -187,7 +177,7 @@ static int program_descriptors_in_chunk( + return -ERANGE; + } + +- encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, channel_id); ++ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, get_channel_id(channel_index)); + if (INVALID_VDMA_ADDRESS == encoded_addr) { + return -EFAULT; + } +@@ -197,7 +187,7 @@ static int program_descriptors_in_chunk( + // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. + hailo_vdma_program_descriptor( + &desc_list->desc_list[desc_index & desc_list->desc_count_mask], +- encoded_addr, page_size, ddr_data_id); ++ encoded_addr, page_size, data_id); + encoded_addr += page_size; + } + +@@ -205,7 +195,7 @@ static int program_descriptors_in_chunk( + // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation. + dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask]; + hailo_vdma_program_descriptor(dma_desc, encoded_addr, +- (residue_size == 0) ? page_size : (u16)residue_size, ddr_data_id); ++ (residue_size == 0) ? page_size : (u16)residue_size, data_id); + + return (int)descs_to_program; + } +@@ -241,7 +231,6 @@ static int bind_and_program_descriptors_ + enum hailo_vdma_interrupts_domain last_desc_interrupts, + bool is_debug) + { +- const u8 channel_id = get_channel_id(channel_index); + int desc_programmed = 0; + int descs_programmed_in_chunk = 0; + u32 max_desc_index = 0; +@@ -279,8 +268,8 @@ static int bind_and_program_descriptors_ + (u32)(sg_dma_len(sg_entry)); + chunk_size = min((u32)program_size, chunk_size); + +- descs_programmed_in_chunk = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, +- starting_desc, max_desc_index, channel_id); ++ descs_programmed_in_chunk = hailo_vdma_program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list, ++ starting_desc, max_desc_index, channel_index, vdma_hw->ddr_data_id); + if (descs_programmed_in_chunk < 0) { + return descs_programmed_in_chunk; + } +@@ -363,16 +352,16 @@ static int validate_channel_state(struct + return 0; + } + +-static void set_num_avail(u8 __iomem *host_regs, u16 num_avail) ++void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail) + { +- 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); ++ u32 regs_val = ioread32(regs); ++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, CHANNEL_NUM_AVAIL_OFFSET * BITS_IN_BYTE, regs_val, num_avail), ++ regs); + } + +-static u16 get_num_proc(u8 __iomem *host_regs) ++u16 hailo_vdma_get_num_proc(u8 __iomem *regs) + { +- return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(host_regs + CHANNEL_NUM_PROC_OFFSET)); ++ return READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, 0, ioread32(regs + CHANNEL_NUM_PROC_OFFSET)); + } + + int hailo_vdma_launch_transfer( +@@ -455,7 +444,7 @@ int hailo_vdma_launch_transfer( + + new_num_avail = (u16)((last_desc + 1) % desc_list->desc_count); + channel->state.num_avail = new_num_avail; +- set_num_avail(channel->host_regs, new_num_avail); ++ hailo_vdma_set_num_avail(channel->host_regs, new_num_avail); + + return (int)total_descs; + } +@@ -463,7 +452,7 @@ int hailo_vdma_launch_transfer( + static void hailo_vdma_push_timestamp(struct hailo_vdma_channel *channel) + { + struct hailo_channel_interrupt_timestamp_list *timestamp_list = &channel->timestamp_list; +- const u16 num_proc = get_num_proc(channel->host_regs); ++ const u16 num_proc = hailo_vdma_get_num_proc(channel->host_regs); + if (TIMESTAMPS_CIRC_SPACE(*timestamp_list) != 0) { + timestamp_list->timestamps[timestamp_list->head].timestamp_ns = ktime_get_ns(); + timestamp_list->timestamps[timestamp_list->head].desc_num_processed = num_proc; +@@ -725,7 +714,7 @@ int hailo_vdma_engine_fill_irq_data(stru + // the actual hw_num_processed is a number between 1 and desc_count. + // Therefore the value can be desc_count, in this case we change it to + // zero. +- hw_num_proc = get_num_proc(channel->host_regs) & channel->state.desc_count_mask; ++ hw_num_proc = hailo_vdma_get_num_proc(channel->host_regs) & channel->state.desc_count_mask; + + while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) { + struct hailo_ongoing_transfer *cur_transfer = +@@ -780,12 +769,13 @@ static void hailo_vdma_channel_abort(u8 + VDMA_CHANNEL_CONTROL_ABORT), host_regs); + } + +-int hailo_vdma_start_channel(u8 __iomem *host_regs, uint64_t desc_dma_address, uint8_t desc_depth, ++int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, + uint8_t data_id) + { + u16 dma_address_l = 0; + u32 dma_address_h = 0; + u32 desc_depth_data_id = 0; ++ u8 desc_depth = ceil_log2(desc_count); + + if (((desc_dma_address & 0xFFFF) != 0) || + (desc_depth > DESCRIPTOR_LIST_MAX_DEPTH)) { +@@ -798,22 +788,22 @@ int hailo_vdma_start_channel(u8 __iomem + } + + // Stop old channel state +- hailo_vdma_stop_channel(host_regs); ++ hailo_vdma_stop_channel(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); ++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET) * BITS_IN_BYTE, ioread32(regs + ++ VDMA_CHANNEL__ALIGNED_ADDRESS_L_OFFSET), dma_address_l), 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); ++ iowrite32(dma_address_h, 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); ++ iowrite32(desc_depth_data_id, regs); + +- start_vdma_control_register(host_regs); ++ start_vdma_control_register(regs); + + return 0; + } +@@ -853,10 +843,10 @@ static int hailo_vdma_wait_until_channel + return -ETIMEDOUT; + } + +-void hailo_vdma_stop_channel(u8 __iomem *host_regs) ++void hailo_vdma_stop_channel(u8 __iomem *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)); ++ u8 host_side_channel_regs = READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, CHANNEL_CONTROL_OFFSET * BITS_IN_BYTE, ioread32(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) +@@ -866,17 +856,17 @@ void hailo_vdma_stop_channel(u8 __iomem + // 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); ++ hailo_vdma_channel_pause(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); ++ err = hailo_vdma_wait_until_channel_idle(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); ++ hailo_vdma_channel_abort(regs); + } + + bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel) +--- a/drivers/media/pci/hailo/common/vdma_common.h ++++ b/drivers/media/pci/hailo/common/vdma_common.h +@@ -16,6 +16,15 @@ + #define VDMA_DESCRIPTOR_LIST_ALIGN (1 << 16) + #define INVALID_VDMA_ADDRESS (0) + ++#define CHANNEL_BASE_OFFSET(channel_index) ((channel_index) << 5) ++ ++#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) ++ + #ifdef __cplusplus + extern "C" + { +@@ -172,6 +181,20 @@ int hailo_vdma_program_descriptors_list( + enum hailo_vdma_interrupts_domain last_desc_interrupts, + bool is_debug); + ++int hailo_vdma_program_descriptors_in_chunk( ++ struct hailo_vdma_hw *vdma_hw, ++ dma_addr_t chunk_addr, ++ unsigned int chunk_size, ++ struct hailo_vdma_descriptors_list *desc_list, ++ u32 desc_index, ++ u32 max_desc_index, ++ u8 channel_index, ++ u8 data_id); ++ ++void hailo_vdma_set_num_avail(u8 __iomem *regs, u16 num_avail); ++ ++u16 hailo_vdma_get_num_proc(u8 __iomem *regs); ++ + /** + * Launch a transfer on some vdma channel. Includes: + * 1. Binding the transfer buffers to the descriptors list. +@@ -249,9 +272,9 @@ 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); ++int hailo_vdma_start_channel(u8 __iomem *regs, uint64_t desc_dma_address, uint32_t desc_count, uint8_t data_id); + +-void hailo_vdma_stop_channel(u8 __iomem *host_regs); ++void hailo_vdma_stop_channel(u8 __iomem *regs); + + bool hailo_check_channel_index(u8 channel_index, u32 src_channels_bitmask, bool is_input_channel); + +--- a/drivers/media/pci/hailo/src/fops.c ++++ b/drivers/media/pci/hailo/src/fops.c +@@ -294,6 +294,54 @@ static void firmware_notification_irq_ha + } + } + ++static void boot_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_SOFT_RESET_IRQ) { ++ hailo_dbg(board, "soft reset trigger IRQ\n"); ++ complete(&board->soft_reset.reset_completed); ++ } ++ if (irq_source->sw_interrupts & HAILO_PCIE_BOOT_IRQ) { ++ hailo_dbg(board, "boot trigger IRQ\n"); ++ complete_all(&board->fw_boot.fw_loaded_completion); ++ } else { ++ board->fw_boot.boot_used_channel_bitmap &= ~irq_source->vdma_channels_bitmap; ++ hailo_dbg(board, "boot vDMA data IRQ - channel_bitmap = 0x%x\n", irq_source->vdma_channels_bitmap); ++ if (0 == board->fw_boot.boot_used_channel_bitmap) { ++ complete_all(&board->fw_boot.vdma_boot_completion); ++ hailo_dbg(board, "boot vDMA data trigger IRQ\n"); ++ } ++ } ++} ++ ++static void nnc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_CONTROL_IRQ) { ++ complete(&board->nnc.fw_control.completion); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_DRIVER_DOWN_IRQ) { ++ complete(&board->driver_down.reset_completed); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_NNC_FW_NOTIFICATION_IRQ) { ++ firmware_notification_irq_handler(board); ++ } ++} ++ ++static void soc_irq_handler(struct hailo_pcie_board *board, struct hailo_pcie_interrupt_source *irq_source) ++{ ++ if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CONTROL_IRQ) { ++ complete_all(&board->soc.control_resp_ready); ++ } ++ ++ if (irq_source->sw_interrupts & HAILO_PCIE_SOC_CLOSE_IRQ) { ++ hailo_info(board, "soc_irq_handler - HAILO_PCIE_SOC_CLOSE_IRQ\n"); ++ // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. ++ hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], ++ 0xFFFFFFFF); ++ } ++} ++ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + irqreturn_t hailo_irqhandler(int irq, void *dev_id, struct pt_regs *regs) + #else +@@ -320,39 +368,21 @@ irqreturn_t hailo_irqhandler(int irq, vo + + return_value = IRQ_HANDLED; + +- // wake fw_control if needed +- if (irq_source.interrupt_bitmask & FW_CONTROL) { +- complete(&board->nnc.fw_control.completion); +- } +- +- // wake driver_down if needed +- if (irq_source.interrupt_bitmask & DRIVER_DOWN) { +- complete(&board->driver_down.reset_completed); +- } +- +- if (irq_source.interrupt_bitmask & FW_NOTIFICATION) { +- if (!completion_done(&board->fw_loaded_completion)) { +- // Complete firmware loaded completion +- complete_all(&board->fw_loaded_completion); ++ if (board->fw_boot.is_in_boot) { ++ boot_irq_handler(board, &irq_source); ++ } else { ++ if (HAILO_ACCELERATOR_TYPE_NNC == board->pcie_resources.accelerator_type) { ++ nnc_irq_handler(board, &irq_source); ++ } else if (HAILO_ACCELERATOR_TYPE_SOC == board->pcie_resources.accelerator_type) { ++ soc_irq_handler(board, &irq_source); + } else { +- firmware_notification_irq_handler(board); ++ hailo_err(board, "Invalid accelerator type %d\n", board->pcie_resources.accelerator_type); + } +- } +- +- if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) { +- complete_all(&board->soc.control_resp_ready); +- } + +- if (irq_source.interrupt_bitmask & SOC_CLOSED_IRQ) { +- hailo_info(board, "hailo_irqhandler - SOC_CLOSED_IRQ\n"); +- // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not. +- hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX], +- 0xFFFFFFFF); +- } +- +- if (0 != irq_source.vdma_channels_bitmap) { +- hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX, +- irq_source.vdma_channels_bitmap); ++ if (0 != irq_source.vdma_channels_bitmap) { ++ hailo_vdma_irq_handler(&board->vdma, DEFAULT_VDMA_ENGINE_INDEX, ++ irq_source.vdma_channels_bitmap); ++ } + } + } + +--- a/drivers/media/pci/hailo/src/nnc.c ++++ b/drivers/media/pci/hailo/src/nnc.c +@@ -148,8 +148,8 @@ static long hailo_read_notification_ioct + + // Check if was disabled + if (current_waiting_thread->is_disabled) { +- hailo_info(board, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid); +- err = -EINVAL; ++ hailo_info(board, "HAILO_READ_NOTIFICATION - notification disabled for tgid=%d\n", current->tgid); ++ err = -ECANCELED; + goto l_exit; + } + +--- a/drivers/media/pci/hailo/src/pcie.c ++++ b/drivers/media/pci/hailo/src/pcie.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) + #include +@@ -29,6 +30,7 @@ + #include "utils/logs.h" + #include "utils/compact.h" + #include "vdma/vdma.h" ++#include "vdma/memory.h" + + #if LINUX_VERSION_CODE < KERNEL_VERSION( 5, 4, 0 ) + #include +@@ -46,8 +48,9 @@ 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; ++static bool force_hailo10h_legacy_mode = false; + static bool force_boot_linux_from_eemc = false; ++static bool support_soft_reset = true; + + #define DEVICE_NODE_NAME "hailo" + static int char_major = 0; +@@ -291,12 +294,468 @@ static void hailo_pcie_remove_board(stru + up(&g_hailo_add_board_mutex); + } + +-static bool wait_for_firmware_completion(struct completion *fw_load_completion) ++/** ++ * Wait until the relevant completion is done. ++ * ++ * @param completion - pointer to the completion struct to wait for. ++ * @param msecs - the amount of time to wait in milliseconds. ++ * @return false if timed out, true if completed. ++ */ ++static bool wait_for_firmware_completion(struct completion *completion, unsigned int msecs) ++{ ++ return (0 != wait_for_completion_timeout(completion, msecs_to_jiffies(msecs))); ++} ++ ++/** ++ * Program one FW file descriptors to the vDMA engine. ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param file_address - the address of the file in the device memory. ++ * @param transfer_buffer - the buffer to program to the vDMA engine. ++ * @param channel_index - the index of the channel to program. ++ * @param filename - the name of the file to program. ++ * @param raise_int_on_completion - true if this is the last descriptors chunk in the specific channel in the boot flow, false otherwise. If true - will enable ++ * an IRQ for the relevant channel when the transfer is finished. ++ * @return the amount of descriptors programmed on success, negative error code on failure. ++ */ ++static int pcie_vdma_program_one_file_descriptors(struct device *dev, struct hailo_pcie_boot_dma_channel_state *boot_channel_state, ++ u32 file_address, struct hailo_vdma_mapped_transfer_buffer transfer_buffer, u8 channel_index, const char *filename, bool raise_int_on_completion) ++{ ++ int device_desc = 0, host_desc = 0; ++ enum hailo_vdma_interrupts_domain interrupts_domain = raise_int_on_completion ? HAILO_VDMA_INTERRUPTS_DOMAIN_HOST : ++ HAILO_VDMA_INTERRUPTS_DOMAIN_NONE; ++ ++ hailo_dev_dbg(dev, "channel_index = %d, file_name = %s, file_address = 0x%x, transfer_buffer.offset = 0x%x,\ ++ size_to_program = 0x%x, starting_desc/desc_index = 0x%x\n", channel_index, filename, file_address, ++ transfer_buffer.offset, transfer_buffer.size, boot_channel_state->desc_program_num); ++ ++ // program descriptors ++ device_desc = hailo_vdma_program_descriptors_in_chunk(&hailo_pcie_vdma_hw, file_address, transfer_buffer.size, ++ &boot_channel_state->device_descriptors_buffer.desc_list, boot_channel_state->desc_program_num, ++ (boot_channel_state->device_descriptors_buffer.desc_list.desc_count - 1), channel_index, HAILO_PCI_EP_HOST_DMA_DATA_ID); ++ if (device_desc < 0) { ++ hailo_dev_err(dev, "Failed to program device descriptors, error = %u\n", device_desc); ++ return device_desc; ++ } ++ ++ host_desc = hailo_vdma_program_descriptors_list(&hailo_pcie_vdma_hw, &boot_channel_state->host_descriptors_buffer.desc_list, ++ boot_channel_state->desc_program_num, &transfer_buffer, true, channel_index, interrupts_domain, false); ++ if (host_desc < 0) { ++ hailo_dev_err(dev, "Failed to program host descriptors, error = %u\n", host_desc); ++ return host_desc; ++ } ++ ++ // checks that same amount of decsriptors were programmed on device side and host side ++ if (host_desc != device_desc) { ++ hailo_dev_err(dev, "Host and device descriptors should be the same\n"); ++ return -EINVAL; ++ } ++ ++ return host_desc; ++} ++ ++/** ++ * Program one FW file to the vDMA engine. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param file_address - the address of the file in the device memory. ++ * @param filename - the name of the file to program. ++ * @param raise_int_on_completion - true if this is the last file in the boot flow, false otherwise. uses to enable an IRQ for the ++ * relevant channel when the transfer is finished. ++ * @return 0 on success, negative error code on failure. at the end of the function the firmware is released. ++ */ ++static int pcie_vdma_program_one_file(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, u32 file_address, ++ const char *filename, bool raise_int_on_completion) ++{ ++ const struct firmware *firmware = NULL; ++ struct hailo_vdma_mapped_transfer_buffer transfer_buffer = {0}; ++ int desc_programmed = 0; ++ int err = 0; ++ size_t bytes_copied = 0, remaining_size = 0, data_offset = 0, desc_num_left = 0, current_desc_to_program = 0; ++ ++ hailo_notice(board, "Programing file %s for dma transfer\n", filename); ++ ++ // load firmware directly without usermode helper for the relevant file ++ err = request_firmware_direct(&firmware, filename, board->vdma.dev); ++ if (err < 0) { ++ hailo_err(board, "Failed to allocate memory for file %s\n", filename); ++ return err; ++ } ++ ++ // set the remaining size as the whole file size to begin with ++ remaining_size = firmware->size; ++ ++ while (remaining_size > 0) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; ++ bool is_last_desc_chunk_of_curr_channel = false; ++ bool rais_interrupt_on_last_chunk = false; ++ ++ hailo_dbg(board, "desc_program_num = 0x%x, desc_page_size = 0x%x, on channel = %d\n", ++ channel->desc_program_num, HAILO_PCI_OVER_VDMA_PAGE_SIZE, boot_dma_state->curr_channel_index); ++ ++ // increment the channel index if the current channel is full ++ if ((MAX_SG_DESCS_COUNT - 1) == channel->desc_program_num) { ++ boot_dma_state->curr_channel_index++; ++ channel = &boot_dma_state->channels[boot_dma_state->curr_channel_index]; ++ board->fw_boot.boot_used_channel_bitmap |= (1 << boot_dma_state->curr_channel_index); ++ } ++ ++ // calculate the number of descriptors left to program and the number of bytes left to program ++ desc_num_left = (MAX_SG_DESCS_COUNT - 1) - channel->desc_program_num; ++ ++ // prepare the transfer buffer to make sure all the fields are initialized ++ transfer_buffer.sg_table = &channel->sg_table; ++ transfer_buffer.size = min(remaining_size, (desc_num_left * HAILO_PCI_OVER_VDMA_PAGE_SIZE)); ++ // no need to check for overflow since the variables are constant and always desc_program_num <= max u16 (65536) ++ // & the buffer max size is 256 Mb << 4G (max u32) ++ transfer_buffer.offset = (channel->desc_program_num * HAILO_PCI_OVER_VDMA_PAGE_SIZE); ++ ++ // check if this is the last descriptor chunk to program in the whole boot flow ++ current_desc_to_program = (transfer_buffer.size / HAILO_PCI_OVER_VDMA_PAGE_SIZE); ++ is_last_desc_chunk_of_curr_channel = ((MAX_SG_DESCS_COUNT - 1) == ++ (current_desc_to_program + channel->desc_program_num)); ++ rais_interrupt_on_last_chunk = (is_last_desc_chunk_of_curr_channel || (raise_int_on_completion && ++ (remaining_size == transfer_buffer.size))); ++ ++ // try to copy the file to the buffer, if failed, release the firmware and return ++ bytes_copied = sg_pcopy_from_buffer(transfer_buffer.sg_table->sgl, transfer_buffer.sg_table->orig_nents, ++ &firmware->data[data_offset], transfer_buffer.size, transfer_buffer.offset); ++ if (transfer_buffer.size != bytes_copied) { ++ hailo_err(board, "There is not enough memory allocated to copy file %s\n", filename); ++ release_firmware(firmware); ++ return -EFBIG; ++ } ++ ++ // program the descriptors ++ desc_programmed = pcie_vdma_program_one_file_descriptors(&board->pDev->dev, channel, (file_address + data_offset), ++ transfer_buffer, boot_dma_state->curr_channel_index, filename, rais_interrupt_on_last_chunk); ++ if (desc_programmed < 0) { ++ hailo_err(board, "Failed to program descriptors for file %s, on cahnnel = %d\n", filename, ++ boot_dma_state->curr_channel_index); ++ release_firmware(firmware); ++ return desc_programmed; ++ } ++ ++ // Update remaining size, data_offset and desc_program_num for the next iteration ++ remaining_size -= transfer_buffer.size; ++ data_offset += transfer_buffer.size; ++ channel->desc_program_num += desc_programmed; ++ } ++ ++ hailo_notice(board, "File %s programed successfully\n", filename); ++ ++ release_firmware(firmware); ++ ++ return desc_programmed; ++} ++ ++/** ++ * Program the entire batch of firmware files to the vDMA engine. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param resources - pointer to the hailo_pcie_resources struct. ++ * @param stage - the stage to program. ++ * @return 0 on success, negative error code on failure. ++ */ ++static long pcie_vdma_program_entire_batch(struct hailo_pcie_board *board, struct hailo_pcie_boot_dma_state *boot_dma_state, ++ struct hailo_pcie_resources *resources, u32 stage) ++{ ++ long err = 0; ++ int file_index = 0; ++ const struct hailo_pcie_loading_stage *stage_info = hailo_pcie_get_loading_stage_info(resources->board_type, stage); ++ const struct hailo_file_batch *files_batch = stage_info->batch; ++ const u8 amount_of_files = stage_info->amount_of_files_in_stage; ++ const char *filename = NULL; ++ u32 file_address = 0; ++ ++ for (file_index = 0; file_index < amount_of_files; file_index++) ++ { ++ filename = files_batch[file_index].filename; ++ file_address = files_batch[file_index].address; ++ ++ if (NULL == filename) { ++ hailo_err(board, "The amount of files wasn't specified for stage %d\n", stage); ++ break; ++ } ++ ++ err = pcie_vdma_program_one_file(board, boot_dma_state, file_address, filename, ++ (file_index == (amount_of_files - 1))); ++ if (err < 0) { ++ hailo_err(board, "Failed to program file %s\n", filename); ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Release noncontinuous memory (virtual continuous memory). (sg table and kernel_addrs) ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param sg_table - the sg table to release. ++ * @param kernel_addrs - the kernel address to release. ++ */ ++static void pcie_vdma_release_noncontinuous_memory(struct device *dev, struct sg_table *sg_table, void *kernel_addrs) ++{ ++ dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++ sg_free_table(sg_table); ++ vfree(kernel_addrs); ++} ++ ++/** ++ * Allocate noncontinuous memory (virtual continuous memory). ++ * ++ * @param dev - pointer to the device struct we are working on. ++ * @param buffer_size - the size of the buffer to allocate. ++ * @param kernel_addrs - pointer to the allocated buffer. ++ * @param sg_table - pointer to the sg table struct. ++ * @return 0 on success, negative error code on failure. on failure all resurces are released. (pages array, sg table, kernel_addrs) ++ */ ++static long pcie_vdma_allocate_noncontinuous_memory(struct device *dev, u64 buffer_size, void **kernel_addrs, struct sg_table *sg_table) ++{ ++ struct page **pages = NULL; ++ size_t npages = 0; ++ struct scatterlist *sgl = NULL; ++ long err = 0; ++ size_t i = 0; ++ ++ // allocate noncontinuous memory for the kernel address (virtual continuous memory) ++ *kernel_addrs = vmalloc(buffer_size); ++ if (NULL == *kernel_addrs) { ++ hailo_dev_err(dev, "Failed to allocate memory for kernel_addrs\n"); ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ // map the memory to pages ++ npages = DIV_ROUND_UP(buffer_size, PAGE_SIZE); ++ ++ // allocate memory for a virtually contiguous array for the pages ++ pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); ++ if (!pages) { ++ err = -ENOMEM; ++ hailo_dev_err(dev, "Failed to allocate memory for pages\n"); ++ goto release_user_addrs; ++ } ++ ++ // walk a vmap address to the struct page it maps ++ for (i = 0; i < npages; i++) { ++ pages[i] = vmalloc_to_page(*kernel_addrs + (i * PAGE_SIZE)); ++ if (!pages[i]) { ++ err = -ENOMEM; ++ hailo_dev_err(dev, "Failed to get page from vmap address\n"); ++ goto release_array; ++ } ++ } ++ ++ // allocate and initialize the sg table from a list of pages ++ sgl = sg_alloc_table_from_pages_segment_compat(sg_table, pages, npages, 0, buffer_size, SGL_MAX_SEGMENT_SIZE, NULL, ++ 0, GFP_KERNEL); ++ if (IS_ERR(sgl)) { ++ err = PTR_ERR(sgl); ++ hailo_dev_err(dev, "sg table alloc failed (err %ld)..\n", err); ++ goto release_array; ++ } ++ ++ // map the sg list ++ sg_table->nents = dma_map_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++ if (0 == sg_table->nents) { ++ hailo_dev_err(dev, "failed to map sg list for user buffer\n"); ++ err = -ENXIO; ++ goto release_sg_table; ++ } ++ ++ // clean exit - just release the pages array & return err = 0 ++ err = 0; ++ kfree(pages); ++ goto exit; ++ ++release_sg_table: ++ dma_unmap_sg(dev, sg_table->sgl, sg_table->orig_nents, DMA_TO_DEVICE); ++release_array: ++ kfree(pages); ++release_user_addrs: ++ vfree(*kernel_addrs); ++exit: ++ return err; ++} ++ ++/** ++ * Release all boot resources. ++ * ++ * @param board - pointer to the board struct we are working on. ++ * @param engine - pointer to the vdma engine struct. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ */ ++static void pcie_vdme_release_boot_resources(struct hailo_pcie_board *board, struct hailo_vdma_engine *engine, ++ struct hailo_pcie_boot_dma_state *boot_dma_state) ++{ ++ u8 channel_index = 0; ++ ++ // release all the resources ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; ++ // release descriptor lists ++ if (channel->host_descriptors_buffer.kernel_address != NULL) { ++ hailo_desc_list_release(&board->pDev->dev, &channel->host_descriptors_buffer); ++ } ++ if (channel->device_descriptors_buffer.kernel_address != NULL) { ++ hailo_desc_list_release(&board->pDev->dev, &channel->device_descriptors_buffer); ++ } ++ ++ // stops all boot vDMA channels ++ hailo_vdma_stop_channel(engine->channels[channel_index].host_regs); ++ hailo_vdma_stop_channel(engine->channels[channel_index].device_regs); ++ ++ // release noncontinuous memory (virtual continuous memory) ++ if (channel->kernel_addrs != NULL) { ++ pcie_vdma_release_noncontinuous_memory(&board->pDev->dev, &channel->sg_table, channel->kernel_addrs); ++ } ++ } ++} ++ ++/** ++ * Allocate boot resources for vDMA transfer. ++ * ++ * @param desc_page_size - the size of the descriptor page. ++ * @param board - pointer to the board struct we are working on. ++ * @param boot_dma_state - pointer to the boot dma state struct which includes all of the boot resources. ++ * @param engine - pointer to the vDMA engine struct. ++ * @return 0 on success, negative error code on failure. in case of failure descriptor lists are released, ++ * boot vDMA channels are stopped and memory is released. ++ */ ++static long pcie_vdme_allocate_boot_resources(u32 desc_page_size, struct hailo_pcie_board *board, ++ struct hailo_pcie_boot_dma_state *boot_dma_state, struct hailo_vdma_engine *engine) ++{ ++ long err = 0; ++ uintptr_t device_handle = 0, host_handle = 0; ++ u8 channel_index = 0; ++ ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &boot_dma_state->channels[channel_index]; ++ ++ // create 2 descriptors list - 1 for the host & 1 for the device for each channel ++ err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, host_handle, false, ++ &channel->host_descriptors_buffer); ++ if (err < 0) { ++ hailo_err(board, "failed to allocate host descriptors list buffer\n"); ++ goto release_all_resources; ++ } ++ ++ err = hailo_desc_list_create(&board->pDev->dev, MAX_SG_DESCS_COUNT, desc_page_size, device_handle, false, ++ &channel->device_descriptors_buffer); ++ if (err < 0) { ++ hailo_err(board, "failed to allocate device descriptors list buffer\n"); ++ goto release_all_resources; ++ } ++ ++ // start vDMA channels - both sides with DDR at the host side (AKA ID 0) ++ err = hailo_vdma_start_channel(engine->channels[channel_index].host_regs, ++ channel->host_descriptors_buffer.dma_address, ++ channel->host_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_err(board, "Error starting host vdma channel\n"); ++ goto release_all_resources; ++ } ++ ++ err = hailo_vdma_start_channel(engine->channels[channel_index].device_regs, ++ channel->device_descriptors_buffer.dma_address, ++ channel->device_descriptors_buffer.desc_list.desc_count, board->vdma.hw->ddr_data_id); ++ if (err < 0) { ++ hailo_err(board, "Error starting device vdma channel\n"); ++ goto release_all_resources; ++ } ++ ++ // initialize the buffer size per channel ++ channel->buffer_size = (MAX_SG_DESCS_COUNT * desc_page_size); ++ ++ // allocate noncontinuous memory (virtual continuous memory) ++ err = pcie_vdma_allocate_noncontinuous_memory(&board->pDev->dev, channel->buffer_size, &channel->kernel_addrs, ++ &channel->sg_table); ++ if (err < 0) { ++ hailo_err(board, "Failed to allocate noncontinuous memory\n"); ++ goto release_all_resources; ++ } ++ } ++ ++ return 0; ++ ++release_all_resources: ++ pcie_vdme_release_boot_resources(board, engine, boot_dma_state); ++ return err; ++} ++ ++/** ++ * Write FW boot files over vDMA using multiple channels for timing optimizations. ++ * ++ * The function is divided into the following steps: ++ * 1) Allocate resources for the boot process. ++ * 2) Programs descriptors to point to the memory and start the vDMA. ++ * 3) Waits until the vDMA is done and triggers the device to start the boot process. ++ * 4) Releases all the resources. ++ * ++ * @param board - pointer to the board struct. ++ * @param stage - the stage of the boot process. ++ * @param desc_page_size - the size of the descriptor page. ++ * @return 0 on success, negative error code on failure. in any case all resurces are released. ++ */ ++static long pcie_write_firmware_batch_over_dma(struct hailo_pcie_board *board, u32 stage, u32 desc_page_size) + { +- return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS))); ++ long err = 0; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; ++ u8 channel_index = 0; ++ ++ err = pcie_vdme_allocate_boot_resources(desc_page_size, board, &board->fw_boot.boot_dma_state, engine); ++ if (err < 0) { ++ hailo_err(board, "Failed to create descriptors and start channels\n"); ++ return err; ++ } ++ ++ // initialize the completion for the vDMA boot data completion ++ reinit_completion(&board->fw_boot.vdma_boot_completion); ++ ++ err = pcie_vdma_program_entire_batch(board, &board->fw_boot.boot_dma_state, &board->pcie_resources, stage); ++ if (err < 0) { ++ hailo_err(board, "Failed to program entire batch\n"); ++ goto release_all; ++ } ++ ++ // sync the sg tables for the device before statirng the vDMA ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ dma_sync_sgtable_for_device(&board->pDev->dev, &board->fw_boot.boot_dma_state.channels[channel_index].sg_table, ++ DMA_TO_DEVICE); ++ } ++ ++ // start the vDMA transfer on all channels ++ for (channel_index = 0; channel_index < HAILO_PCI_OVER_VDMA_NUM_CHANNELS; channel_index++) { ++ struct hailo_pcie_boot_dma_channel_state *channel = &board->fw_boot.boot_dma_state.channels[channel_index]; ++ if (channel->desc_program_num != 0) { ++ hailo_vdma_set_num_avail(engine->channels[channel_index].host_regs, channel->desc_program_num); ++ hailo_vdma_set_num_avail(engine->channels[channel_index].device_regs, channel->desc_program_num); ++ hailo_dbg(board, "Set num avail to %u, on channel %u\n", channel->desc_program_num, channel_index); ++ } ++ } ++ ++ if (!wait_for_firmware_completion(&board->fw_boot.vdma_boot_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, SECOND_STAGE)->timeout)) { ++ hailo_err(board, "Timeout waiting for vDMA boot data completion\n"); ++ err = -ETIMEDOUT; ++ goto release_all; ++ } ++ ++ hailo_notice(board, "vDMA transfer completed, triggering boot\n"); ++ reinit_completion(&board->fw_boot.fw_loaded_completion); ++ hailo_trigger_firmware_boot(&board->pcie_resources, stage); ++ ++release_all: ++ pcie_vdme_release_boot_resources(board, engine, &board->fw_boot.boot_dma_state); ++ return err; + } + +-static int hailo_load_soc_firmware(struct hailo_pcie_resources *resources, ++static int load_soc_firmware(struct hailo_pcie_board *board, struct hailo_pcie_resources *resources, + struct device *dev, struct completion *fw_load_completion) + { + u32 boot_status = 0; +@@ -304,104 +763,165 @@ static int hailo_load_soc_firmware(struc + u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE; + + if (hailo_pcie_is_firmware_loaded(resources)) { +- hailo_dev_warn(dev, "Firmware batch was already loaded\n"); ++ hailo_dev_warn(dev, "SOC Firmware batch was already loaded\n"); + return 0; + } + ++ // configure the EP registers for the DMA transaction ++ hailo_pcie_configure_ep_registers_for_dma_transaction(resources); ++ + init_completion(fw_load_completion); ++ init_completion(&board->fw_boot.vdma_boot_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); ++ hailo_dev_err(dev, "Failed writing SOC FIRST_STAGE firmware files. err %d\n", err); + return err; + } + +- if (!wait_for_firmware_completion(fw_load_completion)) { ++ if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, FIRST_STAGE)->timeout)) { + boot_status = hailo_get_boot_status(resources); +- hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ hailo_dev_err(dev, "Timeout waiting for SOC FIRST_STAGE 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); ++ reinit_completion(fw_load_completion); ++ ++ err = (int)pcie_write_firmware_batch_over_dma(board, second_stage, HAILO_PCI_OVER_VDMA_PAGE_SIZE); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); ++ hailo_dev_err(dev, "Failed writing SOC SECOND_STAGE firmware files over vDMA. err %d\n", err); + return err; + } + +- if (!wait_for_firmware_completion(fw_load_completion)) { ++ if (!wait_for_firmware_completion(fw_load_completion, hailo_pcie_get_loading_stage_info(resources->board_type, SECOND_STAGE)->timeout)) { + boot_status = hailo_get_boot_status(resources); +- hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status); ++ hailo_dev_err(dev, "Timeout waiting for SOC SECOND_STAGE firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } + +- hailo_dev_notice(dev, "Firmware Batch loaded successfully\n"); ++ reinit_completion(fw_load_completion); ++ reinit_completion(&board->fw_boot.vdma_boot_completion); ++ ++ hailo_dev_notice(dev, "SOC Firmware Batch loaded successfully\n"); + + return 0; + } +- +-static int hailo_load_nnc_firmware(struct hailo_pcie_resources *resources, +- struct device *dev, struct completion *fw_load_completion) ++static int load_nnc_firmware(struct hailo_pcie_board *board) + { + u32 boot_status = 0; + int err = 0; ++ struct device *dev = &board->pDev->dev; + +- if (hailo_pcie_is_firmware_loaded(resources)) { +- hailo_dev_warn(dev, "Firmware batch was already loaded\n"); +- return 0; ++ if (hailo_pcie_is_firmware_loaded(&board->pcie_resources)) { ++ if (support_soft_reset) { ++ err = hailo_pcie_soft_reset(&board->pcie_resources, &board->soft_reset.reset_completed); // send control, wait for done ++ if (err < 0) { ++ hailo_dev_err(dev, "Failed hailo pcie soft reset. err %d\n", err); ++ return 0; ++ } ++ hailo_dev_notice(dev, "Soft reset done\n"); ++ } else { ++ hailo_dev_warn(dev, "NNC Firmware batch was already loaded\n"); ++ return 0; ++ } + } + +- init_completion(fw_load_completion); ++ init_completion(&board->fw_boot.fw_loaded_completion); + +- err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE); ++ err = hailo_pcie_write_firmware_batch(dev, &board->pcie_resources, FIRST_STAGE); + if (err < 0) { +- hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err); ++ hailo_dev_err(dev, "Failed writing NNC firmware files. err %d\n", err); + return err; + } + +- 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); ++ if (!wait_for_firmware_completion(&board->fw_boot.fw_loaded_completion, hailo_pcie_get_loading_stage_info(board->pcie_resources.board_type, FIRST_STAGE)->timeout)) { ++ boot_status = hailo_get_boot_status(&board->pcie_resources); ++ hailo_dev_err(dev, "Timeout waiting for NNC firmware file, boot status %u\n", boot_status); + return -ETIMEDOUT; + } + +- hailo_dev_notice(dev, "Firmware loaded successfully\n"); ++ hailo_dev_notice(dev, "NNC Firmware loaded successfully\n"); + + return 0; + } + +-static int hailo_activate_board(struct hailo_pcie_board *board) ++int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed) + { ++ bool completion_result = false; + int err = 0; + +- (void)hailo_pcie_disable_aspm(board, PCIE_LINK_STATE_L0S, false); ++ hailo_pcie_write_firmware_soft_reset(resources); + +- err = hailo_enable_interrupts(board); +- if (err < 0) { +- hailo_err(board, "Failed Enabling interrupts %d\n", err); ++ reinit_completion(reset_completed); ++ ++ // Wait for response ++ completion_result = ++ wait_for_firmware_completion(reset_completed, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS)); ++ if (completion_result == false) { ++ pr_warn("hailo reset firmware, timeout waiting for shutdown response (timeout_ms=%d)\n", FIRMWARE_WAIT_TIMEOUT_MS); ++ err = -ETIMEDOUT; + return err; + } + ++ msleep(TIME_UNTIL_REACH_BOOTLOADER); ++ pr_notice("hailo_driver_down finished\n"); ++ ++ return err; ++} ++ ++static int load_firmware(struct hailo_pcie_board *board) ++{ + switch (board->pcie_resources.accelerator_type) { + case HAILO_ACCELERATOR_TYPE_SOC: +- err = hailo_load_soc_firmware(&board->pcie_resources, &board->pDev->dev, +- &board->fw_loaded_completion); +- break; ++ return load_soc_firmware(board, &board->pcie_resources, &board->pDev->dev, &board->fw_boot.fw_loaded_completion); + case HAILO_ACCELERATOR_TYPE_NNC: +- err = hailo_load_nnc_firmware(&board->pcie_resources, &board->pDev->dev, +- &board->fw_loaded_completion); +- break; ++ return load_nnc_firmware(board); + default: +- hailo_err(board, "Invalid board type"); +- err = -EINVAL; ++ hailo_err(board, "Invalid board type %d\n", board->pcie_resources.accelerator_type); ++ return -EINVAL; + } ++} ++ ++static int enable_boot_interrupts(struct hailo_pcie_board *board) ++{ ++ int err = hailo_enable_interrupts(board); + if (err < 0) { +- hailo_err(board, "Firmware load failed\n"); +- hailo_disable_interrupts(board); ++ hailo_err(board, "Failed enabling interrupts %d\n", err); + return err; + } + ++ board->fw_boot.is_in_boot = true; ++ return 0; ++} ++ ++static void disable_boot_interrupts(struct hailo_pcie_board *board) ++{ ++ board->fw_boot.is_in_boot = false; + hailo_disable_interrupts(board); ++} ++ ++static int hailo_activate_board(struct hailo_pcie_board *board) ++{ ++ int err = 0; ++ ktime_t start_time = 0, end_time = 0; ++ ++ (void)hailo_pcie_disable_aspm(board, PCIE_LINK_STATE_L0S, false); ++ ++ err = enable_boot_interrupts(board); ++ if (err < 0) { ++ return err; ++ } ++ ++ start_time = ktime_get(); ++ err = load_firmware(board); ++ end_time = ktime_get(); ++ hailo_notice(board, "FW loaded, took %lld ms\n", ktime_to_ms(ktime_sub(end_time, start_time))); ++ disable_boot_interrupts(board); ++ ++ if (err < 0) { ++ hailo_err(board, "Firmware load failed\n"); ++ return err; ++ } + + if (power_mode_enabled()) { + // Setting the device to low power state, until the user opens the device +@@ -520,12 +1040,9 @@ static int pcie_resources_init(struct pc + } + + +- // 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) { ++ if (HAILO_BOARD_TYPE_HAILO10H == board_type){ ++ if (true == force_hailo10h_legacy_mode) { + board_type = HAILO_BOARD_TYPE_HAILO10H_LEGACY; +- } else { +- board_type = HAILO_BOARD_TYPE_HAILO10H; + } + } + +@@ -696,7 +1213,8 @@ static int hailo_pcie_probe(struct pci_d + } + + pBoard->interrupts_enabled = false; +- init_completion(&pBoard->fw_loaded_completion); ++ pBoard->fw_boot.is_in_boot = false; ++ init_completion(&pBoard->fw_boot.fw_loaded_completion); + + sema_init(&pBoard->mutex, 1); + atomic_set(&pBoard->ref_count, 0); +@@ -707,6 +1225,7 @@ static int hailo_pcie_probe(struct pci_d + hailo_soc_init(&pBoard->soc); + + init_completion(&pBoard->driver_down.reset_completed); ++ init_completion(&pBoard->soft_reset.reset_completed); + + memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params)); + +@@ -724,6 +1243,10 @@ static int hailo_pcie_probe(struct pci_d + goto probe_release_pcie_resources; + } + ++ // Initialize the boot channel bitmap to 1 since channel 0 is always used for boot ++ // (we will always use at least 1 channel which is LSB in the bitmap) ++ pBoard->fw_boot.boot_used_channel_bitmap = (1 << 0); ++ memset(&pBoard->fw_boot.boot_dma_state, 0, sizeof(pBoard->fw_boot.boot_dma_state)); + err = hailo_activate_board(pBoard); + if (err < 0) { + hailo_err(pBoard, "Failed activating board %d\n", err); +@@ -927,8 +1450,8 @@ static const struct pci_error_handlers h + static struct pci_device_id hailo_pcie_id_table[] = + { + {PCI_DEVICE_DATA(HAILO, HAILO8, HAILO_BOARD_TYPE_HAILO8)}, +- {PCI_DEVICE_DATA(HAILO, HAILO15, HAILO_BOARD_TYPE_HAILO15)}, +- {PCI_DEVICE_DATA(HAILO, PLUTO, HAILO_BOARD_TYPE_PLUTO)}, ++ {PCI_DEVICE_DATA(HAILO, HAILO10H, HAILO_BOARD_TYPE_HAILO10H)}, ++ {PCI_DEVICE_DATA(HAILO, HAILO15L, HAILO_BOARD_TYPE_HAILO15L)}, + {0,0,0,0,0,0,0 }, + }; + +@@ -1024,12 +1547,15 @@ 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_param(force_hailo10h_legacy_mode, bool, S_IRUGO); ++MODULE_PARM_DESC(force_hailo10h_legacy_mode, "Forces work with Hailo10h in legacy mode(relevant for emulators)"); + + module_param(force_boot_linux_from_eemc, bool, S_IRUGO); + MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)"); + ++module_param(support_soft_reset, bool, S_IRUGO); ++MODULE_PARM_DESC(support_soft_reset, "enables driver reload to reload a new firmware as well"); ++ + 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 +@@ -19,6 +19,9 @@ + + #include + ++#define HAILO_PCI_OVER_VDMA_NUM_CHANNELS (8) ++#define HAILO_PCI_OVER_VDMA_PAGE_SIZE (512) ++ + struct hailo_fw_control_info { + // protects that only one fw control will be send at a time + struct semaphore mutex; +@@ -33,6 +36,11 @@ struct hailo_pcie_driver_down_info { + struct completion reset_completed; + }; + ++struct hailo_pcie_soft_reset { ++ // called from the interrupt handler to notify that FW completed reset ++ struct completion reset_completed; ++}; ++ + struct hailo_fw_boot { + // the filp that enabled interrupts for fw boot. the interrupt is enabled if this is not null + struct file *filp; +@@ -64,6 +72,32 @@ struct hailo_file_context { + u32 soc_used_channels_bitmap; + }; + ++struct hailo_pcie_boot_dma_channel_state { ++ struct hailo_descriptors_list_buffer host_descriptors_buffer; ++ struct hailo_descriptors_list_buffer device_descriptors_buffer; ++ struct sg_table sg_table; ++ u64 buffer_size; ++ void *kernel_addrs; ++ u32 desc_program_num; ++}; ++ ++struct hailo_pcie_boot_dma_state { ++ struct hailo_pcie_boot_dma_channel_state channels[HAILO_PCI_OVER_VDMA_NUM_CHANNELS]; ++ u8 curr_channel_index; ++}; ++ ++struct hailo_pcie_fw_boot { ++ struct hailo_pcie_boot_dma_state boot_dma_state; ++ // is_in_boot is set to true when the board is in boot mode ++ bool is_in_boot; ++ // boot_used_channel_bitmap is a bitmap of the channels that are used for boot ++ u16 boot_used_channel_bitmap; ++ // fw_loaded_completion is used to notify that the FW was loaded - SOC & NNC ++ struct completion fw_loaded_completion; ++ // vdma_boot_completion is used to notify that the vDMA boot data was transferred completely on all used channels for boot ++ struct completion vdma_boot_completion; ++}; ++ + struct hailo_pcie_board { + struct list_head board_list; + struct pci_dev *pDev; +@@ -74,13 +108,15 @@ struct hailo_pcie_board { + struct hailo_pcie_nnc nnc; + struct hailo_pcie_soc soc; + struct hailo_pcie_driver_down_info driver_down; ++ struct hailo_pcie_soft_reset soft_reset; + struct semaphore mutex; + struct hailo_vdma_controller vdma; + ++ struct hailo_pcie_fw_boot fw_boot; ++ + struct hailo_memory_transfer_params memory_transfer_params; + u32 desc_max_page_size; + enum hailo_allocation_mode allocation_mode; +- struct completion fw_loaded_completion; + bool interrupts_enabled; + }; + +@@ -89,6 +125,7 @@ bool power_mode_enabled(void); + struct hailo_pcie_board* hailo_pcie_get_board_index(u32 index); + void hailo_disable_interrupts(struct hailo_pcie_board *board); + int hailo_enable_interrupts(struct hailo_pcie_board *board); ++int hailo_pcie_soft_reset(struct hailo_pcie_resources *resources, struct completion *reset_completed); + + #endif /* _HAILO_PCI_PCIE_H_ */ + +--- a/drivers/media/pci/hailo/src/soc.c ++++ b/drivers/media/pci/hailo/src/soc.c +@@ -12,12 +12,15 @@ + #include "vdma_common.h" + #include "utils/logs.h" + #include "vdma/memory.h" ++#include "pcie_common.h" + + #include + +-#define PCI_SOC_VDMA_ENGINE_INDEX (0) ++#ifndef HAILO_EMULATOR + #define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000) +-#define PCI_SOC_INPUT_CHANNEL_BITMASK (0x000000FF) ++#else ++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000000) ++#endif /* ifndef HAILO_EMULATOR */ + + void hailo_soc_init(struct hailo_pcie_soc *soc) + { +@@ -84,10 +87,9 @@ long hailo_soc_connect_ioctl(struct hail + struct hailo_soc_connect_params params; + struct hailo_vdma_channel *input_channel = NULL; + struct hailo_vdma_channel *output_channel = NULL; +- struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_VDMA_ENGINE_INDEX]; + struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL; + struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL; +- uint8_t depth = 0; + int err = 0; + + if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { +@@ -136,9 +138,8 @@ long hailo_soc_connect_ioctl(struct hail + } + + // 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, ++ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, input_descriptors_buffer->desc_list.desc_count, + 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); +@@ -149,9 +150,8 @@ long hailo_soc_connect_ioctl(struct hail + hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap); + + // configure and start output channel +- depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count); + // DMA Direction is only to get channel index - so +- err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth, ++ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, output_descriptors_buffer->desc_list.desc_count, + 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); +@@ -175,7 +175,7 @@ static int close_channels(struct hailo_p + { + struct hailo_pcie_soc_request request = {0}; + struct hailo_pcie_soc_response response = {0}; +- struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX]; ++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_VDMA_ENGINE_INDEX]; + struct hailo_vdma_channel *channel = NULL; + u8 channel_index = 0; + +--- a/drivers/media/pci/hailo/utils/compact.h ++++ b/drivers/media/pci/hailo/utils/compact.h +@@ -48,6 +48,14 @@ static inline long get_user_pages_compac + } + #endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) ++static inline void dma_sync_sgtable_for_device(struct device *dev, ++ struct sg_table *sgt, enum dma_data_direction dir) ++{ ++ dma_sync_sg_for_device(dev, sgt->sgl, sgt->orig_nents, dir); ++} ++#endif ++ + #ifndef _LINUX_MMAP_LOCK_H + static inline void mmap_read_lock(struct mm_struct *mm) + { +--- a/drivers/media/pci/hailo/vdma/memory.c ++++ b/drivers/media/pci/hailo/vdma/memory.c +@@ -14,10 +14,10 @@ + #include + #include + +- +-#define SGL_MAX_SEGMENT_SIZE (0x10000) + // See linux/mm.h + #define MMIO_AND_NO_PAGES_VMA_MASK (VM_IO | VM_PFNMAP) ++// The linux kernel names the dmabuf's vma vm_file field "dmabuf" ++#define VMA_VM_FILE_DMABUF_NAME ("dmabuf") + + static int map_mmio_address(uintptr_t user_address, u32 size, struct vm_area_struct *vma, + struct sg_table *sgt); +@@ -27,10 +27,16 @@ static void clear_sg_table(struct sg_tab + + #if LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) + +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) ++#define DMA_NS_NAME DMA_BUF ++#else ++#define DMA_NS_NAME "DMA_BUF" ++#endif // LINUX_VERSION_CODE < KERNEL_VERSION(6, 13, 0) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + // Import DMA_BUF namespace for needed kernels +-MODULE_IMPORT_NS(DMA_BUF); +-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) */ ++MODULE_IMPORT_NS(DMA_NS_NAME); ++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 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) +@@ -103,6 +109,39 @@ static void hailo_unmap_dmabuf(struct ha + + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION( 3, 3, 0 ) */ + ++// Function that checks if the vma is backed by a mapped dmabuf ++static bool is_dmabuf_vma(struct vm_area_struct *vma) ++{ ++ return (vma && vma->vm_file && (0 == strcmp(vma->vm_file->f_path.dentry->d_name.name, VMA_VM_FILE_DMABUF_NAME))); ++} ++ ++static int create_fd_from_vma(struct device *dev, struct vm_area_struct *vma) { ++ struct file *file = NULL; ++ int fd = 0; ++ ++ if (!vma || !vma->vm_file) { ++ dev_err(dev, "Invalid VMA or no associated file.\n"); ++ return -EINVAL; ++ } ++ ++ file = vma->vm_file; ++ ++ // This functions increments the ref count of the file ++ get_file(file); ++ ++ // 0 for default flags ++ fd = get_unused_fd_flags(0); ++ if (fd < 0) { ++ dev_err(dev, "Failed to get unused file descriptor.\n"); ++ fput(file); ++ return fd; ++ } ++ ++ // Install the file into the file descriptor table ++ fd_install(fd, file); ++ return fd; ++} ++ + 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) +@@ -112,7 +151,8 @@ struct hailo_vdma_buffer *hailo_vdma_buf + struct sg_table sgt = {0}; + struct vm_area_struct *vma = NULL; + bool is_mmio = false; +- struct hailo_dmabuf_info dmabuf_info = {0}; ++ struct hailo_dmabuf_info dmabuf_info = {0}; ++ bool created_dmabuf_fd_from_vma = false; + + mapped_buffer = kzalloc(sizeof(*mapped_buffer), GFP_KERNEL); + if (NULL == mapped_buffer) { +@@ -121,12 +161,27 @@ struct hailo_vdma_buffer *hailo_vdma_buf + goto cleanup; + } + +- if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING) && (HAILO_DMA_DMABUF_BUFFER != buffer_type)) { ++ if (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", user_address, size); +- ret = -EFAULT; +- goto cleanup; ++ if (IS_ENABLED(HAILO_SUPPORT_MMIO_DMA_MAPPING)) { ++ if (NULL == vma) { ++ dev_err(dev, "no vma for virt_addr/size = 0x%08lx/0x%08zx\n", user_address, size); ++ ret = -EFAULT; ++ goto cleanup; ++ } ++ } ++ ++ if (is_dmabuf_vma(vma)) { ++ dev_dbg(dev, "Given vma is backed by dmabuf - creating fd and mapping as dmabuf\n"); ++ buffer_type = HAILO_DMA_DMABUF_BUFFER; ++ ret = create_fd_from_vma(dev, vma); ++ if (ret < 0) { ++ dev_err(dev, "Failed creating fd from vma in given dmabuf\n"); ++ goto cleanup; ++ } ++ // Override user address with fd to the dmabuf - like normal dmabuf flow ++ user_address = ret; ++ created_dmabuf_fd_from_vma = true; + } + } + +@@ -157,6 +212,11 @@ struct hailo_vdma_buffer *hailo_vdma_buf + dev_err(dev, "Failed mapping dmabuf\n"); + goto cleanup; + } ++ // If created dmabuf fd from vma need to decrement refcount and release fd ++ if (created_dmabuf_fd_from_vma) { ++ fput(vma->vm_file); ++ put_unused_fd(user_address); ++ } + } else { + // user_address is a standard 'struct page' backed memory address + ret = prepare_sg_table(&sgt, user_address, size, low_mem_driver_allocated_buffer); +@@ -331,7 +391,7 @@ int hailo_desc_list_create(struct device + dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory " + "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n", + descriptors_count, buffer_size); +- return -ENOMEM; ++ return -ENOBUFS; + } + + descriptors->buffer_size = buffer_size; +@@ -467,7 +527,7 @@ int hailo_vdma_continuous_buffer_alloc(s + if (NULL == kernel_address) { + dev_warn(dev, "Failed to allocate continuous buffer, size 0x%zx. This failure means there is not a sufficient amount of CMA memory " + "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n", size); +- return -ENOMEM; ++ return -ENOBUFS; + } + + continuous_buffer->kernel_address = kernel_address; +--- a/drivers/media/pci/hailo/vdma/memory.h ++++ b/drivers/media/pci/hailo/vdma/memory.h +@@ -11,6 +11,8 @@ + + #include "vdma/vdma.h" + ++#define SGL_MAX_SEGMENT_SIZE (0x10000) ++ + 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); diff --git a/target/linux/bcm27xx/patches-6.6/950-1526-overlays-Regularisation-and-improvements.patch b/target/linux/bcm27xx/patches-6.6/950-1526-overlays-Regularisation-and-improvements.patch new file mode 100644 index 000000000..c763b1d27 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1526-overlays-Regularisation-and-improvements.patch @@ -0,0 +1,139 @@ +From fa71765fd3c98fa170407e72e052004913da6874 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 27 Jan 2025 14:17:46 +0000 +Subject: [PATCH] overlays: Regularisation and improvements + +A few small improvements, with a view to making the updated overlays +behave the same before and after the big conversion. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/ads1115-overlay.dts | 3 ++- + arch/arm/boot/dts/overlays/i2c-fan-overlay.dts | 4 ++-- + arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts | 3 ++- + arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts | 3 ++- + arch/arm/boot/dts/overlays/mcp23017-overlay.dts | 3 ++- + arch/arm/boot/dts/overlays/pca953x-overlay.dts | 2 -- + arch/arm/boot/dts/overlays/pcf857x-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts | 2 +- + 9 files changed, 13 insertions(+), 11 deletions(-) + +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -120,7 +120,8 @@ + chd_cfg = <&channel_d>,"reg:0"; + chd_gain = <&channel_d>,"ti,gain:0"; + chd_datarate = <&channel_d>,"ti,datarate:0"; +- i2c0 = <&frag100>, "target:0=",<&i2c0>; ++ i2c0 = <&frag100>, "target:0=",<&i2c0>, ++ <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; + i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -17,7 +17,6 @@ + emc2301: emc2301@2f { + compatible = "microchip,emc2301"; + reg = <0x2f>; +- status = "okay"; + #cooling-cells = <0x02>; + }; + }; +@@ -82,7 +81,8 @@ + }; + + __overrides__ { +- i2c0 = <&frag100>,"target:0=",<&i2c0>; ++ i2c0 = <&frag100>,"target:0=",<&i2c0>, ++ <0>,"+101+102"; + i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; + i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -27,7 +27,8 @@ + }; + + __overrides__ { +- i2c0 = <&frag100>, "target:0=",<&i2c0>; ++ i2c0 = <&frag100>, "target:0=",<&i2c0>, ++ <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; + i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -27,7 +27,8 @@ + }; + + __overrides__ { +- i2c0 = <&frag100>, "target:0=",<&i2c0>; ++ i2c0 = <&frag100>, "target:0=",<&i2c0>, ++ <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; + i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -87,7 +87,8 @@ + addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0"; + mcp23008 = <0>,"=2"; + noints = <0>,"!1!3"; +- i2c0 = <&frag100>, "target:0=",<&i2c0>; ++ i2c0 = <&frag100>, "target:0=",<&i2c0>, ++ <0>,"+101+102"; + i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, + <0>,"+101+102"; + i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -17,8 +17,6 @@ + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; +- +- status = "okay"; + }; + }; + }; +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -11,13 +11,13 @@ + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; ++ status = "okay"; + + pcf857x: pcf857x@0 { + compatible = ""; + reg = <0x00>; + gpio-controller; + #gpio-cells = <2>; +- status = "okay"; + }; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -27,7 +27,7 @@ + }; + + fragment@1 { +- target-path = "/"; ++ target = <&clocks>; + __overlay__ { + sc16is750_clk: sc16is750_i2c_clk@48 { + compatible = "fixed-clock"; +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -27,7 +27,7 @@ + }; + + fragment@1 { +- target-path = "/"; ++ target = <&clocks>; + __overlay__ { + sc16is752_clk: sc16is752_i2c_clk@48 { + compatible = "fixed-clock"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1527-overlays-Factor-out-the-common-i2c-bus-selection.patch b/target/linux/bcm27xx/patches-6.6/950-1527-overlays-Factor-out-the-common-i2c-bus-selection.patch new file mode 100644 index 000000000..504e46376 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1527-overlays-Factor-out-the-common-i2c-bus-selection.patch @@ -0,0 +1,1630 @@ +From a320d39840745502d420f56b3d49b3723a12058f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Sun, 26 Jan 2025 22:08:16 +0000 +Subject: [PATCH] overlays: Factor out the common i2c bus selection + +Create an i2c-buses.dtsi to hold all of the common I2C bus selection +logic, and refactor existing overlays to use it. This patch should +have no functional change overall except to increase the range of +options for some overlays. + +There is a slightly ugly mechanism for overriding the default bus, where +the mux nodes may or may not need to be enabled. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/README | 264 ++++------------- + .../arm/boot/dts/overlays/ads1115-overlay.dts | 39 +-- + .../boot/dts/overlays/edt-ft5406-overlay.dts | 39 +-- + arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 5 +- + arch/arm/boot/dts/overlays/i2c-buses.dtsi | 67 +++++ + .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 45 +-- + .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 42 +-- + .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 41 +-- + .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 43 +-- + .../boot/dts/overlays/i2c-sensor-overlay.dts | 43 +-- + .../boot/dts/overlays/mcp23017-overlay.dts | 47 +-- + .../arm/boot/dts/overlays/pca953x-overlay.dts | 277 +++--------------- + .../arm/boot/dts/overlays/pcf857x-overlay.dts | 38 +-- + .../dts/overlays/sc16is750-i2c-overlay.dts | 37 +-- + .../dts/overlays/sc16is752-i2c-overlay.dts | 37 +-- + .../overlays/seeed-can-fd-hat-v2-overlay.dts | 9 +- + .../overlays/vc4-kms-dsi-7inch-overlay.dts | 17 +- + .../vc4-kms-dsi-waveshare-800x480-overlay.dts | 15 +- + 18 files changed, 202 insertions(+), 903 deletions(-) + create mode 100644 arch/arm/boot/dts/overlays/i2c-buses.dtsi + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -568,6 +568,8 @@ Load: dtoverlay=ads1115,[= + Params: addr I2C bus address of device. Set based on how the + addr pin is wired. (default=0x48 assumes addr + is pulled to GND) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + cha_enable Enable virtual channel a. + cha_cfg Set the configuration for virtual channel a. + (default=4 configures this channel for the +@@ -577,22 +579,6 @@ Params: addr I2C bus + cha_gain Set the gain of the Programmable Gain + Amplifier for this channel. (Default 1 sets the + full scale of the channel to 4.096 Volts) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C4 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c5 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + Channel parameters can be set for each enabled channel. + A maximum of 4 channels can be enabled (letters a thru d). +@@ -1263,20 +1249,11 @@ Params: sizex Touchscr + invx Touchscreen inverted x axis + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c1 Choose the I2C1 bus on GPIOs 2&3 +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C4 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c5 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) + addr Sets the address for the touch controller. Note + that the device must be configured to use the + specified address. +- i2c-path Override I2C path to allow for i2c-gpio buses ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + + Name: enc28j60 +@@ -2114,37 +2091,41 @@ Load: dtoverlay=i2c-bcm2708 + Params: + + +-Name: i2c-fan +-Info: Adds support for a number of I2C fan controllers +-Load: dtoverlay=i2c-fan,= +-Params: addr Sets the address for the fan controller. Note +- that the device must be configured to use the +- specified address. +- +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- ++Name: i2c-bus ++Info: This is not a real overlay. Many overlays support the use of a variety ++ of I2C buses, and this is where the relevant parameters are documented. ++Load: ++Params: i2c0 Choose the I2C0 bus on GPIOs 0&1 ++ i2c1 Choose the I2C1 bus on GPIOs 2&3 ++ i2c2 Choose the I2C2 bus (configure with the i2c2 ++ overlay - BCM2711 only) + i2c3 Choose the I2C3 bus (configure with the i2c3 + overlay - BCM2711 only) +- + i2c4 Choose the I2C4 bus (configure with the i2c4 + overlay - BCM2711 only) +- + i2c5 Choose the I2C5 bus (configure with the i2c5 + overlay - BCM2711 only) +- + i2c6 Choose the I2C6 bus (configure with the i2c6 + overlay - BCM2711 only) +- ++ i2c_csi_dsi Choose the I2C bus connected to the main ++ camera/display connector. ++ See "dtparam -h i2c_csi_dsi" for details. ++ i2c_csi_dsi0 Choose the I2C bus connected to the second ++ camera/display connector, if present. ++ See "dtparam -h i2c_csi_dsi0" for details. + i2c-path Override I2C path to allow for i2c-gpio buses + ++ ++Name: i2c-fan ++Info: Adds support for a number of I2C fan controllers ++Load: dtoverlay=i2c-fan,= ++Params: addr Sets the address for the fan controller. Note ++ that the device must be configured to use the ++ specified address. ++ ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + minpwm PWM setting for the fan when the SoC is below + mintemp (range 0-255. default 0) + maxpwm PWM setting for the fan when the SoC is above +@@ -2196,33 +2177,12 @@ Params: pca9542 Select t + + addr Change I2C address of the device (default 0x70) + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + base Set an explicit base value for the channel bus + numbers + +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- +- i2c-path Override I2C path to allow for i2c-gpio buses +- + disconnect_on_idle Force the mux to disconnect all child buses + after every transaction. + +@@ -2234,22 +2194,8 @@ Name: i2c-pwm-pca9685a + Info: Adds support for an NXP PCA9685A I2C PWM controller on i2c_arm + Load: dtoverlay=i2c-pwm-pca9685a,= + Params: addr I2C address of PCA9685A (default 0x40) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + + Name: i2c-rtc +@@ -2303,29 +2249,8 @@ Params: abx80x Select o + + s35390a Select the ABLIC S35390A device + +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- +- i2c-path Override I2C path to allow for i2c-gpio buses ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + + addr Sets the address for the RTC. Note that the + device must be configured to use the specified +@@ -2451,6 +2376,9 @@ Params: addr Set the + JC42, LM75, MCP980x, MPU6050, MPU9250, MS5637, + MS5803, MS5805, MS5837, MS8607, SHT3x or TMP102 + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + adt7410 Select the Analog Devices ADT7410 and ADT7420 + temperature sensors + Valid address 0x48-0x4b, default 0x48 +@@ -2591,29 +2519,6 @@ Params: addr Set the + veml6070 Select the Vishay VEML6070 ultraviolet light + sensor + +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- +- i2c-path Override I2C path to allow for i2c-gpio buses +- + + Name: i2c0 + Info: Change i2c0 pin usage. Not all pin combinations are usable on all +@@ -3255,24 +3160,11 @@ Params: gpiopin Gpio pin + + addr I2C address of the MCP23017 (default: 0x20) + ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" ++ + mcp23008 Configure an MCP23008 instead. + noints Disable the interrupt GPIO line. +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C4 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c5 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: mcp23s17 +@@ -3692,6 +3584,8 @@ Name: pca953x + Info: TI PCA953x family of I2C GPIO expanders. Default is for NXP PCA9534. + Load: dtoverlay=pca953x,= + Params: addr I2C address of expander. Default 0x20. ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + pca6416 Select the NXP PCA6416 (16 bit) + pca9505 Select the NXP PCA9505 (40 bit) + pca9535 Select the NXP PCA9535 (16 bit) +@@ -3722,22 +3616,6 @@ Params: addr I2C addr + cat9554 Select the Onnn CAT9554 (8 bit) + pca9654 Select the Onnn PCA9654 (8 bit) + xra1202 Select the Exar XRA1202 (8 bit) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcf857x +@@ -3745,26 +3623,12 @@ Info: NXP PCF857x family of I2C GPIO e + Load: dtoverlay=pcf857x,= + Params: addr I2C address of expander. Default + depends on model selected. ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + pcf8574 Select the NXP PCF8574 (8 bit) + pcf8574a Select the NXP PCF8574A (8 bit) + pcf8575 Select the NXP PCF8575 (16 bit) + pca8574 Select the NXP PCA8574 (8 bit) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: pcie-32bit-dma +@@ -4463,23 +4327,9 @@ Info: Overlay for the NXP SC16IS750 UA + Load: dtoverlay=sc16is750-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + xtal On-board crystal frequency (default 14745600) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C4 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c5 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is750-spi0 +@@ -4497,23 +4347,9 @@ Info: Overlay for the NXP SC16IS752 du + Load: dtoverlay=sc16is752-i2c,= + Params: int_pin GPIO used for IRQ (default 24) + addr Address (default 0x48) ++ i2c-bus Supports all the standard I2C bus selection ++ parameters - see "dtoverlay -h i2c-bus" + xtal On-board crystal frequency (default 14745600) +- i2c0 Choose the I2C0 bus on GPIOs 0&1 +- i2c_csi_dsi Choose the I2C bus connected to the main +- camera/display connector. +- See "dtparam -h i2c_csi_dsi" for details. +- i2c_csi_dsi0 Choose the I2C bus connected to the second +- camera/display connector, if present. +- See "dtparam -h i2c_csi_dsi0" for details. +- i2c3 Choose the I2C3 bus (configure with the i2c3 +- overlay - BCM2711 only) +- i2c4 Choose the I2C4 bus (configure with the i2c4 +- overlay - BCM2711 only) +- i2c5 Choose the I2C5 bus (configure with the i2c5 +- overlay - BCM2711 only) +- i2c6 Choose the I2C6 bus (configure with the i2c6 +- overlay - BCM2711 only) +- i2c-path Override I2C path to allow for i2c-gpio buses + + + Name: sc16is752-spi0 +--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts +@@ -5,6 +5,8 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +@@ -81,27 +83,6 @@ + }; + }; + +- frag100: fragment@100 { +- target = <&i2c1>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + addr = <&ads1115>,"reg:0"; + cha_enable = <0>,"=0"; +@@ -120,21 +101,5 @@ + chd_cfg = <&channel_d>,"reg:0"; + chd_gain = <&channel_d>,"ti,gain:0"; + chd_datarate = <&channel_d>,"ti,datarate:0"; +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts ++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts +@@ -7,43 +7,16 @@ + /dts-v1/; + /plugin/; + ++#define ENABLE_I2C0_MUX ++#include "i2c-buses.dtsi" + #include "edt-ft5406.dtsi" + +-/ { +- fragment@0 { +- target = <&i2c0if>; +- __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@1 { +- target = <&i2c0mux>; +- __overlay__ { +- status = "okay"; +- }; +- }; ++&busfrag { ++ target = <&i2c_csi_dsi>; ++}; + ++/ { + __overrides__ { +- i2c0 = <&ts_i2c_frag>,"target:0=",<&i2c0>; +- i2c1 = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path=i2c1", +- <0>,"-0-1"; +- i2c3 = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path=i2c3", +- <0>,"-0-1"; +- i2c4 = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path=i2c4", +- <0>,"-0-1"; +- i2c5 = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path=i2c5", +- <0>,"-0-1"; +- i2c6 = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path=i2c6", +- <0>,"-0-1"; +- i2c-path = <&ts_i2c_frag>, "target?=0", +- <&ts_i2c_frag>, "target-path", +- <0>,"-0-1"; + addr = <&ft5406>,"reg:0"; + }; + }; +--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi ++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi +@@ -22,12 +22,11 @@ + }; + }; + +- ts_i2c_frag: fragment@12 { +- target = <&i2c_csi_dsi>; ++ fragment@12 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + ft5406: ts@38 { + compatible = "edt,edt-ft5506"; +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/i2c-buses.dtsi +@@ -0,0 +1,67 @@ ++// Common i2c buses, and dtparams to select them ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ ++ busfrag: fragment@100 { ++ target = <&i2c_arm>; ++ i2cbus: __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@101 { ++ target = <&i2c0if>; ++#ifdef ENABLE_I2C0_MUX ++ __overlay__ { ++#else ++ __dormant__ { ++#endif ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@102 { ++ target = <&i2c0mux>; ++#ifdef ENABLE_I2C0_MUX ++ __overlay__ { ++#else ++ __dormant__ { ++#endif ++ status = "okay"; ++ }; ++ }; ++ ++ __overrides__ { ++ i2c0 = <&busfrag>,"target:0=",<&i2c0>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c_csi_dsi = <&busfrag>,"target:0=",<&i2c_csi_dsi>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c_csi_dsi0 = <&busfrag>, "target:0=",<&i2c_csi_dsi0>, ++ <&busfrag>, "target-path?=0", ++ <0>,"+101+102"; ++ i2c1 = <&busfrag>,"target:0=",<&i2c1>, ++ <&busfrag>, "target-path?=0", ++ <0>,"-101-102"; ++ i2c2 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c2", ++ <0>,"-101-102"; ++ i2c3 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c3", ++ <0>,"-101-102"; ++ i2c4 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c4", ++ <0>,"-101-102"; ++ i2c5 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c5", ++ <0>,"-101-102"; ++ i2c6 = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path=i2c6", ++ <0>,"-101-102"; ++ i2c-path = <&busfrag>, "target?=0", ++ <&busfrag>, "target-path", ++ <0>,"-101-102"; ++ }; ++}; +--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts +@@ -4,6 +4,8 @@ + + #include + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +@@ -22,35 +24,14 @@ + }; + }; + +- frag100: fragment@100 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@103 { ++ fragment@1 { + target = <&cpu_thermal>; + __overlay__ { + polling-delay = <2000>; /* milliseconds */ + }; + }; + +- fragment@104 { ++ fragment@2 { + target = <&thermal_trips>; + __overlay__ { + fanmid0: fanmid0 { +@@ -66,7 +47,7 @@ + }; + }; + +- fragment@105 { ++ fragment@3 { + target = <&cooling_maps>; + __overlay__ { + map0: map0 { +@@ -81,22 +62,6 @@ + }; + + __overrides__ { +- i2c0 = <&frag100>,"target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; + addr = <&emc2301>,"reg:0"; + minpwm = <&emc2301>,"emc2305,pwm-min.0"; + maxpwm = <&emc2301>,"emc2305,pwm-max.0"; +--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts +@@ -5,6 +5,8 @@ + + #include + ++#include "i2c-buses.dtsi" ++ + /{ + compatible = "brcm,bcm2835"; + +@@ -13,7 +15,6 @@ + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + pca9542: mux@70 { + compatible = "nxp,pca9542"; +@@ -40,7 +41,6 @@ + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + pca9545: mux@70 { + compatible = "nxp,pca9545"; +@@ -77,7 +77,6 @@ + __dormant__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + pca9548: mux@70 { + compatible = "nxp,pca9548"; +@@ -129,27 +128,6 @@ + }; + }; + +- frag100: fragment@100 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + pca9542 = <0>, "+0"; + pca9545 = <0>, "+1"; +@@ -163,22 +141,6 @@ + <&pca9545>,"base-nr:0", + <&pca9548>,"base-nr:0"; + +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; + disconnect_on_idle = + <&pca9542>,"idle-state:0=", , + <&pca9545>,"idle-state:0=", , +--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts +@@ -2,6 +2,8 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + /{ + compatible = "brcm,bcm2835"; + +@@ -10,7 +12,6 @@ + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + pca: pca@40 { + compatible = "nxp,pca9685-pwm"; +@@ -21,45 +22,7 @@ + }; + }; + +- +- frag100: fragment@100 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + addr = <&pca>,"reg:0"; +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts +@@ -3,45 +3,4 @@ + /plugin/; + + #include "i2c-rtc-common.dtsi" +- +-/ { +- frag100: fragment@100 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- __overrides__ { +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; +- }; +-}; ++#include "i2c-buses.dtsi" +--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts ++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts +@@ -3,45 +3,4 @@ + /plugin/; + + #include "i2c-sensor-common.dtsi" +- +-/ { +- frag100: fragment@100 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- __overrides__ { +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; +- }; +-}; ++#include "i2c-buses.dtsi" +--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts ++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts +@@ -3,16 +3,11 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +- fragment@0 { +- target = <&i2cbus>; +- __overlay__ { +- status = "okay"; +- }; +- }; +- + fragment@1 { + target = <&gpio>; + __overlay__ { +@@ -60,49 +55,11 @@ + }; + }; + +- frag100: fragment@100 { +- target = <&i2c1>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@102 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + gpiopin = <&mcp23017_pins>,"brcm,pins:0", + <&mcp23017_irq>,"interrupts:0"; + addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0"; + mcp23008 = <0>,"=2"; + noints = <0>,"!1!3"; +- i2c0 = <&frag100>, "target:0=",<&i2c0>, +- <0>,"+101+102"; +- i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+101+102"; +- i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+101+102"; +- i2c3 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c3"; +- i2c4 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c4"; +- i2c5 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c5"; +- i2c6 = <&frag100>, "target?=0", +- <&frag100>, "target-path=i2c6"; +- i2c-path = <&frag100>, "target?=0", +- <&frag100>, "target-path"; + }; + }; +- +--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts +@@ -2,11 +2,13 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + /{ + compatible = "brcm,bcm2835"; + +- frag0: fragment@0 { +- target = <&i2c_arm>; ++ fragment@0 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -21,248 +23,37 @@ + }; + }; + +- fragment@1 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca6416"; +- }; +- }; +- fragment@2 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9505"; +- }; +- }; +- fragment@3 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9535"; +- }; +- }; +- fragment@4 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9536"; +- }; +- }; +- fragment@5 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9537"; +- }; +- }; +- fragment@6 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9538"; +- }; +- }; +- fragment@7 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9539"; +- }; +- }; +- fragment@8 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9554"; +- }; +- }; +- fragment@9 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9555"; +- }; +- }; +- fragment@10 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9556"; +- }; +- }; +- fragment@11 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9557"; +- }; +- }; +- fragment@12 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9574"; +- }; +- }; +- fragment@13 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9575"; +- }; +- }; +- fragment@14 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pca9698"; +- }; +- }; +- fragment@15 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pcal6416"; +- }; +- }; +- fragment@16 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pcal6524"; +- }; +- }; +- fragment@17 { +- target = <&pca>; +- __dormant__ { +- compatible = "nxp,pcal9555a"; +- }; +- }; +- fragment@18 { +- target = <&pca>; +- __dormant__ { +- compatible = "maxim,max7310"; +- }; +- }; +- fragment@19 { +- target = <&pca>; +- __dormant__ { +- compatible = "maxim,max7312"; +- }; +- }; +- fragment@20 { +- target = <&pca>; +- __dormant__ { +- compatible = "maxim,max7313"; +- }; +- }; +- fragment@21 { +- target = <&pca>; +- __dormant__ { +- compatible = "maxim,max7315"; +- }; +- }; +- fragment@22 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,pca6107"; +- }; +- }; +- fragment@23 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,tca6408"; +- }; +- }; +- fragment@24 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,tca6416"; +- }; +- }; +- fragment@25 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,tca6424"; +- }; +- }; +- fragment@26 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,tca9539"; +- }; +- }; +- fragment@27 { +- target = <&pca>; +- __dormant__ { +- compatible = "ti,tca9554"; +- }; +- }; +- fragment@28 { +- target = <&pca>; +- __dormant__ { +- compatible = "onnn,cat9554"; +- }; +- }; +- fragment@29 { +- target = <&pca>; +- __dormant__ { +- compatible = "onnn,pca9654"; +- }; +- }; +- fragment@30 { +- target = <&pca>; +- __dormant__ { +- compatible = "exar,xra1202"; +- }; +- }; +- +- fragment@100 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + addr = <&pca>,"reg:0"; +- pca6416 = <0>, "+1"; +- pca9505 = <0>, "+2"; +- pca9535 = <0>, "+3"; +- pca9536 = <0>, "+4"; +- pca9537 = <0>, "+5"; +- pca9538 = <0>, "+6"; +- pca9539 = <0>, "+7"; +- pca9554 = <0>, "+8"; +- pca9555 = <0>, "+9"; +- pca9556 = <0>, "+10"; +- pca9557 = <0>, "+11"; +- pca9574 = <0>, "+12"; +- pca9575 = <0>, "+13"; +- pca9698 = <0>, "+14"; +- pcal6416 = <0>, "+15"; +- pcal6524 = <0>, "+16"; +- pcal9555a = <0>, "+17"; +- max7310 = <0>, "+18"; +- max7312 = <0>, "+19"; +- max7313 = <0>, "+20"; +- max7315 = <0>, "+21"; +- pca6107 = <0>, "+22"; +- tca6408 = <0>, "+23"; +- tca6416 = <0>, "+24"; +- tca6424 = <0>, "+25"; +- tca9539 = <0>, "+26"; +- tca9554 = <0>, "+27"; +- cat9554 = <0>, "+28"; +- pca9654 = <0>, "+29"; +- xra1202 = <0>, "+30"; +- i2c0 = <&frag0>, "target:0=",<&i2c0>, +- <0>,"+100+101"; +- i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+100+101"; +- i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+100+101"; +- i2c3 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c3"; +- i2c4 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c4"; +- i2c5 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c5"; +- i2c6 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c6"; +- i2c-path = <&frag0>, "target?=0", +- <&frag0>, "target-path"; ++ pca6416 = <&pca>,"compatible=nxp,pca6416"; ++ pca9505 = <&pca>,"compatible=nxp,pca9505"; ++ pca9535 = <&pca>,"compatible=nxp,pca9535"; ++ pca9536 = <&pca>,"compatible=nxp,pca9536"; ++ pca9537 = <&pca>,"compatible=nxp,pca9537"; ++ pca9538 = <&pca>,"compatible=nxp,pca9538"; ++ pca9539 = <&pca>,"compatible=nxp,pca9539"; ++ pca9554 = <&pca>,"compatible=nxp,pca9554"; ++ pca9555 = <&pca>,"compatible=nxp,pca9555"; ++ pca9556 = <&pca>,"compatible=nxp,pca9556"; ++ pca9557 = <&pca>,"compatible=nxp,pca9557"; ++ pca9574 = <&pca>,"compatible=nxp,pca9574"; ++ pca9575 = <&pca>,"compatible=nxp,pca9575"; ++ pca9698 = <&pca>,"compatible=nxp,pca9698"; ++ pcal6416 = <&pca>,"compatible=nxp,pcal6416"; ++ pcal6524 = <&pca>,"compatible=nxp,pcal6524"; ++ pcal9555a = <&pca>,"compatible=nxp,pcal9555a"; ++ max7310 = <&pca>,"compatible=maxim,max7310"; ++ max7312 = <&pca>,"compatible=maxim,max7312"; ++ max7313 = <&pca>,"compatible=maxim,max7313"; ++ max7315 = <&pca>,"compatible=maxim,max7315"; ++ pca6107 = <&pca>,"compatible=ti,pca6107"; ++ tca6408 = <&pca>,"compatible=ti,tca6408"; ++ tca6416 = <&pca>,"compatible=ti,tca6416"; ++ tca6424 = <&pca>,"compatible=ti,tca6424"; ++ tca9539 = <&pca>,"compatible=ti,tca9539"; ++ tca9554 = <&pca>,"compatible=ti,tca9554"; ++ cat9554 = <&pca>,"compatible=onnn,cat9554"; ++ pca9654 = <&pca>,"compatible=onnn,pca9654"; ++ xra1202 = <&pca>,"compatible=exar,xra1202"; + }; + }; +--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts +@@ -3,11 +3,13 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +- frag0: fragment@0 { +- target = <&i2c_arm>; ++ fragment@0 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +@@ -22,41 +24,11 @@ + }; + }; + +- fragment@100 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + pcf8574 = <&pcf857x>,"compatible=nxp,pcf8574", <&pcf857x>,"reg:0=0x20"; + pcf8574a = <&pcf857x>,"compatible=nxp,pcf8574a", <&pcf857x>,"reg:0=0x38"; + pcf8575 = <&pcf857x>,"compatible=nxp,pcf8575", <&pcf857x>,"reg:0=0x20"; +- pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; ++ pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20"; + addr = <&pcf857x>,"reg:0"; +- i2c0 = <&frag0>, "target:0=",<&i2c0>, +- <0>,"+100+101"; +- i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+100+101"; +- i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+100+101"; +- i2c3 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c3"; +- i2c4 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c4"; +- i2c5 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c5"; +- i2c6 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c6"; +- i2c-path = <&frag0>, "target?=0", +- <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts +@@ -1,15 +1,16 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +- frag0: fragment@0 { +- target = <&i2c_arm>; ++ fragment@0 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + sc16is750: sc16is750@48 { + compatible = "nxp,sc16is750"; +@@ -48,40 +49,10 @@ + }; + }; + +- fragment@100 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + int_pin = <&sc16is750>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is750>,"reg:0", <&sc16is750_clk>,"name"; + xtal = <&sc16is750_clk>,"clock-frequency:0"; +- i2c0 = <&frag0>, "target:0=",<&i2c0>, +- <0>,"+100+101"; +- i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+100+101"; +- i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+100+101"; +- i2c3 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c3"; +- i2c4 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c4"; +- i2c5 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c5"; +- i2c6 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c6"; +- i2c-path = <&frag0>, "target?=0", +- <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts +@@ -1,15 +1,16 @@ + /dts-v1/; + /plugin/; + ++#include "i2c-buses.dtsi" ++ + / { + compatible = "brcm,bcm2835"; + +- frag0: fragment@0 { +- target = <&i2c_arm>; ++ fragment@0 { ++ target = <&i2cbus>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; +- status = "okay"; + + sc16is752: sc16is752@48 { + compatible = "nxp,sc16is752"; +@@ -48,40 +49,10 @@ + }; + }; + +- fragment@100 { +- target = <&i2c0if>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- +- fragment@101 { +- target = <&i2c0mux>; +- __dormant__ { +- status = "okay"; +- }; +- }; +- + __overrides__ { + int_pin = <&sc16is752>,"interrupts:0", <&int_pins>,"brcm,pins:0", + <&int_pins>,"reg:0"; + addr = <&sc16is752>,"reg:0",<&sc16is752_clk>,"name"; + xtal = <&sc16is752_clk>,"clock-frequency:0"; +- i2c0 = <&frag0>, "target:0=",<&i2c0>, +- <0>,"+100+101"; +- i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>, +- <0>,"+100+101"; +- i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>, +- <0>,"+100+101"; +- i2c3 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c3"; +- i2c4 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c4"; +- i2c5 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c5"; +- i2c6 = <&frag0>, "target?=0", +- <&frag0>, "target-path=i2c6"; +- i2c-path = <&frag0>, "target?=0", +- <&frag0>, "target-path"; + }; + }; +--- a/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts ++++ b/arch/arm/boot/dts/overlays/seeed-can-fd-hat-v2-overlay.dts +@@ -98,8 +98,9 @@ + }; + }; + fragment@8 { +- target = <&i2cbus>; ++ target = <&i2c_arm>; + __overlay__ { ++ status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + pcf85063@51 { +@@ -108,10 +109,4 @@ + }; + }; + }; +- fragment@9 { +- target = <&i2c_arm>; +- i2cbus: __overlay__ { +- status = "okay"; +- }; +- }; + }; +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts +@@ -1,4 +1,4 @@ +-/* ++ /* + * Device Tree overlay for RaspberryPi 7" Touchscreen panel + * + */ +@@ -8,6 +8,11 @@ + + #include "edt-ft5406.dtsi" + ++&ft5406 { ++ vcc-supply = <®_display>; ++ reset-gpio = <®_display 1 1>; ++}; ++ + / { + /* No compatible as it will have come from edt-ft5406.dtsi */ + +@@ -77,7 +82,7 @@ + + i2c_frag: fragment@2 { + target = <&i2c_csi_dsi>; +- __overlay__ { ++ i2cbus: __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; +@@ -104,18 +109,10 @@ + 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>, + <&panel_disp>, "reg:0=0", + <®_bridge>, "reg:0=0", + <®_bridge>, "regulator-name=bridge_reg_0"; +--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-800x480-overlay.dts +@@ -9,6 +9,11 @@ + + #include "edt-ft5406.dtsi" + ++&ft5406 { ++ vcc-supply = <®_display>; ++ reset-gpio = <®_display 1 1>; ++}; ++ + / { + /* No compatible as it will have come from edt-ft5406.dtsi */ + +@@ -73,7 +78,7 @@ + + i2c_frag: fragment@2 { + target = <&i2c_csi_dsi>; +- __overlay__ { ++ i2cbus: __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; +@@ -100,18 +105,10 @@ + 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"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1529-misc-rp1-pio-SM_CONFIG_XFER32-larger-DMA-bufs.patch b/target/linux/bcm27xx/patches-6.6/950-1529-misc-rp1-pio-SM_CONFIG_XFER32-larger-DMA-bufs.patch new file mode 100644 index 000000000..a084ec698 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1529-misc-rp1-pio-SM_CONFIG_XFER32-larger-DMA-bufs.patch @@ -0,0 +1,63 @@ +From 8a08b4ad6dbd48a826b3052e52a4fdc88c3ac36e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 30 Jan 2025 15:26:39 +0000 +Subject: [PATCH] misc: rp1-pio: SM_CONFIG_XFER32 = larger DMA bufs + +Add an ioctl type - SM_CONFIG_XFER32 - that takes uints for the buf_size +and buf_count values. + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 9 +++++++++ + include/uapi/misc/rp1_pio_if.h | 8 ++++++++ + 2 files changed, 17 insertions(+) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -710,6 +710,14 @@ static int rp1_pio_sm_config_xfer_user(s + args->buf_size, args->buf_count); + } + ++static int rp1_pio_sm_config_xfer32_user(struct rp1_pio_client *client, void *param) ++{ ++ struct rp1_pio_sm_config_xfer32_args *args = param; ++ ++ return rp1_pio_sm_config_xfer_internal(client, args->sm, args->dir, ++ args->buf_size, args->buf_count); ++} ++ + static int rp1_pio_sm_tx_user(struct rp1_pio_device *pio, struct dma_info *dma, + const void __user *userbuf, size_t bytes) + { +@@ -970,6 +978,7 @@ struct handler_info { + HANDLER(SM_CONFIG_XFER, sm_config_xfer_user), + HANDLER(SM_XFER_DATA, sm_xfer_data_user), + HANDLER(SM_XFER_DATA32, sm_xfer_data32_user), ++ HANDLER(SM_CONFIG_XFER32, sm_config_xfer32_user), + + HANDLER(CAN_ADD_PROGRAM, can_add_program), + HANDLER(ADD_PROGRAM, add_program), +--- a/include/uapi/misc/rp1_pio_if.h ++++ b/include/uapi/misc/rp1_pio_if.h +@@ -160,6 +160,13 @@ struct rp1_pio_sm_config_xfer_args { + uint16_t buf_count; + }; + ++struct rp1_pio_sm_config_xfer32_args { ++ uint16_t sm; ++ uint16_t dir; ++ uint32_t buf_size; ++ uint32_t buf_count; ++}; ++ + struct rp1_pio_sm_xfer_data_args { + uint16_t sm; + uint16_t dir; +@@ -185,6 +192,7 @@ struct rp1_access_hw_args { + #define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) + #define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) + #define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args) ++#define PIO_IOC_SM_CONFIG_XFER32 _IOW(PIO_IOC_MAGIC, 3, struct rp1_pio_sm_config_xfer32_args) + + #define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) + #define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) diff --git a/target/linux/bcm27xx/patches-6.6/950-1530-spi-dw-Wait-for-idle-after-TX.patch b/target/linux/bcm27xx/patches-6.6/950-1530-spi-dw-Wait-for-idle-after-TX.patch new file mode 100644 index 000000000..fa32b7119 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1530-spi-dw-Wait-for-idle-after-TX.patch @@ -0,0 +1,29 @@ +From 54a442deb925c37346abbbcc566234757925910c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 3 Feb 2025 17:50:20 +0000 +Subject: [PATCH] spi: dw: Wait for idle after TX + +If this is a DMA transfer, and if there is no simultaneous RX transfer, +wait for the interface to go idle before reporting that TX is done. + +Link: https://forums.raspberrypi.com/viewtopic.php?t=383027 + +Signed-off-by: Phil Elwell +--- + drivers/spi/spi-dw-dma.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/spi/spi-dw-dma.c ++++ b/drivers/spi/spi-dw-dma.c +@@ -304,6 +304,11 @@ static int dw_spi_dma_wait_tx_done(struc + return -EIO; + } + ++ if (!xfer->rx_buf) { ++ while (dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY) ++ cpu_relax(); ++ } ++ + return 0; + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1531-misc-rp1-pio-Error-out-on-incompatible-firmware.patch b/target/linux/bcm27xx/patches-6.6/950-1531-misc-rp1-pio-Error-out-on-incompatible-firmware.patch new file mode 100644 index 000000000..2fa70f38a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1531-misc-rp1-pio-Error-out-on-incompatible-firmware.patch @@ -0,0 +1,42 @@ +From e0a21a407b78477aa530800255d53405950cb1fb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 3 Feb 2025 14:44:08 +0000 +Subject: [PATCH] misc: rp1-pio: Error out on incompatible firmware + +If the RP1 firmware has reported an error then return that from the PIO +probe function, otherwise defer the probing. + +Link: https://github.com/raspberrypi/linux/issues/6642 + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1277,8 +1277,10 @@ static int rp1_pio_probe(struct platform + return dev_err_probe(dev, pdev->id, "alias is missing\n"); + + fw = devm_rp1_firmware_get(dev, dev->of_node); +- if (IS_ERR_OR_NULL(fw)) +- return dev_err_probe(dev, -ENOENT, "failed to contact RP1 firmware\n"); ++ if (!fw) ++ return dev_err_probe(dev, -EPROBE_DEFER, "failed to find RP1 firmware driver\n"); ++ if (IS_ERR(fw)) ++ return dev_err_probe(dev, PTR_ERR(fw), "failed to contact RP1 firmware\n"); + ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); + if (ret < 0) + return ret; +@@ -1355,6 +1357,11 @@ static void rp1_pio_remove(struct platfo + + if (g_pio == pio) + g_pio = NULL; ++ ++ device_destroy(pio->dev_class, pio->dev_num); ++ cdev_del(&pio->cdev); ++ class_destroy(pio->dev_class); ++ unregister_chrdev_region(pio->dev_num, 1); + } + + static const struct of_device_id rp1_pio_ids[] = { diff --git a/target/linux/bcm27xx/patches-6.6/950-1532-firmware-rp1-Linger-on-firmware-failure.patch b/target/linux/bcm27xx/patches-6.6/950-1532-firmware-rp1-Linger-on-firmware-failure.patch new file mode 100644 index 000000000..cf0bbc8c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1532-firmware-rp1-Linger-on-firmware-failure.patch @@ -0,0 +1,103 @@ +From 3f60adc5ce8238996ec43d2b76a6e38e46f8271b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 3 Feb 2025 14:51:52 +0000 +Subject: [PATCH] firmware: rp1: Linger on firmware failure + +To avoid pointless retries, let the probe function succeed if the +firmware interface is configured correctly but the firmware is +incompatible. The value of the private drvdata field holds the outcome. + +Link: https://github.com/raspberrypi/linux/issues/6642 + +Signed-off-by: Phil Elwell +--- + drivers/firmware/rp1.c | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +--- a/drivers/firmware/rp1.c ++++ b/drivers/firmware/rp1.c +@@ -114,7 +114,8 @@ static void rp1_firmware_delete(struct k + + void rp1_firmware_put(struct rp1_firmware *fw) + { +- kref_put(&fw->consumers, rp1_firmware_delete); ++ if (!IS_ERR_OR_NULL(fw)) ++ kref_put(&fw->consumers, rp1_firmware_delete); + } + EXPORT_SYMBOL_GPL(rp1_firmware_put); + +@@ -157,7 +158,7 @@ struct rp1_firmware *rp1_firmware_get(st + const char *match = rp1_firmware_of_match[0].compatible; + struct platform_device *pdev; + struct device_node *fwnode; +- struct rp1_firmware *fw; ++ struct rp1_firmware *fw = NULL; + + if (!client) + return NULL; +@@ -166,17 +167,17 @@ struct rp1_firmware *rp1_firmware_get(st + return NULL; + if (!of_device_is_compatible(fwnode, match)) { + of_node_put(fwnode); +- return NULL; ++ return ERR_PTR(-ENXIO); + } + + pdev = of_find_device_by_node(fwnode); + of_node_put(fwnode); + + if (!pdev) +- goto err_exit; ++ return ERR_PTR(-ENXIO); + + fw = platform_get_drvdata(pdev); +- if (!fw) ++ if (IS_ERR_OR_NULL(fw)) + goto err_exit; + + if (!kref_get_unless_zero(&fw->consumers)) +@@ -188,7 +189,7 @@ struct rp1_firmware *rp1_firmware_get(st + + err_exit: + put_device(&pdev->dev); +- return NULL; ++ return fw; + } + EXPORT_SYMBOL_GPL(rp1_firmware_get); + +@@ -204,8 +205,8 @@ struct rp1_firmware *devm_rp1_firmware_g + int ret; + + fw = rp1_firmware_get(client); +- if (!fw) +- return NULL; ++ if (IS_ERR_OR_NULL(fw)) ++ return fw; + + ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw); + if (ret) +@@ -270,19 +271,18 @@ static int rp1_firmware_probe(struct pla + init_completion(&fw->c); + kref_init(&fw->consumers); + +- platform_set_drvdata(pdev, fw); +- + ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION, + NULL, 0, &version, sizeof(version)); + if (ret == sizeof(version)) { + dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n", + version[0], version[1], version[2], version[3], version[4]); +- ret = 0; +- } else if (ret >= 0) { +- ret = -EIO; ++ platform_set_drvdata(pdev, fw); ++ } else { ++ rp1_firmware_put(fw); ++ platform_set_drvdata(pdev, ERR_PTR(-ENOENT)); + } + +- return ret; ++ return 0; + } + + static int rp1_firmware_remove(struct platform_device *pdev) diff --git a/target/linux/bcm27xx/patches-6.6/950-1533-mailbox-rp1-Don-t-claim-channels-in-of_xlate.patch b/target/linux/bcm27xx/patches-6.6/950-1533-mailbox-rp1-Don-t-claim-channels-in-of_xlate.patch new file mode 100644 index 000000000..5d6014d68 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1533-mailbox-rp1-Don-t-claim-channels-in-of_xlate.patch @@ -0,0 +1,30 @@ +From 4d577c42b47dfffda80da995fafd5b16fdb242e2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 4 Feb 2025 13:18:45 +0000 +Subject: [PATCH] mailbox: rp1: Don't claim channels in of_xlate + +The of_xlate method saves the calculated event mask in the con_priv +field. It also rejects subsequent attempt to use that channel because +the mask is non-zero, which causes a repeated instantiation of a client +driver to fail. + +The of_xlate method is not meant to be a point of resource acquisition. +Leave the con_priv initialisation, but drop the test that it was +previously zero. + +Signed-off-by: Phil Elwell +--- + drivers/mailbox/rp1-mailbox.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/mailbox/rp1-mailbox.c ++++ b/drivers/mailbox/rp1-mailbox.c +@@ -133,8 +133,6 @@ static struct mbox_chan *rp1_mbox_xlate( + return ERR_PTR(-EINVAL); + + chan = &mbox->chans[doorbell]; +- if (chan->con_priv) +- return ERR_PTR(-EBUSY); + + chan->con_priv = (void *)(uintptr_t)(1 << doorbell); + diff --git a/target/linux/bcm27xx/patches-6.6/950-1534-fixup-spi-dw-Wait-for-idle-after-TX.patch b/target/linux/bcm27xx/patches-6.6/950-1534-fixup-spi-dw-Wait-for-idle-after-TX.patch new file mode 100644 index 000000000..9ac62a340 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1534-fixup-spi-dw-Wait-for-idle-after-TX.patch @@ -0,0 +1,25 @@ +From fab0093d88452972f05f4b13e91e31d00b55421a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 4 Feb 2025 15:25:30 +0000 +Subject: [PATCH] fixup! spi: dw: Wait for idle after TX + +Relax a bit harder - transmission of the last bits may take a while. + +Signed-off-by: Phil Elwell +--- + drivers/spi/spi-dw-dma.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-dw-dma.c ++++ b/drivers/spi/spi-dw-dma.c +@@ -305,8 +305,9 @@ static int dw_spi_dma_wait_tx_done(struc + } + + if (!xfer->rx_buf) { ++ delay.value = dws->n_bytes * BITS_PER_BYTE; + while (dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY) +- cpu_relax(); ++ spi_delay_exec(&delay, xfer); + } + + return 0; diff --git a/target/linux/bcm27xx/patches-6.6/950-1535-dtoverlays-adds-support-for-Hifiberry-ADC8x-to-the-D.patch b/target/linux/bcm27xx/patches-6.6/950-1535-dtoverlays-adds-support-for-Hifiberry-ADC8x-to-the-D.patch new file mode 100644 index 000000000..a742b30a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1535-dtoverlays-adds-support-for-Hifiberry-ADC8x-to-the-D.patch @@ -0,0 +1,76 @@ +From 2cdd83392ba025cc88072c0153d443fc59919960 Mon Sep 17 00:00:00 2001 +From: j-schambacher +Date: Mon, 10 Feb 2025 14:58:34 +0100 +Subject: [PATCH] dtoverlays: adds support for Hifiberry ADC8x to the DAC8x + +Allows the usage of ADC8x stacked on top of the DAC8x. +Activates all I2S pins and uses now the dummy-dai instead +of the formerly used pcm5102 to allow the use of a +capture device, too. The simple card driver will +probe for the ADC8x and may activate the 8 channel +capture. Uses GPIO5 for detection. + +Signed-off-by: j-schambacher +--- + arch/arm/boot/dts/overlays/README | 3 +++ + .../boot/dts/overlays/hifiberry-dac8x-overlay.dts | 14 ++++++++++---- + 2 files changed, 13 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -1879,6 +1879,9 @@ Params: + + Name: hifiberry-dac8x + Info: Configures the HifiBerry DAC8X audio cards (only on Pi5) ++ This driver also detects a stacked ADC8x and activates the ++ capture capabilities. ++ Note: for standalone use of the ADC8x activate the ADC8x module. + Load: dtoverlay=hifiberry-dac8x + Params: + +--- a/arch/arm/boot/dts/overlays/hifiberry-dac8x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/hifiberry-dac8x-overlay.dts +@@ -1,7 +1,10 @@ ++// SPDX-License-Identifier: GPL-2.0 + // Definitions for HiFiBerry DAC8x + /dts-v1/; + /plugin/; + ++#include ++ + / { + compatible = "brcm,bcm2712"; + +@@ -10,8 +13,10 @@ + __overlay__ { + rp1_i2s0_dac8x: rp1_i2s0_dac8x { + function = "i2s0"; +- pins = "gpio18", "gpio19", "gpio21", +- "gpio23", "gpio25", "gpio27"; ++ pins = "gpio18", "gpio19", "gpio20", ++ "gpio21", "gpio22", "gpio23", ++ "gpio24", "gpio25", "gpio26", ++ "gpio27"; + bias-disable; + status = "okay"; + }; +@@ -30,9 +35,9 @@ + fragment@2 { + target-path = "/"; + __overlay__ { +- pcm5102a-codec { ++ dummy-codec { + #sound-dai-cells = <0>; +- compatible = "ti,pcm5102a"; ++ compatible = "snd-soc-dummy"; + status = "okay"; + }; + }; +@@ -43,6 +48,7 @@ + __overlay__ { + compatible = "hifiberry,hifiberry-dac8x"; + i2s-controller = <&i2s_clk_producer>; ++ hasadc-gpio = <&gpio 5 GPIO_ACTIVE_LOW>; + status = "okay"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.6/950-1536-ASoC-adds-ADC8x-support-to-the-Hifiberry-DAC8x.patch b/target/linux/bcm27xx/patches-6.6/950-1536-ASoC-adds-ADC8x-support-to-the-Hifiberry-DAC8x.patch new file mode 100644 index 000000000..fec52cc60 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1536-ASoC-adds-ADC8x-support-to-the-Hifiberry-DAC8x.patch @@ -0,0 +1,75 @@ +From d664f45f77423b03e5a435e480822075c03331bd Mon Sep 17 00:00:00 2001 +From: j-schambacher +Date: Mon, 10 Feb 2025 15:08:48 +0100 +Subject: [PATCH] ASoC: adds ADC8x support to the Hifiberry DAC8x + +The driver probes for the ADC8x which can be stacked on top +of the DAC8x. It enables a symmetric 8 channel capture using +the dummy-dai. + +Signed-off-by: j-schambacher +--- + sound/soc/bcm/rpi-simple-soundcard.c | 38 +++++++++++++++++++++++++--- + 1 file changed, 34 insertions(+), 4 deletions(-) + +--- a/sound/soc/bcm/rpi-simple-soundcard.c ++++ b/sound/soc/bcm/rpi-simple-soundcard.c +@@ -354,16 +354,46 @@ static struct snd_rpi_simple_drvdata drv + .dai = snd_hifiberry_dac_dai, + }; + ++SND_SOC_DAILINK_DEFS(hifiberry_dac8x, ++ 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_dac8x_init(struct snd_soc_pcm_runtime *rtd) + { + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); ++ struct snd_soc_card *card = rtd->card; ++ struct gpio_desc *gpio_desc; ++ bool has_adc; + +- /* override the defaults to reflect 4 x PCM5102A on the card +- * and limit the sample rate to 192ksps +- */ ++ /* Configure the codec for 8 channel playback */ + codec_dai->driver->playback.channels_max = 8; + codec_dai->driver->playback.rates = SNDRV_PCM_RATE_8000_192000; + ++ /* Activate capture based on ADC8x detection */ ++ gpio_desc = devm_gpiod_get(card->dev, "hasadc", GPIOD_IN); ++ if (IS_ERR(gpio_desc)) { ++ dev_err(card->dev, "Failed to get GPIO: %ld\n", PTR_ERR(gpio_desc)); ++ return PTR_ERR(gpio_desc); ++ } ++ ++ has_adc = gpiod_get_value(gpio_desc); ++ ++ if (has_adc) { ++ struct snd_soc_dai_link *dai = rtd->dai_link; ++ ++ dev_info(card->dev, "ADC8x detected: capture enabled\n"); ++ codec_dai->driver->symmetric_rate = 1; ++ codec_dai->driver->symmetric_channels = 1; ++ codec_dai->driver->symmetric_sample_bits = 1; ++ codec_dai->driver->capture.rates = SNDRV_PCM_RATE_8000_192000; ++ dai->name = "HiFiBerry DAC8xADC8x"; ++ dai->stream_name = "HiFiBerry DAC8xADC8x HiFi"; ++ } else { ++ dev_info(card->dev, "no ADC8x detected\n"); ++ rtd->dai_link->playback_only = 1; // Disable capture ++ } ++ + return 0; + } + +@@ -375,7 +405,7 @@ static struct snd_soc_dai_link snd_hifib + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .init = hifiberry_dac8x_init, +- SND_SOC_DAILINK_REG(hifiberry_dac), ++ SND_SOC_DAILINK_REG(hifiberry_dac8x), + }, + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1539-Reapply-usb-dwc3-Set-DMA-and-coherent-masks-early.patch b/target/linux/bcm27xx/patches-6.6/950-1539-Reapply-usb-dwc3-Set-DMA-and-coherent-masks-early.patch new file mode 100644 index 000000000..327819f28 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1539-Reapply-usb-dwc3-Set-DMA-and-coherent-masks-early.patch @@ -0,0 +1,337 @@ +From 1b1e75ae7d69816609a95ff2fb92729a2ff04d8e Mon Sep 17 00:00:00 2001 +From: Dom Cobley +Date: Mon, 10 Feb 2025 13:26:33 +0000 +Subject: [PATCH] Reapply "usb: dwc3: Set DMA and coherent masks early" + +This reverts commit 09dfdf6129532e19b2cfd4992d1d09e7119ccd48. +--- + drivers/phy/broadcom/Kconfig | 2 +- + .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 59 +++++++++++++++++++ + drivers/phy/broadcom/phy-brcm-usb-init.h | 2 + + drivers/phy/broadcom/phy-brcm-usb.c | 18 +++++- + drivers/usb/dwc3/core.c | 53 +++++++++++++++++ + drivers/usb/dwc3/core.h | 5 ++ + drivers/usb/dwc3/host.c | 9 ++- + 7 files changed, 145 insertions(+), 3 deletions(-) + +--- a/drivers/phy/broadcom/Kconfig ++++ b/drivers/phy/broadcom/Kconfig +@@ -93,7 +93,7 @@ config PHY_BRCM_SATA + + config PHY_BRCM_USB + tristate "Broadcom STB USB PHY driver" +- depends on ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST ++ depends on ARCH_BCMBCA || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST + depends on OF + select GENERIC_PHY + select SOC_BRCMSTB if ARCH_BRCMSTB +--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c ++++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +@@ -341,6 +341,36 @@ static void usb_init_common_7216(struct + usb_init_common(params); + } + ++static void usb_init_common_2712(struct brcm_usb_init_params *params) ++{ ++ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; ++ void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC]; ++ u32 reg; ++ ++ if (params->syscon_piarbctl) ++ syscon_piarbctl_init(params->syscon_piarbctl); ++ ++ USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); ++ ++ usb_wake_enable_7211b0(params, false); ++ ++ usb_init_common(params); ++ ++ /* ++ * The BDC controller will get occasional failures with ++ * the default "Read Transaction Size" of 6 (1024 bytes). ++ * Set it to 4 (256 bytes). ++ */ ++ if ((params->supported_port_modes != USB_CTLR_MODE_HOST) && bdc_ec) { ++ reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA); ++ reg &= ~BDC_EC_AXIRDA_RTS_MASK; ++ reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT); ++ brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA); ++ } ++ ++ usb2_eye_fix_7211b0(params); ++} ++ + static void usb_init_xhci(struct brcm_usb_init_params *params) + { + pr_debug("%s\n", __func__); +@@ -386,6 +416,18 @@ static void usb_uninit_common_7211b0(str + + } + ++static void usb_uninit_common_2712(struct brcm_usb_init_params *params) ++{ ++ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; ++ ++ if (params->wake_enabled) { ++ USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN); ++ usb_wake_enable_7211b0(params, true); ++ } else { ++ USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); ++ } ++} ++ + static void usb_uninit_xhci(struct brcm_usb_init_params *params) + { + +@@ -440,6 +482,16 @@ static const struct brcm_usb_init_ops bc + .set_dual_select = usb_set_dual_select, + }; + ++static const struct brcm_usb_init_ops bcm2712_ops = { ++ .init_ipp = usb_init_ipp, ++ .init_common = usb_init_common_2712, ++ .init_xhci = usb_init_xhci, ++ .uninit_common = usb_uninit_common_2712, ++ .uninit_xhci = usb_uninit_xhci, ++ .get_dual_select = usb_get_dual_select, ++ .set_dual_select = usb_set_dual_select, ++}; ++ + void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) + { + +@@ -457,3 +509,10 @@ void brcm_usb_dvr_init_7211b0(struct brc + params->family_name = "7211"; + params->ops = &bcm7211b0_ops; + } ++ ++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params) ++{ ++ params->family_name = "2712"; ++ params->ops = &bcm2712_ops; ++ params->suspend_with_clocks = true; ++} +--- a/drivers/phy/broadcom/phy-brcm-usb-init.h ++++ b/drivers/phy/broadcom/phy-brcm-usb-init.h +@@ -70,12 +70,14 @@ struct brcm_usb_init_params { + const struct brcm_usb_init_ops *ops; + struct regmap *syscon_piarbctl; + bool wake_enabled; ++ bool suspend_with_clocks; + }; + + void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params); + void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); + void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); + void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params); ++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params); + + static inline u32 brcm_usb_readl(void __iomem *addr) + { +--- a/drivers/phy/broadcom/phy-brcm-usb.c ++++ b/drivers/phy/broadcom/phy-brcm-usb.c +@@ -75,7 +75,7 @@ struct brcm_usb_phy_data { + }; + + static s8 *node_reg_names[BRCM_REGS_MAX] = { +- "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" ++ "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" + }; + + static int brcm_pm_notifier(struct notifier_block *notifier, +@@ -315,6 +315,18 @@ static const struct match_chip_info chip + .optional_reg = BRCM_REGS_BDC_EC, + }; + ++static const struct match_chip_info chip_info_2712 = { ++ .init_func = &brcm_usb_dvr_init_2712, ++ .required_regs = { ++ BRCM_REGS_CTRL, ++ BRCM_REGS_XHCI_EC, ++ BRCM_REGS_XHCI_GBL, ++ BRCM_REGS_USB_MDIO, ++ -1, ++ }, ++ .optional_reg = BRCM_REGS_BDC_EC, ++}; ++ + static const struct match_chip_info chip_info_7445 = { + .init_func = &brcm_usb_dvr_init_7445, + .required_regs = { +@@ -338,6 +350,10 @@ static const struct of_device_id brcm_us + .data = &chip_info_7211b0, + }, + { ++ .compatible = "brcm,bcm2712-usb-phy", ++ .data = &chip_info_2712, ++ }, ++ { + .compatible = "brcm,brcmstb-usb-phy", + .data = &chip_info_7445, + }, +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -1223,6 +1223,24 @@ static void dwc3_config_threshold(struct + } + } + ++static void dwc3_set_axi_pipe_limit(struct dwc3 *dwc) ++{ ++ struct device *dev = dwc->dev; ++ u32 cfg; ++ ++ if (!dwc->axi_pipe_limit) ++ return; ++ if (dwc->axi_pipe_limit > 16) { ++ dev_err(dev, "Invalid axi_pipe_limit property\n"); ++ return; ++ } ++ cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1); ++ cfg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT(15); ++ cfg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(dwc->axi_pipe_limit - 1); ++ ++ dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, cfg); ++} ++ + /** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure +@@ -1288,6 +1306,8 @@ static int dwc3_core_init(struct dwc3 *d + + dwc3_set_incr_burst_type(dwc); + ++ dwc3_set_axi_pipe_limit(dwc); ++ + ret = dwc3_phy_power_on(dwc); + if (ret) + goto err_exit_phy; +@@ -1386,6 +1406,24 @@ static int dwc3_core_init(struct dwc3 *d + + dwc3_config_threshold(dwc); + ++ if (DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) { ++ u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; ++ u8 tx_maxburst = dwc->tx_max_burst_prd; ++ ++ if (tx_thr_num && tx_maxburst) { ++ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); ++ reg |= DWC3_GTXTHRCFG_PKTCNTSEL; ++ ++ reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0); ++ reg |= DWC3_GTXTHRCFG_TXPKTCNT(tx_thr_num); ++ ++ reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0); ++ reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst); ++ ++ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); ++ } ++ } ++ + return 0; + + err_power_off_phy: +@@ -1529,6 +1567,7 @@ static void dwc3_get_properties(struct d + u8 tx_thr_num_pkt_prd = 0; + u8 tx_max_burst_prd = 0; + u8 tx_fifo_resize_max_num; ++ u8 axi_pipe_limit; + + /* default to highest possible threshold */ + lpm_nyet_threshold = 0xf; +@@ -1549,6 +1588,9 @@ static void dwc3_get_properties(struct d + */ + tx_fifo_resize_max_num = 6; + ++ /* Default to 0 (don't override hardware defaults) */ ++ axi_pipe_limit = 0; ++ + dwc->maximum_speed = usb_get_maximum_speed(dev); + dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); + dwc->dr_mode = usb_get_dr_mode(dev); +@@ -1669,6 +1711,9 @@ static void dwc3_get_properties(struct d + dwc->dis_split_quirk = device_property_read_bool(dev, + "snps,dis-split-quirk"); + ++ device_property_read_u8(dev, "snps,axi-pipe-limit", ++ &axi_pipe_limit); ++ + dwc->lpm_nyet_threshold = lpm_nyet_threshold; + dwc->tx_de_emphasis = tx_de_emphasis; + +@@ -1686,6 +1731,8 @@ static void dwc3_get_properties(struct d + dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; + dwc->tx_max_burst_prd = tx_max_burst_prd; + ++ dwc->axi_pipe_limit = axi_pipe_limit; ++ + dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; + } + +@@ -1978,6 +2025,12 @@ static int dwc3_probe(struct platform_de + if (IS_ERR(dwc->usb_psy)) + return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + ++ if (!dwc->sysdev_is_parent) { ++ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64)); ++ if (ret) ++ return ret; ++ } ++ + dwc->reset = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(dwc->reset)) { + ret = PTR_ERR(dwc->reset); +--- a/drivers/usb/dwc3/core.h ++++ b/drivers/usb/dwc3/core.h +@@ -185,6 +185,9 @@ + #define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */ + #define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff + ++/* Global SoC Bus Configuration Register 1 */ ++#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n) (((n) & 0xf) << 8) ++ + /* Global Debug LSP MUX Select */ + #define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */ + #define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff) +@@ -1070,6 +1073,7 @@ struct dwc3_scratchpad_array { + * @tx_max_burst_prd: max periodic ESS transmit burst size + * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize + * @clear_stall_protocol: endpoint number that requires a delayed status phase ++ * @axi_max_pipe: set to override the maximum number of pipelined AXI transfers + * @hsphy_interface: "utmi" or "ulpi" + * @connected: true when we're connected to a host, false otherwise + * @softconnect: true when gadget connect is called, false when disconnect runs +@@ -1311,6 +1315,7 @@ struct dwc3 { + u8 tx_max_burst_prd; + u8 tx_fifo_resize_max_num; + u8 clear_stall_protocol; ++ u8 axi_pipe_limit; + + const char *hsphy_interface; + +--- a/drivers/usb/dwc3/host.c ++++ b/drivers/usb/dwc3/host.c +@@ -82,16 +82,23 @@ out: + + int dwc3_host_init(struct dwc3 *dwc) + { ++ struct platform_device *pdev = to_platform_device(dwc->dev); + struct property_entry props[5]; + struct platform_device *xhci; + int ret, irq; + int prop_idx = 0; ++ int id; + + irq = dwc3_host_get_irq(dwc); + if (irq < 0) + return irq; + +- xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); ++ id = of_alias_get_id(pdev->dev.of_node, "usb"); ++ if (id >= 0) ++ xhci = platform_device_alloc("xhci-hcd", id); ++ else ++ xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); ++ + if (!xhci) { + dev_err(dwc->dev, "couldn't allocate xHCI device\n"); + return -ENOMEM; diff --git a/target/linux/bcm27xx/patches-6.6/950-1541-overlays-Add-OpenHydroponics-RootMaster-overlay.patch b/target/linux/bcm27xx/patches-6.6/950-1541-overlays-Add-OpenHydroponics-RootMaster-overlay.patch new file mode 100644 index 000000000..f16d0bdd2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1541-overlays-Add-OpenHydroponics-RootMaster-overlay.patch @@ -0,0 +1,119 @@ +From 081eebdeccfd12e0aaba4b64c9f87b608777913b Mon Sep 17 00:00:00 2001 +From: Micke Prag +Date: Thu, 13 Feb 2025 22:08:43 +0100 +Subject: [PATCH] overlays: Add OpenHydroponics RootMaster overlay + +Signed-off-by: Micke Prag +--- + arch/arm/boot/dts/overlays/Makefile | 1 + + arch/arm/boot/dts/overlays/README | 7 ++ + .../boot/dts/overlays/rootmaster-overlay.dts | 77 +++++++++++++++++++ + 3 files changed, 85 insertions(+) + create mode 100644 arch/arm/boot/dts/overlays/rootmaster-overlay.dts + +--- a/arch/arm/boot/dts/overlays/Makefile ++++ b/arch/arm/boot/dts/overlays/Makefile +@@ -228,6 +228,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ + qca7000-uart0.dtbo \ + ramoops.dtbo \ + ramoops-pi4.dtbo \ ++ rootmaster.dtbo \ + rotary-encoder.dtbo \ + rpi-backlight.dtbo \ + rpi-codeczero.dtbo \ +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -4084,6 +4084,13 @@ Params: base-addr Where to + console-size Size of non-panic dmesg captures (default 0) + + ++Name: rootmaster ++Info: Overlay for OpenHydroponics RootMaster board. ++ https://openhydroponics.com/hw/rootmaster ++Load: dtoverlay=rootmaster ++Params: ++ ++ + Name: rotary-encoder + Info: Overlay for GPIO connected rotary encoder. + Load: dtoverlay=rotary-encoder,= +--- /dev/null ++++ b/arch/arm/boot/dts/overlays/rootmaster-overlay.dts +@@ -0,0 +1,77 @@ ++// redo: ovmerge -c mcp251xfd-overlay.dts,spi0-0,interrupt=25 w1-gpio-overlay.dts,gpiopin=4 ++ ++/dts-v1/; ++/plugin/; ++ ++#include ++#include ++#include ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ fragment@0 { ++ target = <&spidev0>; ++ __overlay__ { ++ status = "disabled"; ++ }; ++ }; ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mcp251xfd_pins: mcp251xfd_spi0_0_pins { ++ brcm,pins = <25>; ++ brcm,function = ; ++ }; ++ }; ++ }; ++ fragment@2 { ++ target-path = "/clocks"; ++ __overlay__ { ++ clk_mcp251xfd_osc: mcp251xfd-spi0-0-osc { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <40000000>; ++ }; ++ }; ++ }; ++ fragment@3 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ mcp251xfd@0 { ++ compatible = "microchip,mcp251xfd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mcp251xfd_pins>; ++ spi-max-frequency = <20000000>; ++ interrupt-parent = <&gpio>; ++ interrupts = <25 IRQ_TYPE_LEVEL_LOW>; ++ clocks = <&clk_mcp251xfd_osc>; ++ }; ++ }; ++ }; ++ fragment@4 { ++ target-path = "/"; ++ __overlay__ { ++ onewire@4 { ++ compatible = "w1-gpio"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&w1_pins>; ++ gpios = <&gpio 4 0>; ++ status = "okay"; ++ }; ++ }; ++ }; ++ fragment@5 { ++ target = <&gpio>; ++ __overlay__ { ++ w1_pins: w1_pins@4 { ++ brcm,pins = <4>; ++ brcm,function = <0>; ++ brcm,pull = <0>; ++ }; ++ }; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1542-media-i2c-arducam-pivariety-Fix-mutex-init-and-NULL-.patch b/target/linux/bcm27xx/patches-6.6/950-1542-media-i2c-arducam-pivariety-Fix-mutex-init-and-NULL-.patch new file mode 100644 index 000000000..784ae522f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1542-media-i2c-arducam-pivariety-Fix-mutex-init-and-NULL-.patch @@ -0,0 +1,37 @@ +From 011cbf22d7583687ae18690185169e5da0be000a Mon Sep 17 00:00:00 2001 +From: Yuriy Pasichnyk +Date: Tue, 18 Feb 2025 16:20:31 +0200 +Subject: [PATCH] media: i2c: arducam-pivariety: Fix mutex init and NULL + pointer + +The mutex used in arducam-pivariety was not properly initialized, +which could lead to undefined behavior. This also caused a NULL +pointer dereference under certain conditions. + +This patch ensures the mutex is correctly initialized during probe +and prevents NULL pointer dereferences. + +Signed-off-by: Yuriy Pasichnyk +--- + drivers/media/i2c/arducam-pivariety.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/media/i2c/arducam-pivariety.c ++++ b/drivers/media/i2c/arducam-pivariety.c +@@ -1208,6 +1208,8 @@ static int pivariety_enum_controls(struc + if (ret) + return ret; + ++ mutex_init(&pivariety->mutex); ++ + index = 0; + while (1) { + ret = pivariety_write(pivariety, CTRL_INDEX_REG, index); +@@ -1295,6 +1297,7 @@ static int pivariety_enum_controls(struc + v4l2_ctrl_handler_setup(ctrl_hdlr); + return 0; + err: ++ mutex_destroy(&pivariety->mutex); + return -ENODEV; + } + diff --git a/target/linux/bcm27xx/patches-6.6/950-1543-misc-rp1-pio-Demote-fw-probe-error-to-warning.patch b/target/linux/bcm27xx/patches-6.6/950-1543-misc-rp1-pio-Demote-fw-probe-error-to-warning.patch new file mode 100644 index 000000000..af9c9f9e5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1543-misc-rp1-pio-Demote-fw-probe-error-to-warning.patch @@ -0,0 +1,32 @@ +From af9965a855a8c6c7140bbcccfa89eec7e90a993d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 25 Feb 2025 12:16:33 +0000 +Subject: [PATCH] misc: rp1-pio: Demote fw probe error to warning + +Support for the RP1 firmware mailbox API is rolling out to Pi 5 EEPROM +images. For most users, the fact that the PIO is not available is no +cause for alarm. Change the message to a warning, so that it does not +appear with "quiet" in cmdline.txt. + +Link: https://github.com/raspberrypi/linux/issues/6642 + +Signed-off-by: Phil Elwell +--- + drivers/misc/rp1-pio.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/misc/rp1-pio.c ++++ b/drivers/misc/rp1-pio.c +@@ -1279,8 +1279,10 @@ static int rp1_pio_probe(struct platform + fw = devm_rp1_firmware_get(dev, dev->of_node); + if (!fw) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to find RP1 firmware driver\n"); +- if (IS_ERR(fw)) +- return dev_err_probe(dev, PTR_ERR(fw), "failed to contact RP1 firmware\n"); ++ if (IS_ERR(fw)) { ++ dev_warn(dev, "failed to contact RP1 firmware\n"); ++ return PTR_ERR(fw); ++ } + ret = rp1_firmware_get_feature(fw, FOURCC_PIO, &op_base, &op_count); + if (ret < 0) + return ret; diff --git a/target/linux/bcm27xx/patches-6.6/950-1544-dts-Add-hogs-for-RP1-GPIO-46-48-on-CM5.patch b/target/linux/bcm27xx/patches-6.6/950-1544-dts-Add-hogs-for-RP1-GPIO-46-48-on-CM5.patch new file mode 100644 index 000000000..98eaf56ee --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1544-dts-Add-hogs-for-RP1-GPIO-46-48-on-CM5.patch @@ -0,0 +1,38 @@ +From a4e7897eef1ad4628572a6abfd30291209a0bf43 Mon Sep 17 00:00:00 2001 +From: Richard Oliver +Date: Tue, 25 Feb 2025 15:06:23 +0000 +Subject: [PATCH] dts: Add hogs for RP1 GPIO 46/48 on CM5 + +On Pi5 5, GPIOs 46/48 are made available on the 'CAM/DISP 1' connector as +'CD1_IO0_MICCLK'/'CD1_IO1_MICDAT1'. These GPIOs are not connected on +CM5. + +Add hogs for GPIO 46/48 on CM5 to prevent camera drivers from +inadvertently using them when connected to 'CAM/DISP 1' + +Signed-off-by: Richard Oliver +--- + arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +@@ -734,6 +734,18 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; + function = "vbus1"; + pins = "gpio42", "gpio43"; + }; ++ ++ micclk1_hog { ++ gpio-hog; ++ gpios = <46 GPIO_ACTIVE_HIGH>; ++ output-high; ++ }; ++ ++ micdat1_hog { ++ gpio-hog; ++ gpios = <48 GPIO_ACTIVE_HIGH>; ++ output-high; ++ }; + }; + + / { diff --git a/target/linux/bcm27xx/patches-6.6/950-1545-spi-rp2040-gpio-bridge-fix-gpiod-error-handling.patch b/target/linux/bcm27xx/patches-6.6/950-1545-spi-rp2040-gpio-bridge-fix-gpiod-error-handling.patch new file mode 100644 index 000000000..31e35a3bf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1545-spi-rp2040-gpio-bridge-fix-gpiod-error-handling.patch @@ -0,0 +1,25 @@ +From f8f3e202fb083906fbc98ef6ea445e424779c076 Mon Sep 17 00:00:00 2001 +From: Richard Oliver +Date: Tue, 25 Feb 2025 15:22:46 +0000 +Subject: [PATCH] spi: rp2040-gpio-bridge: fix gpiod error handling + +In some circumstances, devm_gpiod_get_array_optional() can return +PTR_ERR rather than NULL to indicate failure. Handle these cases. + +Signed-off-by: Richard Oliver +--- + drivers/spi/spi-rp2040-gpio-bridge.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-rp2040-gpio-bridge.c ++++ b/drivers/spi/spi-rp2040-gpio-bridge.c +@@ -956,7 +956,8 @@ static void rp2040_gbdg_parse_dt(struct + + rp2040_gbdg->fast_xfer_gpios = + devm_gpiod_get_array_optional(dev, "fast_xfer", GPIOD_ASIS); +- if (!rp2040_gbdg->fast_xfer_gpios) { ++ if (IS_ERR_OR_NULL(rp2040_gbdg->fast_xfer_gpios)) { ++ rp2040_gbdg->fast_xfer_gpios = NULL; + dev_info(dev, "Could not acquire fast_xfer-gpios\n"); + goto node_put; + } diff --git a/target/linux/bcm27xx/patches-6.6/950-1546-spi-rp2040-gpio-bridge-probe-Cfg-fast_xfer-clk.patch b/target/linux/bcm27xx/patches-6.6/950-1546-spi-rp2040-gpio-bridge-probe-Cfg-fast_xfer-clk.patch new file mode 100644 index 000000000..04b9ea602 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1546-spi-rp2040-gpio-bridge-probe-Cfg-fast_xfer-clk.patch @@ -0,0 +1,37 @@ +From 2e071057fded90e789c0101498e45a1778be93fe Mon Sep 17 00:00:00 2001 +From: Richard Oliver +Date: Tue, 25 Feb 2025 15:27:59 +0000 +Subject: [PATCH] spi: rp2040-gpio-bridge: probe: Cfg fast_xfer clk + +Fast transfer mode requires that the first bit of data is clocked with a +rising edge. This can cause extra bits of data to be clocked on hardware +where the clock signal uses a pull-up. This change ensures that clk is +driven low before fast data transfer mode is entered. + +Signed-off-by: Richard Oliver +--- + drivers/spi/spi-rp2040-gpio-bridge.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-rp2040-gpio-bridge.c ++++ b/drivers/spi/spi-rp2040-gpio-bridge.c +@@ -617,7 +617,6 @@ static int rp2040_gbdg_fast_xfer(struct + &clock_mux); + + gpiod_direction_output(priv_data->fast_xfer_gpios->desc[0], 1); +- gpiod_direction_output(priv_data->fast_xfer_gpios->desc[1], 0); + + rp2040_gbdg_rp1_calc_offsets(priv_data->fast_xfer_data_index, + &data_bank, &data_offset); +@@ -989,6 +988,11 @@ static void rp2040_gbdg_parse_dt(struct + goto node_put; + } + ++ /* ++ * fast_xfer mode requires first data bit to be clocked on a rising ++ * edge. Configure as output-low here before fast_xfer mode is entered. ++ */ ++ gpiod_direction_output(rp2040_gbdg->fast_xfer_gpios->desc[1], 0); + node_put: + if (of_args[0].np) + of_node_put(of_args[0].np); diff --git a/target/linux/bcm27xx/patches-6.6/950-1547-fix-clang-compilation-error.patch b/target/linux/bcm27xx/patches-6.6/950-1547-fix-clang-compilation-error.patch new file mode 100644 index 000000000..13fa24bea --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1547-fix-clang-compilation-error.patch @@ -0,0 +1,30 @@ +From 1337c39626c14ca5e61b4cddbc097d87212a0b13 Mon Sep 17 00:00:00 2001 +From: KubaTaba1uga +Date: Mon, 3 Mar 2025 17:25:29 +0100 +Subject: [PATCH] fix clang compilation error + +--- + drivers/dma-buf/heaps/system_heap.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/dma-buf/heaps/system_heap.c ++++ b/drivers/dma-buf/heaps/system_heap.c +@@ -50,11 +50,16 @@ static gfp_t order_flags[] = {HIGH_ORDER + * to match with the sizes often found in IOMMUs. Using order 4 pages instead + * of order 0 pages can significantly improve the performance of many IOMMUs + * by reducing TLB pressure and time spent updating page tables. ++ * ++ * Note: `module_max_order` must be set explicitly instead of using ++ * `orders[0]` to avoid Clang's "initializer element is not a ++ * compile-time constant" error. + */ +-static const unsigned int orders[] = {8, 4, 0}; ++#define MAX_ORDERS_VALUE 8 ++static const unsigned int orders[] = {MAX_ORDERS_VALUE, 4, 0}; + #define NUM_ORDERS ARRAY_SIZE(orders) + +-static unsigned int module_max_order = orders[0]; ++static unsigned int module_max_order = MAX_ORDERS_VALUE; + + module_param_named(max_order, module_max_order, uint, 0400); + MODULE_PARM_DESC(max_order, "Maximum allocation order override."); diff --git a/target/linux/bcm27xx/patches-6.6/950-1548-delete-the-comment.patch b/target/linux/bcm27xx/patches-6.6/950-1548-delete-the-comment.patch new file mode 100644 index 000000000..0918f8cde --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1548-delete-the-comment.patch @@ -0,0 +1,22 @@ +From f1076a9d7a269d72b6707283560d0d38203cb07a Mon Sep 17 00:00:00 2001 +From: KubaTaba1uga +Date: Tue, 4 Mar 2025 09:50:30 +0100 +Subject: [PATCH] delete the comment + +--- + drivers/dma-buf/heaps/system_heap.c | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/dma-buf/heaps/system_heap.c ++++ b/drivers/dma-buf/heaps/system_heap.c +@@ -50,10 +50,6 @@ static gfp_t order_flags[] = {HIGH_ORDER + * to match with the sizes often found in IOMMUs. Using order 4 pages instead + * of order 0 pages can significantly improve the performance of many IOMMUs + * by reducing TLB pressure and time spent updating page tables. +- * +- * Note: `module_max_order` must be set explicitly instead of using +- * `orders[0]` to avoid Clang's "initializer element is not a +- * compile-time constant" error. + */ + #define MAX_ORDERS_VALUE 8 + static const unsigned int orders[] = {MAX_ORDERS_VALUE, 4, 0}; diff --git a/target/linux/bcm27xx/patches-6.6/950-1549-dtoverlays-ov9281-Add-continuous-clock-option-as-an-.patch b/target/linux/bcm27xx/patches-6.6/950-1549-dtoverlays-ov9281-Add-continuous-clock-option-as-an-.patch new file mode 100644 index 000000000..a2b7df78d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1549-dtoverlays-ov9281-Add-continuous-clock-option-as-an-.patch @@ -0,0 +1,66 @@ +From ac56a225e0889e60e912ecd3a51333a5aee901fc Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Tue, 4 Mar 2025 15:28:53 +0000 +Subject: [PATCH] dtoverlays: ov9281: Add continuous clock option as an + override + +The previous change to make ov9281 always run in continuous clock +mode causes problems on Pi3 for reasons that aren't fully +understood. Pi4 is quite happy with it. + +Change the default back to being non-continuous clock, and add +an override to select continuous clock mode and its slightly +greater max frame rate. + +https://forums.raspberrypi.com/viewtopic.php?p=2300215 + +Signed-off-by: Dave Stevenson +--- + arch/arm/boot/dts/overlays/README | 3 +++ + arch/arm/boot/dts/overlays/ov9281-overlay.dts | 16 +++++++++++++++- + 2 files changed, 18 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/overlays/README ++++ b/arch/arm/boot/dts/overlays/README +@@ -3570,6 +3570,9 @@ Params: rotation Mounting + Compute Module (CSI0, i2c_vc, and cam0_reg). + arducam Slow down the regulator for slow Arducam + modules. ++ clk-continuous Switch to continuous mode on the CSI clock lane, ++ which increases the maximum frame rate slightly. ++ Appears not to work on Pi3. + + + Name: papirus +--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts ++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts +@@ -64,6 +64,20 @@ + }; + }; + ++ fragment@6 { ++ target = <&csi_ep>; ++ __overlay__ { ++ clock-noncontinuous; ++ }; ++ }; ++ ++ fragment@7 { ++ target = <&cam_endpoint>; ++ __overlay__ { ++ clock-noncontinuous; ++ }; ++ }; ++ + __overrides__ { + rotation = <&cam_node>,"rotation:0"; + orientation = <&cam_node>,"orientation:0"; +@@ -75,7 +89,7 @@ + <&cam_node>, "avdd-supply:0=",<&cam0_reg>, + <®_frag>, "target:0=",<&cam0_reg>; + arducam = <0>, "+5"; +- ++ clk-continuous = <0>, "-6-7"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1550-drm-v3d-Add-clock-handling.patch b/target/linux/bcm27xx/patches-6.6/950-1550-drm-v3d-Add-clock-handling.patch new file mode 100644 index 000000000..3bce057bf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1550-drm-v3d-Add-clock-handling.patch @@ -0,0 +1,129 @@ +From 32c319ba2f2fd662a3b7bd042515cd650807dbff Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Sat, 1 Feb 2025 13:50:46 +0100 +Subject: [PATCH] drm/v3d: Add clock handling +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +[ Upstream commit 4dd40b5f9c3d89b67af0dbe059cf4a51aac6bf06 ] + +Since the initial commit 57692c94dcbe ("drm/v3d: Introduce a new DRM driver +for Broadcom V3D V3.x+") the struct v3d_dev reserved a pointer for +an optional V3D clock. But there wasn't any code, which fetched it. +So add the missing clock handling before accessing any V3D registers. + +Signed-off-by: Stefan Wahren +Reviewed-by: Maíra Canal +Signed-off-by: Maíra Canal +Link: https://patchwork.freedesktop.org/patch/msgid/20250201125046.33030-1-wahrenst@gmx.net +[ Maíra: Backported to the downstream repository ] +Signed-off-by: Maíra Canal +--- + drivers/gpu/drm/v3d/v3d_drv.c | 44 ++++++++++++++++++++++------------- + 1 file changed, 28 insertions(+), 16 deletions(-) + +--- a/drivers/gpu/drm/v3d/v3d_drv.c ++++ b/drivers/gpu/drm/v3d/v3d_drv.c +@@ -232,11 +232,21 @@ static int v3d_platform_drm_probe(struct + if (ret) + return ret; + ++ v3d->clk = devm_clk_get_optional(dev, NULL); ++ if (IS_ERR(v3d->clk)) ++ return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n"); ++ ++ ret = clk_prepare_enable(v3d->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "Couldn't enable the V3D clock\n"); ++ return ret; ++ } ++ + mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO); + mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)); + ret = dma_set_mask_and_coherent(dev, mask); + if (ret) +- return ret; ++ goto clk_disable; + + v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH); + +@@ -251,32 +261,29 @@ static int v3d_platform_drm_probe(struct + ret = PTR_ERR(v3d->reset); + + if (ret == -EPROBE_DEFER) +- return ret; ++ goto clk_disable; + + v3d->reset = NULL; + ret = map_regs(v3d, &v3d->bridge_regs, "bridge"); + if (ret) { + dev_err(dev, + "Failed to get reset control or bridge regs\n"); +- return ret; ++ goto clk_disable; + } + } + +- v3d->clk = devm_clk_get(dev, NULL); +- if (IS_ERR_OR_NULL(v3d->clk)) { +- if (PTR_ERR(v3d->clk) != -EPROBE_DEFER) +- dev_err(dev, "Failed to get clock (%ld)\n", PTR_ERR(v3d->clk)); +- return PTR_ERR(v3d->clk); +- } +- + node = rpi_firmware_find_node(); +- if (!node) +- return -EINVAL; ++ if (!node) { ++ ret = -EINVAL; ++ goto clk_disable; ++ } + + firmware = rpi_firmware_get(node); + of_node_put(node); +- if (!firmware) +- return -EPROBE_DEFER; ++ if (!firmware) { ++ ret = -EPROBE_DEFER; ++ goto clk_disable; ++ } + + v3d->clk_up_rate = rpi_firmware_clk_get_max_rate(firmware, + RPI_FIRMWARE_V3D_CLK_ID); +@@ -293,14 +300,15 @@ static int v3d_platform_drm_probe(struct + if (v3d->ver < 41) { + ret = map_regs(v3d, &v3d->gca_regs, "gca"); + if (ret) +- return ret; ++ goto clk_disable; + } + + v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr, + GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); + if (!v3d->mmu_scratch) { + dev_err(dev, "Failed to allocate MMU scratch page\n"); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto clk_disable; + } + + ret = v3d_gem_init(drm); +@@ -326,6 +334,8 @@ gem_destroy: + v3d_gem_destroy(drm); + dma_free: + dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); ++clk_disable: ++ clk_disable_unprepare(v3d->clk); + return ret; + } + +@@ -340,6 +350,8 @@ static void v3d_platform_drm_remove(stru + + dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch, + v3d->mmu_scratch_paddr); ++ ++ clk_disable_unprepare(v3d->clk); + } + + static struct platform_driver v3d_platform_driver = { diff --git a/target/linux/bcm27xx/patches-6.6/950-1552-drm-v3d-Set-job-pointer-to-NULL-when-the-job-s-fence.patch b/target/linux/bcm27xx/patches-6.6/950-1552-drm-v3d-Set-job-pointer-to-NULL-when-the-job-s-fence.patch new file mode 100644 index 000000000..3bf33ffc7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1552-drm-v3d-Set-job-pointer-to-NULL-when-the-job-s-fence.patch @@ -0,0 +1,84 @@ +From 131564261399a36a5cf2ac2731ed1ceffba93d10 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +Date: Sat, 22 Feb 2025 14:56:46 -0300 +Subject: [PATCH] drm/v3d: Set job pointer to NULL when the job's fence has an + error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Similar to commit e4b5ccd392b9 ("drm/v3d: Ensure job pointer is set to +NULL after job completion"), ensure the job pointer is set to `NULL` when +a job's fence has an error. Failing to do so can trigger kernel warnings +in specific scenarios, such as: + +1. v3d_csd_job_run() assigns `v3d->csd_job = job` +2. CSD job exceeds hang limit, causing a timeout → v3d_gpu_reset_for_timeout() +3. GPU reset +4. drm_sched_resubmit_jobs() sets the job's fence to `-ECANCELED`. +5. v3d_csd_job_run() detects the fence error and returns NULL, not + submitting the job to the GPU +6. User-space runs `modprobe -r v3d` +7. v3d_gem_destroy() + +v3d_gem_destroy() triggers a warning indicating that the CSD job never +ended, as we didn't set `v3d->csd_job` to NULL after the timeout. The same +can also happen to BIN, RENDER, and TFU jobs. + +Signed-off-by: Maíra Canal +--- + drivers/gpu/drm/v3d/v3d_sched.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -189,8 +189,12 @@ static struct dma_fence *v3d_bin_job_run + struct dma_fence *fence; + unsigned long irqflags; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ spin_lock_irqsave(&v3d->job_lock, irqflags); ++ v3d->bin_job = NULL; ++ spin_unlock_irqrestore(&v3d->job_lock, irqflags); + return NULL; ++ } + + /* Lock required around bin_job update vs + * v3d_overflow_mem_work(). +@@ -244,8 +248,10 @@ static struct dma_fence *v3d_render_job_ + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->render_job = NULL; + return NULL; ++ } + + v3d->render_job = job; + +@@ -292,8 +298,10 @@ v3d_tfu_job_run(struct drm_sched_job *sc + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->tfu_job = NULL; + return NULL; ++ } + + v3d->tfu_job = job; + +@@ -337,8 +345,10 @@ v3d_csd_job_run(struct drm_sched_job *sc + struct dma_fence *fence; + int i, csd_cfg0_reg, csd_cfg_reg_count; + +- if (unlikely(job->base.base.s_fence->finished.error)) ++ if (unlikely(job->base.base.s_fence->finished.error)) { ++ v3d->csd_job = NULL; + return NULL; ++ } + + v3d->csd_job = job; + diff --git a/target/linux/bcm27xx/patches-6.6/950-1553-drm-v3d-Associate-a-V3D-tech-revision-to-all-support.patch b/target/linux/bcm27xx/patches-6.6/950-1553-drm-v3d-Associate-a-V3D-tech-revision-to-all-support.patch new file mode 100644 index 000000000..a13516a04 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1553-drm-v3d-Associate-a-V3D-tech-revision-to-all-support.patch @@ -0,0 +1,447 @@ +From 97988373018e7fa7ff33b7774f88d30e48f71509 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +Date: Tue, 25 Feb 2025 20:44:59 -0300 +Subject: [PATCH] drm/v3d: Associate a V3D tech revision to all supported + devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The V3D driver currently determines the GPU tech version (33, 41...) +by reading a register. This approach has worked so far since this +information wasn’t needed before powering on the GPU. + +V3D 7.1 introduces new registers that must be written to power on the +GPU, requiring us to know the V3D version beforehand. To address this, +associate each supported SoC with the corresponding VideoCore GPU version +as part of the device data. + +To prevent possible mistakes, add an assertion to verify that the version +specified in the device data matches the one reported by the hardware. +If there is a mismatch, the kernel will trigger a warning. + +Signed-off-by: Maíra Canal +--- + drivers/gpu/drm/v3d/v3d_debugfs.c | 128 +++++++++++++++--------------- + drivers/gpu/drm/v3d/v3d_drv.c | 22 +++-- + drivers/gpu/drm/v3d/v3d_drv.h | 11 ++- + drivers/gpu/drm/v3d/v3d_gem.c | 12 +-- + drivers/gpu/drm/v3d/v3d_irq.c | 10 +-- + drivers/gpu/drm/v3d/v3d_sched.c | 12 +-- + 6 files changed, 106 insertions(+), 89 deletions(-) + +--- a/drivers/gpu/drm/v3d/v3d_debugfs.c ++++ b/drivers/gpu/drm/v3d/v3d_debugfs.c +@@ -22,74 +22,74 @@ struct v3d_reg_def { + }; + + static const struct v3d_reg_def v3d_hub_reg_defs[] = { +- REGDEF(33, 42, V3D_HUB_AXICFG), +- REGDEF(33, 71, V3D_HUB_UIFCFG), +- REGDEF(33, 71, V3D_HUB_IDENT0), +- REGDEF(33, 71, V3D_HUB_IDENT1), +- REGDEF(33, 71, V3D_HUB_IDENT2), +- REGDEF(33, 71, V3D_HUB_IDENT3), +- REGDEF(33, 71, V3D_HUB_INT_STS), +- REGDEF(33, 71, V3D_HUB_INT_MSK_STS), +- +- REGDEF(33, 71, V3D_MMU_CTL), +- REGDEF(33, 71, V3D_MMU_VIO_ADDR), +- REGDEF(33, 71, V3D_MMU_VIO_ID), +- REGDEF(33, 71, V3D_MMU_DEBUG_INFO), +- +- REGDEF(71, 71, V3D_V7_GMP_STATUS), +- REGDEF(71, 71, V3D_V7_GMP_CFG), +- REGDEF(71, 71, V3D_V7_GMP_VIO_ADDR), ++ REGDEF(V3D_GEN_33, V3D_GEN_42, V3D_HUB_AXICFG), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_UIFCFG), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_IDENT0), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_IDENT1), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_IDENT2), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_IDENT3), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_INT_STS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_HUB_INT_MSK_STS), ++ ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_MMU_CTL), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_MMU_VIO_ADDR), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_MMU_VIO_ID), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_MMU_DEBUG_INFO), ++ ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_GMP_STATUS), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_GMP_CFG), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_GMP_VIO_ADDR), + }; + + static const struct v3d_reg_def v3d_gca_reg_defs[] = { +- REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN), +- REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN_ACK), ++ REGDEF(V3D_GEN_33, V3D_GEN_33, V3D_GCA_SAFE_SHUTDOWN), ++ REGDEF(V3D_GEN_33, V3D_GEN_33, V3D_GCA_SAFE_SHUTDOWN_ACK), + }; + + static const struct v3d_reg_def v3d_core_reg_defs[] = { +- REGDEF(33, 71, V3D_CTL_IDENT0), +- REGDEF(33, 71, V3D_CTL_IDENT1), +- REGDEF(33, 71, V3D_CTL_IDENT2), +- REGDEF(33, 71, V3D_CTL_MISCCFG), +- REGDEF(33, 71, V3D_CTL_INT_STS), +- REGDEF(33, 71, V3D_CTL_INT_MSK_STS), +- REGDEF(33, 71, V3D_CLE_CT0CS), +- REGDEF(33, 71, V3D_CLE_CT0CA), +- REGDEF(33, 71, V3D_CLE_CT0EA), +- REGDEF(33, 71, V3D_CLE_CT1CS), +- REGDEF(33, 71, V3D_CLE_CT1CA), +- REGDEF(33, 71, V3D_CLE_CT1EA), +- +- REGDEF(33, 71, V3D_PTB_BPCA), +- REGDEF(33, 71, V3D_PTB_BPCS), +- +- REGDEF(33, 41, V3D_GMP_STATUS), +- REGDEF(33, 41, V3D_GMP_CFG), +- REGDEF(33, 41, V3D_GMP_VIO_ADDR), +- +- REGDEF(33, 71, V3D_ERR_FDBGO), +- REGDEF(33, 71, V3D_ERR_FDBGB), +- REGDEF(33, 71, V3D_ERR_FDBGS), +- REGDEF(33, 71, V3D_ERR_STAT), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_IDENT0), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_IDENT1), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_IDENT2), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_MISCCFG), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_INT_STS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CTL_INT_MSK_STS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT0CS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT0CA), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT0EA), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT1CS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT1CA), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_CLE_CT1EA), ++ ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_PTB_BPCA), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_PTB_BPCS), ++ ++ REGDEF(V3D_GEN_33, V3D_GEN_41, V3D_GMP_STATUS), ++ REGDEF(V3D_GEN_33, V3D_GEN_41, V3D_GMP_CFG), ++ REGDEF(V3D_GEN_33, V3D_GEN_41, V3D_GMP_VIO_ADDR), ++ ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_ERR_FDBGO), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_ERR_FDBGB), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_ERR_FDBGS), ++ REGDEF(V3D_GEN_33, V3D_GEN_71, V3D_ERR_STAT), + }; + + static const struct v3d_reg_def v3d_csd_reg_defs[] = { +- REGDEF(41, 71, V3D_CSD_STATUS), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG0), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG1), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG2), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG3), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG4), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG5), +- REGDEF(41, 41, V3D_CSD_CURRENT_CFG6), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG0), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG1), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG2), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG3), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG4), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG5), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG6), +- REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG7), ++ REGDEF(V3D_GEN_41, V3D_GEN_71, V3D_CSD_STATUS), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG0), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG1), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG2), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG3), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG4), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG5), ++ REGDEF(V3D_GEN_41, V3D_GEN_41, V3D_CSD_CURRENT_CFG6), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG0), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG1), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG2), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG3), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG4), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG5), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG6), ++ REGDEF(V3D_GEN_71, V3D_GEN_71, V3D_V7_CSD_CURRENT_CFG7), + }; + + static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) +@@ -165,7 +165,7 @@ static int v3d_v3d_debugfs_ident(struct + str_yes_no(ident2 & V3D_HUB_IDENT2_WITH_MMU)); + seq_printf(m, "TFU: %s\n", + str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TFU)); +- if (v3d->ver <= 42) { ++ if (v3d->ver <= V3D_GEN_42) { + seq_printf(m, "TSY: %s\n", + str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY)); + } +@@ -197,11 +197,11 @@ static int v3d_v3d_debugfs_ident(struct + seq_printf(m, " QPUs: %d\n", nslc * qups); + seq_printf(m, " Semaphores: %d\n", + V3D_GET_FIELD(ident1, V3D_IDENT1_NSEM)); +- if (v3d->ver <= 42) { ++ if (v3d->ver <= V3D_GEN_42) { + seq_printf(m, " BCG int: %d\n", + (ident2 & V3D_IDENT2_BCG_INT) != 0); + } +- if (v3d->ver < 40) { ++ if (v3d->ver < V3D_GEN_41) { + seq_printf(m, " Override TMU: %d\n", + (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0); + } +@@ -311,8 +311,8 @@ static int v3d_measure_clock(struct seq_ + int core = 0; + int measure_ms = 1000; + +- if (v3d->ver >= 40) { +- int cycle_count_reg = v3d->ver < 71 ? ++ if (v3d->ver >= V3D_GEN_41) { ++ int cycle_count_reg = v3d->ver < V3D_GEN_71 ? + V3D_PCTR_CYCLE_COUNT : V3D_V7_PCTR_CYCLE_COUNT; + V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3, + V3D_SET_FIELD(cycle_count_reg, +--- a/drivers/gpu/drm/v3d/v3d_drv.c ++++ b/drivers/gpu/drm/v3d/v3d_drv.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -88,7 +89,7 @@ static int v3d_get_param_ioctl(struct dr + args->value = 1; + return 0; + case DRM_V3D_PARAM_SUPPORTS_PERFMON: +- args->value = (v3d->ver >= 40); ++ args->value = (v3d->ver >= V3D_GEN_41); + return 0; + case DRM_V3D_PARAM_SUPPORTS_MULTISYNC_EXT: + args->value = 1; +@@ -189,10 +190,10 @@ static const struct drm_driver v3d_drm_d + }; + + static const struct of_device_id v3d_of_match[] = { +- { .compatible = "brcm,2712-v3d" }, +- { .compatible = "brcm,2711-v3d" }, +- { .compatible = "brcm,7268-v3d" }, +- { .compatible = "brcm,7278-v3d" }, ++ { .compatible = "brcm,2711-v3d", .data = (void *)V3D_GEN_42 }, ++ { .compatible = "brcm,2712-v3d", .data = (void *)V3D_GEN_71 }, ++ { .compatible = "brcm,7268-v3d", .data = (void *)V3D_GEN_33 }, ++ { .compatible = "brcm,7278-v3d", .data = (void *)V3D_GEN_41 }, + {}, + }; + MODULE_DEVICE_TABLE(of, v3d_of_match); +@@ -211,6 +212,7 @@ static int v3d_platform_drm_probe(struct + struct device_node *node; + struct drm_device *drm; + struct v3d_dev *v3d; ++ enum v3d_gen gen; + int ret; + u32 mmu_debug; + u32 ident1; +@@ -224,6 +226,9 @@ static int v3d_platform_drm_probe(struct + + platform_set_drvdata(pdev, drm); + ++ gen = (enum v3d_gen)of_device_get_match_data(dev); ++ v3d->ver = gen; ++ + ret = map_regs(v3d, &v3d->hub_regs, "hub"); + if (ret) + return ret; +@@ -253,6 +258,11 @@ static int v3d_platform_drm_probe(struct + ident1 = V3D_READ(V3D_HUB_IDENT1); + v3d->ver = (V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_TVER) * 10 + + V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_REV)); ++ /* Make sure that the V3D tech version retrieved from the HW is equal ++ * to the one advertised by the device tree. ++ */ ++ WARN_ON(v3d->ver != gen); ++ + v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES); + WARN_ON(v3d->cores > 1); /* multicore not yet implemented */ + +@@ -297,7 +307,7 @@ static int v3d_platform_drm_probe(struct + v3d->clk_down_rate = + (clk_get_rate(clk_get_parent(v3d->clk)) / (1 << 4)) + 10000; + +- if (v3d->ver < 41) { ++ if (v3d->ver < V3D_GEN_41) { + ret = map_regs(v3d, &v3d->gca_regs, "gca"); + if (ret) + goto clk_disable; +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -115,13 +115,20 @@ struct v3d_perfmon { + u64 values[]; + }; + ++enum v3d_gen { ++ V3D_GEN_33 = 33, ++ V3D_GEN_41 = 41, ++ V3D_GEN_42 = 42, ++ V3D_GEN_71 = 71, ++}; ++ + struct v3d_dev { + struct drm_device drm; + + /* Short representation (e.g. 33, 41) of the V3D tech version + * and revision. + */ +- int ver; ++ enum v3d_gen ver; + bool single_irq_line; + + void __iomem *hub_regs; +@@ -213,7 +220,7 @@ to_v3d_dev(struct drm_device *dev) + static inline bool + v3d_has_csd(struct v3d_dev *v3d) + { +- return v3d->ver >= 41; ++ return v3d->ver >= V3D_GEN_41; + } + + #define v3d_to_pdev(v3d) to_platform_device((v3d)->drm.dev) +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -69,7 +69,7 @@ v3d_init_core(struct v3d_dev *v3d, int c + * type. If you want the default behavior, you can still put + * "2" in the indirect texture state's output_type field. + */ +- if (v3d->ver < 40) ++ if (v3d->ver < V3D_GEN_41) + V3D_CORE_WRITE(core, V3D_CTL_MISCCFG, V3D_MISCCFG_OVRTMUOUT); + + /* Whenever we flush the L2T cache, we always want to flush +@@ -89,7 +89,7 @@ v3d_init_hw_state(struct v3d_dev *v3d) + static void + v3d_idle_axi(struct v3d_dev *v3d, int core) + { +- if (v3d->ver >= 71) ++ if (v3d->ver >= V3D_GEN_71) + return; + + V3D_CORE_WRITE(core, V3D_GMP_CFG, V3D_GMP_CFG_STOP_REQ); +@@ -105,7 +105,7 @@ v3d_idle_axi(struct v3d_dev *v3d, int co + static void + v3d_idle_gca(struct v3d_dev *v3d) + { +- if (v3d->ver >= 41) ++ if (v3d->ver >= V3D_GEN_41) + return; + + V3D_GCA_WRITE(V3D_GCA_SAFE_SHUTDOWN, V3D_GCA_SAFE_SHUTDOWN_EN); +@@ -179,13 +179,13 @@ v3d_reset(struct v3d_dev *v3d) + static void + v3d_flush_l3(struct v3d_dev *v3d) + { +- if (v3d->ver < 41) { ++ if (v3d->ver < V3D_GEN_41) { + u32 gca_ctrl = V3D_GCA_READ(V3D_GCA_CACHE_CTRL); + + V3D_GCA_WRITE(V3D_GCA_CACHE_CTRL, + gca_ctrl | V3D_GCA_CACHE_CTRL_FLUSH); + +- if (v3d->ver < 33) { ++ if (v3d->ver < V3D_GEN_33) { + V3D_GCA_WRITE(V3D_GCA_CACHE_CTRL, + gca_ctrl & ~V3D_GCA_CACHE_CTRL_FLUSH); + } +@@ -198,7 +198,7 @@ v3d_flush_l3(struct v3d_dev *v3d) + static void + v3d_invalidate_l2c(struct v3d_dev *v3d, int core) + { +- if (v3d->ver > 32) ++ if (v3d->ver >= V3D_GEN_33) + return; + + V3D_CORE_WRITE(core, V3D_CTL_L2CACTL, +--- a/drivers/gpu/drm/v3d/v3d_irq.c ++++ b/drivers/gpu/drm/v3d/v3d_irq.c +@@ -125,8 +125,8 @@ v3d_irq(int irq, void *arg) + status = IRQ_HANDLED; + } + +- if ((v3d->ver < 71 && (intsts & V3D_INT_CSDDONE)) || +- (v3d->ver >= 71 && (intsts & V3D_V7_INT_CSDDONE))) { ++ if ((v3d->ver < V3D_GEN_71 && (intsts & V3D_INT_CSDDONE)) || ++ (v3d->ver >= V3D_GEN_71 && (intsts & V3D_V7_INT_CSDDONE))) { + struct v3d_fence *fence = + to_v3d_fence(v3d->csd_job->base.irq_fence); + v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock(); +@@ -142,7 +142,7 @@ v3d_irq(int irq, void *arg) + /* We shouldn't be triggering these if we have GMP in + * always-allowed mode. + */ +- if (v3d->ver < 71 && (intsts & V3D_INT_GMPV)) ++ if (v3d->ver < V3D_GEN_71 && (intsts & V3D_INT_GMPV)) + dev_err(v3d->drm.dev, "GMP violation\n"); + + /* V3D 4.2 wires the hub and core IRQs together, so if we & +@@ -200,7 +200,7 @@ v3d_hub_irq(int irq, void *arg) + + V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL)); + +- if (v3d->ver >= 41) { ++ if (v3d->ver >= V3D_GEN_41) { + axi_id = axi_id >> 5; + if (axi_id < ARRAY_SIZE(v3d41_axi_ids)) + client = v3d41_axi_ids[axi_id]; +@@ -219,7 +219,7 @@ v3d_hub_irq(int irq, void *arg) + status = IRQ_HANDLED; + } + +- if (v3d->ver >= 71 && intsts & V3D_V7_HUB_INT_GMPV) { ++ if (v3d->ver >= V3D_GEN_71 && intsts & V3D_V7_HUB_INT_GMPV) { + dev_err(v3d->drm.dev, "GMP Violation\n"); + status = IRQ_HANDLED; + } +--- a/drivers/gpu/drm/v3d/v3d_sched.c ++++ b/drivers/gpu/drm/v3d/v3d_sched.c +@@ -288,7 +288,7 @@ static struct dma_fence *v3d_render_job_ + return fence; + } + +-#define V3D_TFU_REG(name) ((v3d->ver < 71) ? V3D_TFU_ ## name : V3D_V7_TFU_ ## name) ++#define V3D_TFU_REG(name) ((v3d->ver < V3D_GEN_71) ? V3D_TFU_ ## name : V3D_V7_TFU_ ## name) + + static struct dma_fence * + v3d_tfu_job_run(struct drm_sched_job *sched_job) +@@ -321,11 +321,11 @@ v3d_tfu_job_run(struct drm_sched_job *sc + V3D_WRITE(V3D_TFU_REG(ICA), job->args.ica); + V3D_WRITE(V3D_TFU_REG(IUA), job->args.iua); + V3D_WRITE(V3D_TFU_REG(IOA), job->args.ioa); +- if (v3d->ver >= 71) ++ if (v3d->ver >= V3D_GEN_71) + V3D_WRITE(V3D_V7_TFU_IOC, job->args.v71.ioc); + V3D_WRITE(V3D_TFU_REG(IOS), job->args.ios); + V3D_WRITE(V3D_TFU_REG(COEF0), job->args.coef[0]); +- if (v3d->ver >= 71 || (job->args.coef[0] & V3D_TFU_COEF0_USECOEF)) { ++ if (v3d->ver >= V3D_GEN_71 || (job->args.coef[0] & V3D_TFU_COEF0_USECOEF)) { + V3D_WRITE(V3D_TFU_REG(COEF1), job->args.coef[1]); + V3D_WRITE(V3D_TFU_REG(COEF2), job->args.coef[2]); + V3D_WRITE(V3D_TFU_REG(COEF3), job->args.coef[3]); +@@ -367,8 +367,8 @@ v3d_csd_job_run(struct drm_sched_job *sc + v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_CSD], sched_job); + v3d_switch_perfmon(v3d, &job->base); + +- csd_cfg0_reg = v3d->ver < 71 ? V3D_CSD_QUEUED_CFG0 : V3D_V7_CSD_QUEUED_CFG0; +- csd_cfg_reg_count = v3d->ver < 71 ? 6 : 7; ++ csd_cfg0_reg = v3d->ver < V3D_GEN_71 ? V3D_CSD_QUEUED_CFG0 : V3D_V7_CSD_QUEUED_CFG0; ++ csd_cfg_reg_count = v3d->ver < V3D_GEN_71 ? 6 : 7; + for (i = 1; i <= csd_cfg_reg_count; i++) + V3D_CORE_WRITE(0, csd_cfg0_reg + 4 * i, job->args.cfg[i]); + /* CFG0 write kicks off the job. */ +@@ -475,7 +475,7 @@ v3d_csd_job_timedout(struct drm_sched_jo + { + struct v3d_csd_job *job = to_csd_job(sched_job); + struct v3d_dev *v3d = job->base.v3d; +- u32 batches = V3D_CORE_READ(0, (v3d->ver < 71 ? V3D_CSD_CURRENT_CFG4 : ++ u32 batches = V3D_CORE_READ(0, (v3d->ver < V3D_GEN_71 ? V3D_CSD_CURRENT_CFG4 : + V3D_V7_CSD_CURRENT_CFG4)); + + /* If we've made progress, skip reset and let the timer get diff --git a/target/linux/bcm27xx/patches-6.6/950-1554-dt-bindings-gpu-v3d-Add-SMS-to-the-registers-list.patch b/target/linux/bcm27xx/patches-6.6/950-1554-dt-bindings-gpu-v3d-Add-SMS-to-the-registers-list.patch new file mode 100644 index 000000000..fb03d82e3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1554-dt-bindings-gpu-v3d-Add-SMS-to-the-registers-list.patch @@ -0,0 +1,43 @@ +From 08e99bc475ad907f3087e4f14c2ddb6560514da1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +Date: Sat, 22 Feb 2025 15:26:39 -0300 +Subject: [PATCH] dt-bindings: gpu: v3d: Add SMS to the registers' list +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +V3D 7.1 exposes a new register block, called V3D_SMS. As BCM2712 has a +V3D 7.1 core, add a new register item to the list. Similar to the GCA +and bridge register, SMS is optional and should only be added for V3D +7.1 variants. + +Cc: Krzysztof Kozlowski +Cc: Conor Dooley +Cc: Nicolas Saenz Julienne +Cc: devicetree@vger.kernel.org +Signed-off-by: Maíra Canal +--- + Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml ++++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml +@@ -27,14 +27,16 @@ properties: + - description: core0 register (required) + - description: GCA cache controller register (if GCA controller present) + - description: bridge register (if no external reset controller) ++ - description: SMS register (if SMS controller present) + minItems: 2 + + reg-names: + items: + - const: hub + - const: core0 +- - enum: [ bridge, gca ] +- - enum: [ bridge, gca ] ++ - enum: [ bridge, gca, sms ] ++ - enum: [ bridge, gca, sms ] ++ - enum: [ bridge, gca, sms ] + minItems: 2 + + interrupts: diff --git a/target/linux/bcm27xx/patches-6.6/950-1555-drm-v3d-Use-V3D_SMS-registers-for-power-on-off-and-r.patch b/target/linux/bcm27xx/patches-6.6/950-1555-drm-v3d-Use-V3D_SMS-registers-for-power-on-off-and-r.patch new file mode 100644 index 000000000..a39e7242d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1555-drm-v3d-Use-V3D_SMS-registers-for-power-on-off-and-r.patch @@ -0,0 +1,198 @@ +From 85e1a7592aa192d760159f9bc6e83d56fdf433f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +Date: Sat, 22 Feb 2025 15:49:49 -0300 +Subject: [PATCH] drm/v3d: Use V3D_SMS registers for power on/off and reset on + V3D 7.x +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In addition to the standard reset controller, V3D 7.x requires configuring +the V3D_SMS registers for proper power on/off and reset. Add the new +registers to `v3d_regs.h` and ensure they are properly configured during +device probing, removal, and reset. + +This change fixes GPU reset issues on the Raspberry Pi 5 (BCM2712). +Without exposing these registers, a GPU reset causes the GPU to hang, +stopping any further job execution and freezing the desktop GUI. The same +issue occurs when unloading and loading the v3d driver. + +Link: https://github.com/raspberrypi/linux/issues/6660 +Signed-off-by: Maíra Canal +--- + drivers/gpu/drm/v3d/v3d_drv.c | 40 ++++++++++++++++++++++++++++++++++ + drivers/gpu/drm/v3d/v3d_drv.h | 11 ++++++++++ + drivers/gpu/drm/v3d/v3d_gem.c | 17 +++++++++++++++ + drivers/gpu/drm/v3d/v3d_regs.h | 26 ++++++++++++++++++++++ + 4 files changed, 94 insertions(+) + +--- a/drivers/gpu/drm/v3d/v3d_drv.c ++++ b/drivers/gpu/drm/v3d/v3d_drv.c +@@ -198,6 +198,36 @@ static const struct of_device_id v3d_of_ + }; + MODULE_DEVICE_TABLE(of, v3d_of_match); + ++static void ++v3d_idle_sms(struct v3d_dev *v3d) ++{ ++ if (v3d->ver < V3D_GEN_71) ++ return; ++ ++ V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF); ++ ++ if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), ++ V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) { ++ DRM_ERROR("Failed to power up SMS\n"); ++ } ++ ++ v3d_reset_sms(v3d); ++} ++ ++static void ++v3d_power_off_sms(struct v3d_dev *v3d) ++{ ++ if (v3d->ver < V3D_GEN_71) ++ return; ++ ++ V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF); ++ ++ if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), ++ V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) { ++ DRM_ERROR("Failed to power off SMS\n"); ++ } ++} ++ + static int + map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name) + { +@@ -237,6 +267,12 @@ static int v3d_platform_drm_probe(struct + if (ret) + return ret; + ++ if (v3d->ver >= V3D_GEN_71) { ++ ret = map_regs(v3d, &v3d->sms_regs, "sms"); ++ if (ret) ++ return ret; ++ } ++ + v3d->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(v3d->clk)) + return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n"); +@@ -247,6 +283,8 @@ static int v3d_platform_drm_probe(struct + return ret; + } + ++ v3d_idle_sms(v3d); ++ + mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO); + mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)); + ret = dma_set_mask_and_coherent(dev, mask); +@@ -361,6 +399,8 @@ static void v3d_platform_drm_remove(stru + dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch, + v3d->mmu_scratch_paddr); + ++ v3d_power_off_sms(v3d); ++ + clk_disable_unprepare(v3d->clk); + } + +--- a/drivers/gpu/drm/v3d/v3d_drv.h ++++ b/drivers/gpu/drm/v3d/v3d_drv.h +@@ -135,6 +135,7 @@ struct v3d_dev { + void __iomem *core_regs[3]; + void __iomem *bridge_regs; + void __iomem *gca_regs; ++ void __iomem *sms_regs; + struct clk *clk; + struct delayed_work clk_down_work; + unsigned long clk_up_rate, clk_down_rate; +@@ -277,6 +278,15 @@ to_v3d_fence(struct dma_fence *fence) + #define V3D_GCA_READ(offset) readl(v3d->gca_regs + offset) + #define V3D_GCA_WRITE(offset, val) writel(val, v3d->gca_regs + offset) + ++#define V3D_SMS_IDLE 0x0 ++#define V3D_SMS_ISOLATING_FOR_RESET 0xa ++#define V3D_SMS_RESETTING 0xb ++#define V3D_SMS_ISOLATING_FOR_POWER_OFF 0xc ++#define V3D_SMS_POWER_OFF_STATE 0xd ++ ++#define V3D_SMS_READ(offset) readl(v3d->sms_regs + (offset)) ++#define V3D_SMS_WRITE(offset, val) writel(val, v3d->sms_regs + (offset)) ++ + #define V3D_CORE_READ(core, offset) readl(v3d->core_regs[core] + offset) + #define V3D_CORE_WRITE(core, offset, val) writel(val, v3d->core_regs[core] + offset) + +@@ -455,6 +465,7 @@ int v3d_wait_bo_ioctl(struct drm_device + struct drm_file *file_priv); + void v3d_job_cleanup(struct v3d_job *job); + void v3d_job_put(struct v3d_job *job); ++void v3d_reset_sms(struct v3d_dev *v3d); + void v3d_reset(struct v3d_dev *v3d); + void v3d_invalidate_caches(struct v3d_dev *v3d); + void v3d_clean_caches(struct v3d_dev *v3d); +--- a/drivers/gpu/drm/v3d/v3d_gem.c ++++ b/drivers/gpu/drm/v3d/v3d_gem.c +@@ -152,6 +152,22 @@ v3d_reset_v3d(struct v3d_dev *v3d) + } + + void ++v3d_reset_sms(struct v3d_dev *v3d) ++{ ++ if (v3d->ver < V3D_GEN_71) ++ return; ++ ++ V3D_SMS_WRITE(V3D_SMS_REE_CS, V3D_SET_FIELD(0x4, V3D_SMS_STATE)); ++ ++ if (wait_for(!(V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_REE_CS), ++ V3D_SMS_STATE) == V3D_SMS_ISOLATING_FOR_RESET) && ++ !(V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_REE_CS), ++ V3D_SMS_STATE) == V3D_SMS_RESETTING), 100)) { ++ DRM_ERROR("Failed to wait for SMS reset\n"); ++ } ++} ++ ++void + v3d_reset(struct v3d_dev *v3d) + { + struct drm_device *dev = &v3d->drm; +@@ -166,6 +182,7 @@ v3d_reset(struct v3d_dev *v3d) + v3d_idle_axi(v3d, 0); + + v3d_idle_gca(v3d); ++ v3d_reset_sms(v3d); + v3d_reset_v3d(v3d); + + v3d_mmu_set_page_table(v3d); +--- a/drivers/gpu/drm/v3d/v3d_regs.h ++++ b/drivers/gpu/drm/v3d/v3d_regs.h +@@ -543,4 +543,30 @@ + # define V3D_ERR_VPAERGS BIT(1) + # define V3D_ERR_VPAEABB BIT(0) + ++#define V3D_SMS_REE_CS 0x00000 ++#define V3D_SMS_TEE_CS 0x00400 ++# define V3D_SMS_INTERRUPT BIT(31) ++# define V3D_SMS_POWER_OFF BIT(30) ++# define V3D_SMS_CLEAR_POWER_OFF BIT(29) ++# define V3D_SMS_LOCK BIT(28) ++# define V3D_SMS_CLEAR_LOCK BIT(27) ++# define V3D_SMS_SVP_MODE_EXIT BIT(26) ++# define V3D_SMS_CLEAR_SVP_MODE_EXIT BIT(25) ++# define V3D_SMS_SVP_MODE_ENTER BIT(24) ++# define V3D_SMS_CLEAR_SVP_MODE_ENTER BIT(23) ++# define V3D_SMS_THEIR_MODE_EXIT BIT(22) ++# define V3D_SMS_THEIR_MODE_ENTER BIT(21) ++# define V3D_SMS_OUR_MODE_EXIT BIT(20) ++# define V3D_SMS_CLEAR_OUR_MODE_EXIT BIT(19) ++# define V3D_SMS_SEQ_PC_MASK V3D_MASK(16, 10) ++# define V3D_SMS_SEQ_PC_SHIFT 10 ++# define V3D_SMS_HUBCORE_STATUS_MASK V3D_MASK(9, 8) ++# define V3D_SMS_HUBCORE_STATUS_SHIFT 8 ++# define V3D_SMS_NEW_MODE_MASK V3D_MASK(7, 6) ++# define V3D_SMS_NEW_MODE_SHIFT 6 ++# define V3D_SMS_OLD_MODE_MASK V3D_MASK(5, 4) ++# define V3D_SMS_OLD_MODE_SHIFT 4 ++# define V3D_SMS_STATE_MASK V3D_MASK(3, 0) ++# define V3D_SMS_STATE_SHIFT 0 ++ + #endif /* V3D_REGS_H */ diff --git a/target/linux/bcm27xx/patches-6.6/950-1556-dts-bcm2712-Add-V3D_SMS-register.patch b/target/linux/bcm27xx/patches-6.6/950-1556-dts-bcm2712-Add-V3D_SMS-register.patch new file mode 100644 index 000000000..2d43d1c85 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1556-dts-bcm2712-Add-V3D_SMS-register.patch @@ -0,0 +1,27 @@ +From 3c21211667e35029054b0dfebdf292e4e7d5754b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +Date: Thu, 27 Feb 2025 21:00:42 -0300 +Subject: [PATCH] dts: bcm2712: Add V3D_SMS register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Maíra Canal +--- + arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi ++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +@@ -1222,8 +1222,9 @@ + v3d: v3d@2000000 { + compatible = "brcm,2712-v3d"; + reg = <0x10 0x02000000 0x0 0x4000>, +- <0x10 0x02008000 0x0 0x6000>; +- reg-names = "hub", "core0"; ++ <0x10 0x02008000 0x0 0x6000>, ++ <0x10 0x02030800 0x0 0x0700>; ++ reg-names = "hub", "core0", "sms"; + + power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; + resets = <&pm BCM2835_RESET_V3D>; diff --git a/target/linux/bcm27xx/patches-6.6/950-1557-dts-Remove-the-power-key-debounce-on-Pi-500.patch b/target/linux/bcm27xx/patches-6.6/950-1557-dts-Remove-the-power-key-debounce-on-Pi-500.patch new file mode 100644 index 000000000..faccc79c2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1557-dts-Remove-the-power-key-debounce-on-Pi-500.patch @@ -0,0 +1,21 @@ +From 08d4e8f52256bd422d8a1f876411603f627d0a82 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 11 Mar 2025 08:58:52 +0000 +Subject: [PATCH] dts: Remove the power key debounce on Pi 500 + +Signed-off-by: Phil Elwell +--- + 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 +@@ -7,7 +7,7 @@ + }; + + &pwr_key { +- debounce-interval = <400>; ++ debounce-interval = <0>; + }; + + &gio { diff --git a/target/linux/bcm27xx/patches-6.6/950-1558-media-i2c-imx477-Add-further-link-frequency-options.patch b/target/linux/bcm27xx/patches-6.6/950-1558-media-i2c-imx477-Add-further-link-frequency-options.patch new file mode 100644 index 000000000..8ed80cd20 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1558-media-i2c-imx477-Add-further-link-frequency-options.patch @@ -0,0 +1,77 @@ +From 0553897d77e849a86e836ddf1e0c0dbbd8e64f83 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 20 Jan 2025 10:40:09 +0000 +Subject: [PATCH] media: i2c: imx477: Add further link frequency options + +https://github.com/raspberrypi/linux/issues/6004 reports further +issues with GPS interference. + +Untested, but adds further link frequency options. + +Signed-off-by: Dave Stevenson +--- + drivers/media/i2c/imx477.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/media/i2c/imx477.c ++++ b/drivers/media/i2c/imx477.c +@@ -169,12 +169,18 @@ enum { + IMX477_LINK_FREQ_450MHZ, + IMX477_LINK_FREQ_453MHZ, + IMX477_LINK_FREQ_456MHZ, ++ IMX477_LINK_FREQ_459MHZ, ++ IMX477_LINK_FREQ_462MHZ, ++ IMX477_LINK_FREQ_498MHZ, + }; + + static const s64 link_freqs[] = { + [IMX477_LINK_FREQ_450MHZ] = 450000000, + [IMX477_LINK_FREQ_453MHZ] = 453000000, + [IMX477_LINK_FREQ_456MHZ] = 456000000, ++ [IMX477_LINK_FREQ_459MHZ] = 459000000, ++ [IMX477_LINK_FREQ_462MHZ] = 462000000, ++ [IMX477_LINK_FREQ_498MHZ] = 498000000, + }; + + /* 450MHz is the nominal "default" link frequency */ +@@ -193,6 +199,21 @@ static const struct imx477_reg link_456M + {0x030F, 0x98}, + }; + ++static const struct imx477_reg link_459Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x99}, ++}; ++ ++static const struct imx477_reg link_462Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0x9a}, ++}; ++ ++static const struct imx477_reg link_498Mhz_regs[] = { ++ {0x030E, 0x00}, ++ {0x030F, 0xa6}, ++}; ++ + static const struct imx477_reg_list link_freq_regs[] = { + [IMX477_LINK_FREQ_450MHZ] = { + .regs = link_450Mhz_regs, +@@ -206,6 +227,18 @@ static const struct imx477_reg_list link + .regs = link_456Mhz_regs, + .num_of_regs = ARRAY_SIZE(link_456Mhz_regs) + }, ++ [IMX477_LINK_FREQ_459MHZ] = { ++ .regs = link_459Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_459Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_462MHZ] = { ++ .regs = link_462Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_462Mhz_regs) ++ }, ++ [IMX477_LINK_FREQ_498MHZ] = { ++ .regs = link_498Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_498Mhz_regs) ++ }, + }; + + static const struct imx477_reg mode_common_regs[] = { diff --git a/target/linux/bcm27xx/patches-6.6/950-1559-overlays-Fix-some-unusable-fragments.patch b/target/linux/bcm27xx/patches-6.6/950-1559-overlays-Fix-some-unusable-fragments.patch new file mode 100644 index 000000000..bebe05555 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.6/950-1559-overlays-Fix-some-unusable-fragments.patch @@ -0,0 +1,82 @@ +From 9da8d6df2051478f0ba16d73c65995955c19cb3a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 18 Mar 2025 13:09:11 +0000 +Subject: [PATCH] overlays: Fix some unusable fragments + +A forthcoming overlaycheck update looks for dormant fragments with no +parameters to enable them. The test discovered some real errors, which +this patch fixes, and one case where some fragments aren't yet being +used, which this comments out until they are. + +Signed-off-by: Phil Elwell +--- + arch/arm/boot/dts/overlays/rpi-poe-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/sx150x-overlay.dts | 2 +- + arch/arm/boot/dts/overlays/vc4-kms-dpi-panel-overlay.dts | 4 ++++ + 4 files changed, 7 insertions(+), 3 deletions(-) + +--- a/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts +@@ -145,7 +145,7 @@ + poe_fan_temp2_hyst = <&trip2>,"hysteresis:0"; + poe_fan_temp3 = <&trip3>,"temperature:0"; + poe_fan_temp3_hyst = <&trip3>,"hysteresis:0"; +- i2c = <0>, "+5+6", ++ i2c = <0>, "+7+8", + <&fwpwm>,"status=disabled", + <&i2c_bus>,"status=okay", + <&poe_mfd>,"status=okay", +--- a/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts ++++ b/arch/arm/boot/dts/overlays/rpi-poe-plus-overlay.dts +@@ -28,7 +28,7 @@ + }; + + __overrides__ { +- i2c = <0>, "+5+6", ++ i2c = <0>, "+7+8", + <&fwpwm>,"status=disabled", + <&rpi_poe_power_supply>,"status=disabled", + <&i2c_bus>,"status=okay", +--- a/arch/arm/boot/dts/overlays/sx150x-overlay.dts ++++ b/arch/arm/boot/dts/overlays/sx150x-overlay.dts +@@ -1681,7 +1681,7 @@ + sx1507-1-3E-int-gpio = <0>,"+67+99", <&sx150x_1_3E_pins>,"brcm,pins:0", <&sx1507_1_3E>,"interrupts:0"; + sx1507-0-3F-int-gpio = <0>,"+68+100", <&sx150x_0_3F_pins>,"brcm,pins:0", <&sx1507_0_3F>,"interrupts:0"; + sx1507-1-3F-int-gpio = <0>,"+69+101", <&sx150x_1_3F_pins>,"brcm,pins:0", <&sx1507_1_3F>,"interrupts:0"; +- sx1507-0-70-int-gpio = <0>,"+60+102", <&sx150x_0_70_pins>,"brcm,pins:0", <&sx1507_0_70>,"interrupts:0"; ++ sx1507-0-70-int-gpio = <0>,"+70+102", <&sx150x_0_70_pins>,"brcm,pins:0", <&sx1507_0_70>,"interrupts:0"; + sx1507-1-70-int-gpio = <0>,"+71+103", <&sx150x_1_70_pins>,"brcm,pins:0", <&sx1507_1_70>,"interrupts:0"; + sx1507-0-71-int-gpio = <0>,"+72+104", <&sx150x_0_71_pins>,"brcm,pins:0", <&sx1507_0_71>,"interrupts:0"; + sx1507-1-71-int-gpio = <0>,"+73+105", <&sx150x_1_71_pins>,"brcm,pins:0", <&sx1507_1_71>,"interrupts:0"; +--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-panel-overlay.dts ++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-panel-overlay.dts +@@ -42,24 +42,28 @@ + pinctrl-0 = <&dpi_18bit_gpio0>; + }; + }; ++#if 0 + fragment@92 { + target = <&dpi>; + __dormant__ { + pinctrl-0 = <&dpi_gpio0>; + }; + }; ++#endif + fragment@93 { + target = <&dpi>; + __dormant__ { + pinctrl-0 = <&dpi_16bit_cpadhi_gpio0>; + }; + }; ++#if 0 + fragment@94 { + target = <&dpi>; + __dormant__ { + pinctrl-0 = <&dpi_16bit_gpio0>; + }; + }; ++#endif + + __overrides__ { + at056tn53v1 = <0>, "+0+90"; diff --git a/target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch b/target/linux/bcm27xx/patches-6.6/950-1560-dts-rp1-Don-t-use-DMA-with-UARTs.patch similarity index 54% rename from target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch rename to target/linux/bcm27xx/patches-6.6/950-1560-dts-rp1-Don-t-use-DMA-with-UARTs.patch index c809c4f10..df19f4cac 100644 --- a/target/linux/bcm27xx/patches-6.6/950-1267-dts-rp1-Disable-DMA-usage-for-UART0.patch +++ b/target/linux/bcm27xx/patches-6.6/950-1560-dts-rp1-Don-t-use-DMA-with-UARTs.patch @@ -1,16 +1,15 @@ -From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001 +From bba53a117a4a5c29da892962332ff1605990e17a Mon Sep 17 00:00:00 2001 From: Phil Elwell -Date: Wed, 18 Sep 2024 16:45:24 +0100 -Subject: [PATCH 1267/1350] dts: rp1: Disable DMA usage for UART0 +Date: Wed, 26 Mar 2025 11:28:28 +0000 +Subject: [PATCH] dts: rp1: Don't use DMA with UARTs -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. +DMA has been enabled on RP1's UART0, but with mixed success. Transmits +seem to work, but the DMA interface is not well suited to receiving +arbitrary amounts of data. In particular, the PL011 driver is slow to +pass on the received data, batching it into large blocks. -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 +On balance, it's better to just disable the DMA support. As with the +other UARTs, the required runes are left in the DTS as comments. Signed-off-by: Phil Elwell --- @@ -19,7 +18,7 @@ Signed-off-by: Phil Elwell --- a/arch/arm64/boot/dts/broadcom/rp1.dtsi +++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi -@@ -55,9 +55,9 @@ +@@ -65,9 +65,9 @@ interrupts = ; clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>; clock-names = "uartclk", "apb_pclk"; @@ -30,5 +29,5 @@ Signed-off-by: Phil Elwell + // <&rp1_dma RP1_DMA_UART0_RX>; + // dma-names = "tx", "rx"; pinctrl-names = "default"; - arm,primecell-periphid = <0x00541011>; + arm,primecell-periphid = <0x00341011>; uart-has-rtscts;