+++ /dev/null
-From fa846f5991de6c37db1f40e26a39f52c130f2bc7 Mon Sep 17 00:00:00 2001
-From: Jonathan Bell <jonathan@raspberrypi.com>
-Date: Mon, 13 Sep 2021 11:14:32 +0100
-Subject: [PATCH 0519/1085] usb: dwc3: Set DMA and coherent masks early
-
-dwc3 allocates scratch and event buffers in the top-level driver. Hack the
-probe function to set the DMA mask before trying to allocate these.
-
-I think the event buffers are only used in device mode, but the scratch
-buffers may be used if core hibernation is enabled.
-
-usb: dwc3: add support for new DT quirks
-
-Apply the optional axi-pipe-limit and dis-in-autoretry-quirk properties
-during driver probe.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
-
-phy: phy-brcm-usb: Add 2712 support
-
-usb: dwc3: if the host controller instance number is present in DT, use it
-
-If two instances of a dwc3 host controller are specified in devicetree,
-then the probe order may be arbitrary which results in the device names
-swapping on a per-boot basis.
-
-If a "usb" alias with the instance number is specified, then use
-that to construct the device name instead of autogenerating one.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
-
-rp1 dwc3 changes
-
-drivers: usb: dwc3: allow setting GTXTHRCFG on dwc_usb3.0 hardware
-
-Equivalent register fields exist in the SuperSpeed Host version of the
-hardware, so allow the use of TX thresholds if specified in devicetree.
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
-
-drivers: usb: dwc3: remove downstream quirk dis-in-autoretry
-
-Upstream have unilaterally disabled the feature.
-
-Partially reverts 6e9142a26ee0fdc3a5adc49ed6cedc0b16ec2ed1 (downstream)
-
-Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
----
- drivers/phy/broadcom/Kconfig | 2 +-
- .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 59 +++++++++++++++++++
- drivers/phy/broadcom/phy-brcm-usb-init.h | 2 +
- drivers/phy/broadcom/phy-brcm-usb.c | 18 +++++-
- drivers/usb/dwc3/core.c | 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;
-@@ -1377,6 +1397,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:
-@@ -1520,6 +1558,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;
-@@ -1540,6 +1579,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);
-@@ -1654,6 +1696,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;
-
-@@ -1671,6 +1716,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;
- }
-
-@@ -1963,6 +2010,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)
-@@ -1067,6 +1070,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
-@@ -1304,6 +1308,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;
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
-@@ -1387,6 +1387,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;
if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) &&
(dwc->maximum_speed == USB_SPEED_HIGH ||
dwc->maximum_speed == USB_SPEED_FULL))
-@@ -1676,6 +1679,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");
--- 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)
#define DWC3_GUCTL1_RESUME_OPMODE_HS_HOST BIT(10)
/* Global Status Register */
-@@ -1122,10 +1123,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.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
-@@ -1352,6 +1355,7 @@ struct dwc3 {
+@@ -1347,6 +1350,7 @@ struct dwc3 {
unsigned ulpi_ext_vbus_drv:1;
unsigned parkmode_disable_ss_quirk:1;
unsigned parkmode_disable_hs_quirk:1;
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
-@@ -1381,6 +1381,12 @@ static int dwc3_core_init(struct dwc3 *d
+@@ -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->parkmode_disable_ss_quirk)
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
-@@ -1675,6 +1681,10 @@ static void dwc3_get_properties(struct d
+@@ -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->parkmode_disable_hs_quirk = device_property_read_bool(dev,
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
-@@ -269,6 +269,8 @@
+@@ -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_PARKMODE_DISABLE_SS BIT(17)
#define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16)
#define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS BIT(15)
-@@ -1123,6 +1125,8 @@ struct dwc3_scratchpad_array {
+@@ -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.
* @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
-@@ -1353,6 +1357,8 @@ struct dwc3 {
+@@ -1348,6 +1352,8 @@ struct dwc3 {
unsigned dis_tx_ipgap_linecheck_quirk:1;
unsigned resume_hs_terminations:1;
unsigned ulpi_ext_vbus_drv:1;
--- /dev/null
+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,<param>=<val>
+ 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,<param>=<val>
++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";
++ };
++
++};
--- /dev/null
+From b3dd7e8947cddec41864e8d3ce5f5d8b81033d6a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Reviewed-by: Alexander Stein <alexander.stein@ew.tq-group.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ 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",
+ },
+ };
--- /dev/null
+From f4f17c3fe223b3d8ad65f5420abbcd69ef901186 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ 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
+
--- /dev/null
+From 23037697914a6d1220768a752c6358d35ca03737 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+---
+ 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 */ },
+ };
--- /dev/null
+From c633c2e93e460925120e0817c14bbfc444a70226 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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 <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
+ #include <linux/module.h>
++#include <linux/moduleparam.h>
+ #include <linux/of.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+@@ -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;
+ }
--- /dev/null
+From eec7048c4e3aec1aadc21fcffcf6be9f5385f72a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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";
+ };
+ };
+
--- /dev/null
+From 0a0814f830829b1a377273ddb09c156c84e1a8ca Mon Sep 17 00:00:00 2001
+From: Alexander Stein <alexander.stein@ew.tq-group.com>
+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 <alexander.stein@ew.tq-group.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+---
+ 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 */
--- /dev/null
+From 384e58f63a3a328f7f656052b6357f3408aac6c3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Dec 2024 20:33:46 +0000
+Subject: [PATCH] dtoverlays: Add overlay for Sony IMX415 image sensor
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ 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,<param>
++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 <dt-bindings/gpio/gpio.h>
++
++/{
++ 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>;
++ };
++ };
++};
--- /dev/null
+From 4e8d73ce89c6dd6fdcb8dd7df8310762707c5b1a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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);
+
--- /dev/null
+From 6722e1358768671c1e5761aa092efb4ae62b2c46 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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) {
--- /dev/null
+From 0292614f8cd061f71f975dae7d74fe5324545321 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From 87fc066350358ce45f5ad52424c8a2e351b1720c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 7 Jan 2025 12:05:41 +0000
+Subject: [PATCH] dts: bcm2711: PL011 UARTs are actually r1p5
+
+The ARM PL011 UART instances in BCM2711 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 <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711.dtsi | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+@@ -134,7 +134,7 @@
+ clocks = <&clocks BCM2835_CLOCK_UART>,
+ <&clocks BCM2835_CLOCK_VPU>;
+ clock-names = "uartclk", "apb_pclk";
+- arm,primecell-periphid = <0x00241011>;
++ arm,primecell-periphid = <0x00341011>;
+ status = "disabled";
+ };
+
+@@ -145,7 +145,7 @@
+ clocks = <&clocks BCM2835_CLOCK_UART>,
+ <&clocks BCM2835_CLOCK_VPU>;
+ clock-names = "uartclk", "apb_pclk";
+- arm,primecell-periphid = <0x00241011>;
++ arm,primecell-periphid = <0x00341011>;
+ status = "disabled";
+ };
+
+@@ -156,7 +156,7 @@
+ clocks = <&clocks BCM2835_CLOCK_UART>,
+ <&clocks BCM2835_CLOCK_VPU>;
+ clock-names = "uartclk", "apb_pclk";
+- arm,primecell-periphid = <0x00241011>;
++ arm,primecell-periphid = <0x00341011>;
+ status = "disabled";
+ };
+
+@@ -167,7 +167,7 @@
+ clocks = <&clocks BCM2835_CLOCK_UART>,
+ <&clocks BCM2835_CLOCK_VPU>;
+ clock-names = "uartclk", "apb_pclk";
+- arm,primecell-periphid = <0x00241011>;
++ arm,primecell-periphid = <0x00341011>;
+ status = "disabled";
+ };
+
+@@ -1155,6 +1155,7 @@
+ };
+
+ &uart0 {
++ arm,primecell-periphid = <0x00341011>;
+ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
--- /dev/null
+From 602be52637ecca0b247cf832c8a4ec345844d325 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
+ };
+
--- /dev/null
+From 7a4972f36a63eba4d862115265a7bf77ac1e4129 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From 4f7341263ebd6ab2ae805c8f27191d24abc05a62 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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 */
--- /dev/null
+From 4111f4ede92b1f5bf869f87c66fc39151999c42f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
+
--- /dev/null
+From 62c33972e3eb724d80179fb71b05e923920f0b0d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
--- /dev/null
+From 301420a8cc44ee457670e015adc3a08cfc4531a9 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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";
+ };
--- /dev/null
+From d70a60eb03ae4fc687b91b5f6c4684000be21c5f Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 */
--- /dev/null
+From eb4d8ffb2b007963662be7eca88baf0e5c358bd6 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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
+ };
+
--- /dev/null
+From e72f42ebc9a236c023f8027a37c9351d58e28b05 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+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 <jonathan@raspberrypi.com>
+---
+ 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 */
+
--- /dev/null
+From e56aa0bd2b552daa4349a7eb2e6b0dec81d3e5cc Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From dafde0ac8b6d4b21578a677c8afad8714af47aaf Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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)
--- /dev/null
+From d06cb3534b6553a1f76bef2ddaf833e23dc12a4c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711.dtsi | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2 --
+ 2 files changed, 4 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711.dtsi
++++ b/arch/arm/boot/dts/broadcom/bcm2711.dtsi
+@@ -451,8 +451,6 @@
+ IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
+ IRQ_TYPE_LEVEL_LOW)>;
+- /* This only applies to the ARMv7 stub */
+- arm,cpu-registers-not-fw-configured;
+ };
+
+ cpus: cpus {
+--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+@@ -741,8 +741,6 @@
+ IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
+ IRQ_TYPE_LEVEL_LOW)>;
+- /* This only applies to the ARMv7 stub */
+- arm,cpu-registers-not-fw-configured;
+ };
+
+ cpus: cpus {
--- /dev/null
+From dfff38316c1284c30c68d02cc424bad0562cf253 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+[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;
--- /dev/null
+From a73ecafc5532e31b184220149cc2863f625700bf Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ .../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,
--- /dev/null
+From a452251cc286f2799969f047698c76fe3d7862b9 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ .../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));
+
+ /**
--- /dev/null
+From 5b958efc20d381ee103103df5df0c88dc02ada18 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <peyton.howe@bellsouth.net>
+Signed-off-by: Peyton Howe <peyton.howe@bellsouth.net>
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ 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;
+ }
+
--- /dev/null
+From e3297f3fbffdaec8076c00167261f504bb2c64b6 Mon Sep 17 00:00:00 2001
+From: Peyton Howe <peyton.howe@bellsouth.net>
+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 <peyton.howe@bellsouth.net>
+---
+ 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";
+ };
+ };
+
--- /dev/null
+From 11381ac246576bc84dfc28f6cdd8030305c605aa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ .../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";
--- /dev/null
+From ba9883e3b667e4f1fbeacc4346f6e9179a3a5479 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+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 <nick.hollinghurst@raspberrypi.com>
+---
+ 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);
--- /dev/null
+From 94de19285cc9bd781394bf1aa4fb8d401aac5195 Mon Sep 17 00:00:00 2001
+From: James Sarrett <jsarrett@gmail.com>
+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";
+ };
+ };
--- /dev/null
+From aabe16b3cdc6172618803ed7a6002d1099d306e5 Mon Sep 17 00:00:00 2001
+From: James Sarrett <jsarrett@gmail.com>
+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",
--- /dev/null
+From a18d9ced4965462cb7b3b4252ada440395105308 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+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 <naush@raspberrypi.com>
+---
+ .../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 <linux/types.h>
+
+-#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 <linux/firmware.h>
+
+
+-#define BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK (0x04000000)
+-#define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000)
+-#define BCS_ISTATUS_HOST_DRIVER_DOWN (0x08000000)
+-#define BCS_ISTATUS_SOC_CONNECT_ACCEPTED (0x10000000)
+-#define BCS_ISTATUS_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 <linux/kconfig.h>
+ #include <linux/printk.h>
+
+-
+-#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 <linux/pagemap.h>
+ #include <linux/firmware.h>
+ #include <linux/kthread.h>
++#include <linux/delay.h>
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
+ #include <linux/dma-direct.h>
+@@ -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 <linux/pci-aspm.h>
+@@ -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 <linux/ioctl.h>
+
++#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 <linux/uaccess.h>
+
+-#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 <linux/sched.h>
+ #include <linux/module.h>
+
+-
+-#define SGL_MAX_SEGMENT_SIZE (0x10000)
+ // See linux/mm.h
+ #define MMIO_AND_NO_PAGES_VMA_MASK (VM_IO | VM_PFNMAP)
++// 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);
--- /dev/null
+From fa71765fd3c98fa170407e72e052004913da6874 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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";
--- /dev/null
+From a320d39840745502d420f56b3d49b3723a12058f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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,<param>[=<val>
+ 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: <None>
+
+
+-Name: i2c-fan
+-Info: Adds support for a number of I2C fan controllers
+-Load: dtoverlay=i2c-fan,<param>=<val>
+-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: <Documentation>
++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,<param>=<val>
++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,<param>=<val>
+ 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,<param>=<val>
+ 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,<param>=<val>
+ 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,<param>=<val>
+ 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,<param>=<val>
+ 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 <dt-bindings/thermal/thermal.h>
+
++#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 <dt-bindings/mux/mux.h>
+
++#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=", <MUX_IDLE_DISCONNECT>,
+ <&pca9545>,"idle-state:0=", <MUX_IDLE_DISCONNECT>,
+--- 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";
--- /dev/null
+From 8a08b4ad6dbd48a826b3052e52a4fdc88c3ac36e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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)
--- /dev/null
+From 54a442deb925c37346abbbcc566234757925910c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
+ }
+
--- /dev/null
+From e0a21a407b78477aa530800255d53405950cb1fb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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[] = {
--- /dev/null
+From 3f60adc5ce8238996ec43d2b76a6e38e46f8271b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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)
--- /dev/null
+From 4d577c42b47dfffda80da995fafd5b16fdb242e2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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);
+
--- /dev/null
+From fab0093d88452972f05f4b13e91e31d00b55421a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From 2cdd83392ba025cc88072c0153d443fc59919960 Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+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 <joerg@hifiberry.com>
+---
+ 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: <None>
+
+ 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: <None>
+
+--- 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 <dt-bindings/gpio/gpio.h>
++
+ / {
+ 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";
+ };
+ };
--- /dev/null
+From d664f45f77423b03e5a435e480822075c03331bd Mon Sep 17 00:00:00 2001
+From: j-schambacher <joerg@hifiberry.com>
+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 <joerg@hifiberry.com>
+---
+ 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),
+ },
+ };
+
--- /dev/null
+From 1b1e75ae7d69816609a95ff2fb92729a2ff04d8e Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+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;
--- /dev/null
+From 081eebdeccfd12e0aaba4b64c9f87b608777913b Mon Sep 17 00:00:00 2001
+From: Micke Prag <micke.prag@gmail.com>
+Date: Thu, 13 Feb 2025 22:08:43 +0100
+Subject: [PATCH] overlays: Add OpenHydroponics RootMaster overlay
+
+Signed-off-by: Micke Prag <micke.prag@gmail.com>
+---
+ 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: <None>
++
++
+ Name: rotary-encoder
+ Info: Overlay for GPIO connected rotary encoder.
+ Load: dtoverlay=rotary-encoder,<param>=<val>
+--- /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 <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/pinctrl/bcm2835.h>
++
++/ {
++ 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 = <BCM2835_FSEL_GPIO_IN>;
++ };
++ };
++ };
++ 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>;
++ };
++ };
++ };
++};
--- /dev/null
+From 011cbf22d7583687ae18690185169e5da0be000a Mon Sep 17 00:00:00 2001
+From: Yuriy Pasichnyk <yurijpasichnyk11@gmail.com>
+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 <yurijpasichnyk11@gmail.com>
+---
+ 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;
+ }
+
--- /dev/null
+From af9965a855a8c6c7140bbcccfa89eec7e90a993d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ 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;
--- /dev/null
+From a4e7897eef1ad4628572a6abfd30291209a0bf43 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+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 <richard.oliver@raspberrypi.com>
+---
+ 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;
++ };
+ };
+
+ / {
--- /dev/null
+From f8f3e202fb083906fbc98ef6ea445e424779c076 Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+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 <richard.oliver@raspberrypi.com>
+---
+ 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;
+ }
--- /dev/null
+From 2e071057fded90e789c0101498e45a1778be93fe Mon Sep 17 00:00:00 2001
+From: Richard Oliver <richard.oliver@raspberrypi.com>
+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 <richard.oliver@raspberrypi.com>
+---
+ 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);
--- /dev/null
+From 1337c39626c14ca5e61b4cddbc097d87212a0b13 Mon Sep 17 00:00:00 2001
+From: KubaTaba1uga <luna7337anul@proton.me>
+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.");
--- /dev/null
+From f1076a9d7a269d72b6707283560d0d38203cb07a Mon Sep 17 00:00:00 2001
+From: KubaTaba1uga <luna7337anul@proton.me>
+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};
--- /dev/null
+From ac56a225e0889e60e912ecd3a51333a5aee901fc Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+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 <dave.stevenson@raspberrypi.com>
+---
+ 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";
+ };
+ };
+
--- /dev/null
+From 32c319ba2f2fd662a3b7bd042515cd650807dbff Mon Sep 17 00:00:00 2001
+From: Stefan Wahren <wahrenst@gmx.net>
+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 <wahrenst@gmx.net>
+Reviewed-by: Maíra Canal <mcanal@igalia.com>
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+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 <mcanal@igalia.com>
+---
+ 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 = {
--- /dev/null
+From 5258ca4ad089548a72657522443b9c3e46fd125b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Sat, 22 Feb 2025 14:40:21 -0300
+Subject: [PATCH] drm/v3d: Don't run jobs that have errors flagged in its fence
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The V3D driver still relies on `drm_sched_increase_karma()` and
+`drm_sched_resubmit_jobs()` for resubmissions when a timeout occurs.
+The function `drm_sched_increase_karma()` marks the job as guilty, while
+`drm_sched_resubmit_jobs()` sets an error (-ECANCELED) in the DMA fence of
+that guilty job.
+
+Because of this, we must check whether the job’s DMA fence has been
+flagged with an error before executing the job. Otherwise, the same guilty
+job may be resubmitted indefinitely, causing repeated GPU resets.
+
+This patch adds a check for an error on the job's fence to prevent running
+a guilty job that was previously flagged when the GPU timed out.
+
+Note that the CPU and CACHE_CLEAN queues do not require this check, as
+their jobs are executed synchronously once the DRM scheduler starts them.
+
+Cc: stable@vger.kernel.org
+Fixes: d223f98f0209 ("drm/v3d: Add support for compute shader dispatch.")
+Fixes: 1584f16ca96e ("drm/v3d: Add support for submitting jobs to the TFU.")
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+---
+ drivers/gpu/drm/v3d/v3d_sched.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_sched.c
++++ b/drivers/gpu/drm/v3d/v3d_sched.c
+@@ -292,11 +292,15 @@ 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))
++ return NULL;
++
++ v3d->tfu_job = job;
++
+ fence = v3d_fence_create(v3d, V3D_TFU);
+ if (IS_ERR(fence))
+ return NULL;
+
+- v3d->tfu_job = job;
+ if (job->base.irq_fence)
+ dma_fence_put(job->base.irq_fence);
+ job->base.irq_fence = dma_fence_get(fence);
+@@ -333,6 +337,9 @@ 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))
++ return NULL;
++
+ v3d->csd_job = job;
+
+ v3d_invalidate_caches(v3d);
--- /dev/null
+From 131564261399a36a5cf2ac2731ed1ceffba93d10 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+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 <mcanal@igalia.com>
+---
+ 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;
+
--- /dev/null
+From 97988373018e7fa7ff33b7774f88d30e48f71509 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+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 <mcanal@igalia.com>
+---
+ 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 <linux/dma-mapping.h>
+ #include <linux/io.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset.h>
+@@ -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
--- /dev/null
+From 08e99bc475ad907f3087e4f14c2ddb6560514da1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+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 <krzk+dt@kernel.org>
+Cc: Conor Dooley <conor+dt@kernel.org>
+Cc: Nicolas Saenz Julienne <nsaenz@kernel.org>
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+---
+ 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:
--- /dev/null
+From 85e1a7592aa192d760159f9bc6e83d56fdf433f3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+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 <mcanal@igalia.com>
+---
+ 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 */
--- /dev/null
+From 3c21211667e35029054b0dfebdf292e4e7d5754b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+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 <mcanal@igalia.com>
+---
+ 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>;
--- /dev/null
+From 08d4e8f52256bd422d8a1f876411603f627d0a82 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+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 <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-500.dts
+@@ -7,7 +7,7 @@
+ };
+
+ &pwr_key {
+- debounce-interval = <400>;
++ debounce-interval = <0>;
+ };
+
+ &gio {