CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
CONFIG_PWM_SYSFS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RASPBERRYPI_FIRMWARE=y
# CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
CONFIG_SG_POOL=y
CONFIG_SMSC_PHY=y
CONFIG_SOFTIRQ_ON_OWN_STACK=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
CONFIG_PWM_SYSFS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RAS=y
# CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
CONFIG_SG_POOL=y
CONFIG_SMP=y
CONFIG_SMP_ON_UP=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
CONFIG_PWM_SYSFS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
# CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
CONFIG_SG_POOL=y
CONFIG_SMP=y
CONFIG_SMSC_PHY=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
+CONFIG_PWM_GPIO=y
CONFIG_PWM_SYSFS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
# CONFIG_SERIAL_DEV_CTRL_TTYPORT is not set
CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_RPI_FW=y
CONFIG_SG_POOL=y
CONFIG_SMP=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
CONFIG_PWM_BRCMSTB=y
+CONFIG_PWM_GPIO=y
CONFIG_PWM_RP1=y
CONFIG_PWM_SYSFS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
CONFIG_SPARSE_IRQ=y
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+CONFIG_SRAM=y
# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 is not set
# CONFIG_RP1_PIO is not set
# CONFIG_SENSORS_RP1_ADC is not set
+# CONFIG_SERIAL_RPI_FW is not set
+# CONFIG_SND_PIMIDI is not set
# CONFIG_SPI_RP2040_GPIO_BRIDGE is not set
# CONFIG_VIDEO_AD5398 is not set
# CONFIG_VIDEO_ARDUCAM_64MP is not set
+++ /dev/null
-From cc63d552b9aab92fb581dfb08267d5af697f477b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Wed, 18 Sep 2024 16:45:24 +0100
-Subject: [PATCH 1267/1350] dts: rp1: Disable DMA usage for UART0
-
-Some recent DMA changes have led to data loss in UART0 on Pi 5. It also
-seems that even prior to these changes there was a problem with aborted
-transfers.
-
-As this is the only RP1 UART configured for DMA, it is better to remove
-the DMA usage until it is shown to be reliable.
-
-Link: https://github.com/raspberrypi/linux/issues/6365
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm64/boot/dts/broadcom/rp1.dtsi | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
-+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -55,9 +55,9 @@
- interrupts = <RP1_INT_UART0 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&rp1_clocks RP1_CLK_UART &rp1_clocks RP1_PLL_SYS_PRI_PH>;
- clock-names = "uartclk", "apb_pclk";
-- dmas = <&rp1_dma RP1_DMA_UART0_TX>,
-- <&rp1_dma RP1_DMA_UART0_RX>;
-- dma-names = "tx", "rx";
-+ // dmas = <&rp1_dma RP1_DMA_UART0_TX>,
-+ // <&rp1_dma RP1_DMA_UART0_RX>;
-+ // dma-names = "tx", "rx";
- pinctrl-names = "default";
- arm,primecell-periphid = <0x00541011>;
- uart-has-rtscts;
--- /dev/null
+From 25e6acfe00f589a5989ebd2c8d21a130fb3bf106 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 18 Oct 2024 09:18:10 +0100
+Subject: [PATCH] drivers: media: bcm2835_isp: Cache LS table dmabuf
+
+Clients such as libcamera do not change the LS table dmabuf on every
+frame. In such cases instead of mapping/remapping the same dmabuf on
+every frame to send to the firmware, cache the dmabuf once and only
+update and remap if the dmabuf has been changed by the userland client.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../bcm2835-isp/bcm2835-v4l2-isp.c | 77 +++++++++++--------
+ 1 file changed, 46 insertions(+), 31 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
++++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c
+@@ -139,6 +139,8 @@ struct bcm2835_isp_dev {
+ /* Image pipeline controls. */
+ int r_gain;
+ int b_gain;
++ struct dma_buf *last_ls_dmabuf;
++ struct mmal_parameter_lens_shading_v2 ls;
+ };
+
+ struct bcm2835_isp_buffer {
+@@ -657,18 +659,18 @@ static void bcm2835_isp_node_stop_stream
+ atomic_dec(&dev->num_streaming);
+ /* If all ports disabled, then disable the component */
+ if (atomic_read(&dev->num_streaming) == 0) {
+- struct bcm2835_isp_lens_shading ls;
+ /*
+ * The ISP component on the firmware has a reference to the
+ * dmabuf handle for the lens shading table. Pass a null handle
+ * to remove that reference now.
+ */
+- memset(&ls, 0, sizeof(ls));
++ memset(&dev->ls, 0, sizeof(dev->ls));
+ /* Must set a valid grid size for the FW */
+- ls.grid_cell_size = 16;
++ dev->ls.grid_cell_size = 16;
+ set_isp_param(&dev->node[0],
+ MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+- &ls, sizeof(ls));
++ &dev->ls, sizeof(dev->ls));
++ dev->last_ls_dmabuf = NULL;
+
+ ret = vchiq_mmal_component_disable(dev->mmal_instance,
+ dev->component);
+@@ -719,6 +721,36 @@ static inline unsigned int get_sizeimage
+ return (bpl * height * fmt->size_multiplier_x2) >> 1;
+ }
+
++static int map_ls_table(struct bcm2835_isp_dev *dev, struct dma_buf *dmabuf,
++ const struct bcm2835_isp_lens_shading *v4l2_ls)
++{
++ void *vcsm_handle;
++ int ret;
++
++ if (IS_ERR_OR_NULL(dmabuf))
++ return -EINVAL;
++
++ /*
++ * struct bcm2835_isp_lens_shading and struct
++ * mmal_parameter_lens_shading_v2 match so that we can do a
++ * simple memcpy here.
++ * Only the dmabuf to the actual table needs any manipulation.
++ */
++ memcpy(&dev->ls, v4l2_ls, sizeof(dev->ls));
++ ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle);
++ if (ret) {
++ dma_buf_put(dmabuf);
++ return ret;
++ }
++
++ dev->ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle);
++ dev->last_ls_dmabuf = dmabuf;
++
++ vc_sm_cma_free(vcsm_handle);
++
++ return 0;
++}
++
+ static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct bcm2835_isp_dev *dev =
+@@ -754,44 +786,27 @@ static int bcm2835_isp_s_ctrl(struct v4l
+ case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING:
+ {
+ struct bcm2835_isp_lens_shading *v4l2_ls;
+- struct mmal_parameter_lens_shading_v2 ls;
+- struct dma_buf *dmabuf;
+- void *vcsm_handle;
+
+ v4l2_ls = (struct bcm2835_isp_lens_shading *)ctrl->p_new.p_u8;
+- /*
+- * struct bcm2835_isp_lens_shading and struct
+- * mmal_parameter_lens_shading_v2 match so that we can do a
+- * simple memcpy here.
+- * Only the dmabuf to the actual table needs any manipulation.
+- */
+- memcpy(&ls, v4l2_ls, sizeof(ls));
++ struct dma_buf *dmabuf = dma_buf_get(v4l2_ls->dmabuf);
+
+- dmabuf = dma_buf_get(v4l2_ls->dmabuf);
+- if (IS_ERR_OR_NULL(dmabuf))
+- return -EINVAL;
+-
+- ret = vc_sm_cma_import_dmabuf(dmabuf, &vcsm_handle);
+- if (ret) {
+- dma_buf_put(dmabuf);
+- return -EINVAL;
+- }
++ if (dmabuf != dev->last_ls_dmabuf)
++ ret = map_ls_table(dev, dmabuf, v4l2_ls);
+
+- ls.mem_handle_table = vc_sm_cma_int_handle(vcsm_handle);
+- if (ls.mem_handle_table)
+- /* The VPU will take a reference on the vcsm handle,
++ if (!ret && dev->ls.mem_handle_table)
++ /*
++ * The VPU will take a reference on the vcsm handle,
+ * which in turn will retain a reference on the dmabuf.
+ * This code can therefore safely release all
+ * references to the buffer.
+ */
+- ret = set_isp_param(node,
+- MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
+- &ls,
+- sizeof(ls));
++ ret =
++ set_isp_param(node,
++ MMAL_PARAMETER_LENS_SHADING_OVERRIDE,
++ &dev->ls, sizeof(dev->ls));
+ else
+ ret = -EINVAL;
+
+- vc_sm_cma_free(vcsm_handle);
+ dma_buf_put(dmabuf);
+ break;
+ }
--- /dev/null
+From 3ab72fc21ea8576e59f6aad10bd6b1a0eae6e5eb Mon Sep 17 00:00:00 2001
+From: Vincent Whitchurch <vincent.whitchurch@axis.com>
+Date: Tue, 4 Jun 2024 23:00:41 +0200
+Subject: [PATCH] pwm: Add GPIO PWM driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 7f61257cd6e1ad4769b4b819668cab00f68f2556 upstream.
+
+Add a software PWM which toggles a GPIO from a high-resolution timer.
+
+This will naturally not be as accurate or as efficient as a hardware
+PWM, but it is useful in some cases. I have for example used it for
+evaluating LED brightness handling (via leds-pwm) on a board where the
+LED was just hooked up to a GPIO, and for a simple verification of the
+timer frequency on another platform.
+
+Since high-resolution timers are used, sleeping GPIO chips are not
+supported and are rejected in the probe function.
+
+Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
+Co-developed-by: Stefan Wahren <wahrenst@gmx.net>
+Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
+Co-developed-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+Reviewed-by: Dhruva Gole <d-gole@ti.com>
+Link: https://lore.kernel.org/r/20240604-pwm-gpio-v7-2-6b67cf60db92@linaro.org
+Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+
+pwm: Backport pwm-gpio.c to rpi-6.6.y
+---
+ .../driver-api/gpio/drivers-on-gpio.rst | 7 +-
+ drivers/pwm/Kconfig | 11 +
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-gpio.c | 240 ++++++++++++++++++
+ 4 files changed, 258 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/pwm/pwm-gpio.c
+
+--- a/Documentation/driver-api/gpio/drivers-on-gpio.rst
++++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst
+@@ -27,7 +27,12 @@ hardware descriptions such as device tre
+ to the lines for a more permanent solution of this type.
+
+ - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from
+- an external speaker connected to a GPIO line.
++ an external speaker connected to a GPIO line. (If the beep is controlled by
++ off/on, for an actual PWM waveform, see pwm-gpio below.)
++
++- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high
++ resolution timer producing a PWM waveform on the GPIO line, as well as
++ Linux high resolution timers can do.
+
+ - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an
+ external connector status, such as a headset line for an audio driver or an
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -217,6 +217,17 @@ config PWM_FSL_FTM
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
++config PWM_GPIO
++ tristate "GPIO PWM support"
++ depends on GPIOLIB
++ depends on HIGH_RES_TIMERS
++ help
++ Generic PWM framework driver for software PWM toggling a GPIO pin
++ from kernel high-resolution timers.
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-gpio.
++
+ config PWM_HIBVT
+ tristate "HiSilicon BVT PWM support"
+ depends on ARCH_HISI || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -18,6 +18,7 @@ obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec
+ obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
+ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
+ obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
++obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o
+ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
+ obj-$(CONFIG_PWM_IMG) += pwm-img.o
+ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
+--- /dev/null
++++ b/drivers/pwm/pwm-gpio.c
+@@ -0,0 +1,240 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Generic software PWM for modulating GPIOs
++ *
++ * Copyright (C) 2020 Axis Communications AB
++ * Copyright (C) 2020 Nicola Di Lieto
++ * Copyright (C) 2024 Stefan Wahren
++ * Copyright (C) 2024 Linus Walleij
++ */
++
++#include <linux/cleanup.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/gpio/consumer.h>
++#include <linux/hrtimer.h>
++#include <linux/math.h>
++#include <linux/module.h>
++#include <linux/mod_devicetable.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/pwm.h>
++#include <linux/spinlock.h>
++#include <linux/time.h>
++#include <linux/types.h>
++
++struct pwm_gpio {
++ struct hrtimer gpio_timer;
++ struct gpio_desc *gpio;
++ struct pwm_state state;
++ struct pwm_state next_state;
++
++ /* Protect internal state between pwm_ops and hrtimer */
++ spinlock_t lock;
++
++ bool changing;
++ bool running;
++ bool level;
++ struct pwm_chip chip;
++};
++
++static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src)
++{
++ u64 dividend;
++ u32 remainder;
++
++ *dest = *src;
++
++ /* Round down to hrtimer resolution */
++ dividend = dest->period;
++ remainder = do_div(dividend, hrtimer_resolution);
++ dest->period -= remainder;
++
++ dividend = dest->duty_cycle;
++ remainder = do_div(dividend, hrtimer_resolution);
++ dest->duty_cycle -= remainder;
++}
++
++static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level)
++{
++ const struct pwm_state *state = &gpwm->state;
++ bool invert = state->polarity == PWM_POLARITY_INVERSED;
++
++ gpwm->level = level;
++ gpiod_set_value(gpwm->gpio, gpwm->level ^ invert);
++
++ if (!state->duty_cycle || state->duty_cycle == state->period) {
++ gpwm->running = false;
++ return 0;
++ }
++
++ gpwm->running = true;
++ return level ? state->duty_cycle : state->period - state->duty_cycle;
++}
++
++static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer)
++{
++ struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio,
++ gpio_timer);
++ u64 next_toggle;
++ bool new_level;
++
++ guard(spinlock_irqsave)(&gpwm->lock);
++
++ /* Apply new state at end of current period */
++ if (!gpwm->level && gpwm->changing) {
++ gpwm->changing = false;
++ gpwm->state = gpwm->next_state;
++ new_level = !!gpwm->state.duty_cycle;
++ } else {
++ new_level = !gpwm->level;
++ }
++
++ next_toggle = pwm_gpio_toggle(gpwm, new_level);
++ if (next_toggle)
++ hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer),
++ ns_to_ktime(next_toggle));
++
++ return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART;
++}
++
++static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++ const struct pwm_state *state)
++{
++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip);
++ bool invert = state->polarity == PWM_POLARITY_INVERSED;
++
++ if (state->duty_cycle && state->duty_cycle < hrtimer_resolution)
++ return -EINVAL;
++
++ if (state->duty_cycle != state->period &&
++ (state->period - state->duty_cycle < hrtimer_resolution))
++ return -EINVAL;
++
++ if (!state->enabled) {
++ hrtimer_cancel(&gpwm->gpio_timer);
++ } else if (!gpwm->running) {
++ int ret;
++
++ /*
++ * This just enables the output, but pwm_gpio_toggle()
++ * really starts the duty cycle.
++ */
++ ret = gpiod_direction_output(gpwm->gpio, invert);
++ if (ret)
++ return ret;
++ }
++
++ guard(spinlock_irqsave)(&gpwm->lock);
++
++ if (!state->enabled) {
++ pwm_gpio_round(&gpwm->state, state);
++ gpwm->running = false;
++ gpwm->changing = false;
++
++ gpiod_set_value(gpwm->gpio, invert);
++ } else if (gpwm->running) {
++ pwm_gpio_round(&gpwm->next_state, state);
++ gpwm->changing = true;
++ } else {
++ unsigned long next_toggle;
++
++ pwm_gpio_round(&gpwm->state, state);
++ gpwm->changing = false;
++
++ next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle);
++ if (next_toggle)
++ hrtimer_start(&gpwm->gpio_timer, next_toggle,
++ HRTIMER_MODE_REL);
++ }
++
++ return 0;
++}
++
++static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
++ struct pwm_state *state)
++{
++ struct pwm_gpio *gpwm = container_of(chip, struct pwm_gpio, chip);
++
++ guard(spinlock_irqsave)(&gpwm->lock);
++
++ if (gpwm->changing)
++ *state = gpwm->next_state;
++ else
++ *state = gpwm->state;
++
++ return 0;
++}
++
++static const struct pwm_ops pwm_gpio_ops = {
++ .apply = pwm_gpio_apply,
++ .get_state = pwm_gpio_get_state,
++};
++
++static void pwm_gpio_disable_hrtimer(void *data)
++{
++ struct pwm_gpio *gpwm = data;
++
++ hrtimer_cancel(&gpwm->gpio_timer);
++}
++
++static int pwm_gpio_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct pwm_chip *chip;
++ struct pwm_gpio *gpwm;
++ int ret;
++
++ gpwm = devm_kzalloc(&pdev->dev, sizeof(*gpwm), GFP_KERNEL);
++ if (IS_ERR(gpwm))
++ return PTR_ERR(gpwm);
++
++ chip = &gpwm->chip;
++
++ spin_lock_init(&gpwm->lock);
++
++ gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
++ if (IS_ERR(gpwm->gpio))
++ return dev_err_probe(dev, PTR_ERR(gpwm->gpio),
++ "%pfw: could not get gpio\n",
++ dev_fwnode(dev));
++
++ if (gpiod_cansleep(gpwm->gpio))
++ return dev_err_probe(dev, -EINVAL,
++ "%pfw: sleeping GPIO not supported\n",
++ dev_fwnode(dev));
++
++ chip->dev = dev;
++ chip->ops = &pwm_gpio_ops;
++ chip->atomic = true;
++ chip->npwm = 1;
++
++ hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm);
++ if (ret)
++ return ret;
++
++ gpwm->gpio_timer.function = pwm_gpio_timer;
++
++ return devm_pwmchip_add(dev, chip);
++}
++
++static const struct of_device_id pwm_gpio_dt_ids[] = {
++ { .compatible = "pwm-gpio" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids);
++
++static struct platform_driver pwm_gpio_driver = {
++ .driver = {
++ .name = "pwm-gpio",
++ .of_match_table = pwm_gpio_dt_ids,
++ },
++ .probe = pwm_gpio_probe,
++};
++module_platform_driver(pwm_gpio_driver);
++
++MODULE_DESCRIPTION("PWM GPIO driver");
++MODULE_AUTHOR("Vincent Whitchurch");
++MODULE_LICENSE("GPL");
--- /dev/null
+From ff0fe12ab875d587348b6f2b9e73ae928049ebee Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Thu, 31 Oct 2024 16:12:54 +0000
+Subject: [PATCH] dtoverlay: Add a dtoverlay for pwm-gpio
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 6 +++
+ .../boot/dts/overlays/pwm-gpio-overlay.dts | 38 +++++++++++++++++++
+ 3 files changed, 45 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -217,6 +217,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ proto-codec.dtbo \
+ pwm.dtbo \
+ pwm-2chan.dtbo \
++ pwm-gpio.dtbo \
+ pwm-ir-tx.dtbo \
+ pwm1.dtbo \
+ qca7000.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3903,6 +3903,12 @@ Params: pin Output p
+ clock PWM clock frequency (informational)
+
+
++Name: pwm-gpio
++Info: Configures the software PWM GPIO driver
++Load: dtoverlay=pwm-gpio,<param>=<val>
++Params: gpio Output pin (default 4)
++
++
+ Name: pwm-ir-tx
+ Info: Use GPIO pin as pwm-assisted infrared transmitter output.
+ This is an alternative to "gpio-ir-tx". pwm-ir-tx makes use
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pwm-gpio-overlay.dts
+@@ -0,0 +1,38 @@
++// Device tree overlay for software GPIO PWM.
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&gpio>;
++ __overlay__ {
++ pwm_gpio_pins: pwm_gpio_pins@4 {
++ brcm,pins = <4>; /* gpio 4 */
++ brcm,function = <1>; /* output */
++ brcm,pull = <0>; /* pull-none */
++ };
++ };
++ };
++
++ fragment@1 {
++ target-path = "/";
++ __overlay__ {
++ pwm_gpio: pwm_gpio@4 {
++ compatible = "pwm-gpio";
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_gpio_pins>;
++ gpios = <&gpio 4 0>;
++ };
++ };
++ };
++
++ __overrides__ {
++ gpio = <&pwm_gpio>,"gpios:4",
++ <&pwm_gpio_pins>,"brcm,pins:0",
++ /* modify reg values to allow multiple instantiation */
++ <&pwm_gpio>,"reg:0",
++ <&pwm_gpio_pins>,"reg:0";
++ };
++};
--- /dev/null
+From 624eb357e1a16385b3d6171e9194e4c5f8d4fd5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 23 Oct 2024 19:09:18 +0100
+Subject: [PATCH] dts: 2712: Drop some numa options from bootargs
+
+iommu_dma_numa_policy=interleave is not valid in the current tree
+It generates an unknown setting will be passed to usespace warning
+
+system_heap.max_order=0 is wanted when numa is enabled, but may not
+be when it is disabled.
+
+Add it on firmware side when we know if numa=fake=<n> is used.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -99,7 +99,7 @@
+
+ / {
+ chosen: chosen {
+- bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave iommu_dma_numa_policy=interleave system_heap.max_order=0";
++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave";
+ stdout-path = "serial10:115200n8";
+ };
+
--- /dev/null
+From 74f3ca5e39586ea26201fe6eaf1b9e6793b101b7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 29 Oct 2024 13:33:21 +0000
+Subject: [PATCH] mmc: quirks: add more broken Kingston Canvas Go! SD card date
+ ranges
+
+A user has reported that a card of this model from late 2021 doesn't
+work, so extend the date range and make it match on all card sizes.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/core/quirks.h | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/core/quirks.h
++++ b/drivers/mmc/core/quirks.h
+@@ -18,10 +18,22 @@
+ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = {
+ /*
+ * Kingston Canvas Go! Plus microSD cards never finish SD cache flush.
+- * This has so far only been observed on cards from 11/2019, while new
+- * cards from 2023/05 do not exhibit this behavior.
++ * This has been observed on cards from 2019/11 and 2021/11, while new
++ * cards from 2023/05 and 2024/08 do not exhibit this behavior.
+ */
+- _FIXUP_EXT("SD64G", CID_MANFID_KINGSTON_SD, 0x5449, 2019, 11,
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2019, CID_MONTH_ANY,
++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2020, CID_MONTH_ANY,
++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2021, CID_MONTH_ANY,
++ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
++ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
++
++ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_KINGSTON_SD, 0x5449, 2022, CID_MONTH_ANY,
+ 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
+ MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY),
+
--- /dev/null
+From 6c0f34fb0f83741f7f03f6bfd3fcbc89cb2c7cde Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 6 Nov 2024 10:26:55 +0000
+Subject: [PATCH] dt-bindings: usb: snps,dwc3: add FS/HS periodic NAK polling
+ quirk
+
+Add two quirk properties that control whether or not the controller
+issues many more handshakes to FS/HS Async endpoints in a single
+(micro)frame. Enabling these can significantly increase throughput for
+endpoints that frequently respond with NAKs.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
++++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
+@@ -231,6 +231,16 @@ properties:
+ description: When set, disable u2mac linestate check during HS transmit
+ type: boolean
+
++ snps,enhanced-nak-fs-quirk:
++ description:
++ When set, the controller schedules many more handshakes to Async FS
++ endpoints, improving throughput when they frequently respond with NAKs.
++
++ snps,enhanced-nak-hs-quirk:
++ description:
++ When set, the controller schedules many more handshakes to Async HS
++ endpoints, improving throughput when they frequently respond with NAKs.
++
+ snps,parkmode-disable-ss-quirk:
+ description:
+ When set, disable park mode for all Superspeed bus instances.
--- /dev/null
+From bb53ca75f9e3631e753f397ccab704a8f975658b Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 6 Nov 2024 10:45:24 +0000
+Subject: [PATCH] usb: dwc3: core: add support for setting NAK enhancement bits
+ for FS/HS
+
+If a device frequently NAKs, it can exhaust the scheduled handshakes in
+a frame. It will then not get polled by the controller until the next
+frame interval. This is most noticeable on FS devices as the controller
+schedules a small set of transactions only once per full-speed frame.
+
+Setting the ENH_PER_NAK_FS/LS bits in the GUCTL1 register increases the
+number of transactions that can be scheduled to Async (Control/Bulk)
+endpoints in the respective frame time. In the FS case, this only
+applies to FS devices directly connected to root ports.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/usb/dwc3/core.c | 10 ++++++++++
+ drivers/usb/dwc3/core.h | 6 ++++++
+ 2 files changed, 16 insertions(+)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -1366,6 +1366,12 @@ static int dwc3_core_init(struct dwc3 *d
+ if (dwc->dis_tx_ipgap_linecheck_quirk)
+ reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+
++ if (dwc->enh_nak_fs_quirk)
++ reg |= DWC3_GUCTL1_NAK_PER_ENH_FS;
++
++ if (dwc->enh_nak_hs_quirk)
++ reg |= DWC3_GUCTL1_NAK_PER_ENH_HS;
++
+ if (dwc->parkmode_disable_ss_quirk)
+ reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+
+@@ -1669,6 +1675,10 @@ static void dwc3_get_properties(struct d
+ "snps,resume-hs-terminations");
+ dwc->ulpi_ext_vbus_drv = device_property_read_bool(dev,
+ "snps,ulpi-ext-vbus-drv");
++ dwc->enh_nak_fs_quirk = device_property_read_bool(dev,
++ "snps,enhanced-nak-fs-quirk");
++ dwc->enh_nak_hs_quirk = device_property_read_bool(dev,
++ "snps,enhanced-nak-hs-quirk");
+ dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
+ "snps,parkmode-disable-ss-quirk");
+ dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev,
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -269,6 +269,8 @@
+ #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
+ #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK BIT(26)
+ #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
++#define DWC3_GUCTL1_NAK_PER_ENH_FS BIT(19)
++#define DWC3_GUCTL1_NAK_PER_ENH_HS BIT(18)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_HS BIT(16)
+ #define DWC3_GUCTL1_PARKMODE_DISABLE_FSLS BIT(15)
+@@ -1118,6 +1120,8 @@ struct dwc3_scratchpad_array {
+ * generation after resume from suspend.
+ * @ulpi_ext_vbus_drv: Set to confiure the upli chip to drives CPEN pin
+ * VBUS with an external supply.
++ * @enh_nak_fs_quirk: Set to schedule more handshakes to Async FS endpoints.
++ * @enh_nak_hs_quirk: Set to schedule more handshakes to Async HS endpoints.
+ * @parkmode_disable_ss_quirk: If set, disable park mode feature for all
+ * Superspeed instances.
+ * @parkmode_disable_hs_quirk: If set, disable park mode feature for all
+@@ -1348,6 +1352,8 @@ struct dwc3 {
+ unsigned dis_tx_ipgap_linecheck_quirk:1;
+ unsigned resume_hs_terminations:1;
+ unsigned ulpi_ext_vbus_drv:1;
++ unsigned enh_nak_fs_quirk:1;
++ unsigned enh_nak_hs_quirk:1;
+ unsigned parkmode_disable_ss_quirk:1;
+ unsigned parkmode_disable_hs_quirk:1;
+ unsigned parkmode_disable_fsls_quirk:1;
--- /dev/null
+From 803757627b48bdad9530b50053321fdea6dfcab4 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 6 Nov 2024 10:54:58 +0000
+Subject: [PATCH] DTS: rp1: set enhanced FS NAK quirk for usb3 controllers
+
+There seem to be only benefits, and no downsides.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/rp1.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -1077,6 +1077,7 @@
+ usb3-lpm-capable;
+ snps,axi-pipe-limit = /bits/ 8 <8>;
+ snps,dis_rxdet_inp3_quirk;
++ snps,enhanced-nak-fs-quirk;
+ snps,parkmode-disable-ss-quirk;
+ snps,parkmode-disable-hs-quirk;
+ snps,parkmode-disable-fsls-quirk;
+@@ -1093,6 +1094,7 @@
+ usb3-lpm-capable;
+ snps,axi-pipe-limit = /bits/ 8 <8>;
+ snps,dis_rxdet_inp3_quirk;
++ snps,enhanced-nak-fs-quirk;
+ snps,parkmode-disable-ss-quirk;
+ snps,parkmode-disable-hs-quirk;
+ snps,parkmode-disable-fsls-quirk;
--- /dev/null
+From e9e852af347ae3ccee4e7abb01f9ef91387980f9 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 6 Nov 2024 11:07:55 +0000
+Subject: [PATCH] drivers: usb: xhci: prevent a theoretical race on
+ non-coherent platforms
+
+For platforms that have xHCI controllers attached over PCIe, and
+non-coherent routes to main memory, a theoretical race exists between
+posting new TRBs to a ring, and writing to the doorbell register.
+
+In a contended system, write traffic from the CPU may be stalled before
+the memory controller, whereas the CPU to Endpoint route is separate
+and not likely to be contended. Similarly, the DMA route from the
+endpoint to main memory may be separate and uncontended.
+
+Therefore the xHCI can receive a doorbell write and find a stale view
+of a transfer ring. In cases where only a single TRB is ping-ponged at
+a time, this can cause the endpoint to not get polled at all.
+
+Adding a readl() before the write forces a round-trip transaction
+across PCIe, definitively serialising the CPU along the PCI
+producer-consumer ordering rules.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/usb/host/xhci-ring.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -505,6 +505,19 @@ void xhci_ring_ep_doorbell(struct xhci_h
+
+ trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
+
++ /*
++ * For non-coherent systems with PCIe DMA (such as Pi 4, Pi 5) there
++ * is a theoretical race between the TRB write and barrier, which
++ * is reported complete as soon as the write leaves the CPU domain,
++ * the doorbell write, which may be reported as complete by the RC
++ * at some arbitrary point, and the visibility of new TRBs in system
++ * RAM by the endpoint DMA engine.
++ *
++ * This read before the write positively serialises the CPU state
++ * by incurring a round-trip across the link.
++ */
++ readl(db_addr);
++
+ writel(DB_VALUE(ep_index, stream_id), db_addr);
+ /* flush the write */
+ readl(db_addr);
--- /dev/null
+From ce65ed02cb6707ae5c9f3a304f5b0124f4eed559 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 4 Nov 2024 14:10:53 +0000
+Subject: [PATCH] iio: humidity: dht11: Allow non-zero decimals
+
+The DHT11 datasheet is pretty cryptic, but it does suggest that after
+each integer value (humidity and temperature) there are "decimal"
+values. Validate these as integers in the range 0-9 and treat them as
+tenths of a unit.
+
+Link: https://github.com/raspberrypi/linux/issues/6220
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/iio/humidity/dht11.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/iio/humidity/dht11.c
++++ b/drivers/iio/humidity/dht11.c
+@@ -152,9 +152,9 @@ static int dht11_decode(struct dht11 *dh
+ dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
+ ((temp_int & 0x80) ? -100 : 100);
+ dht11->humidity = ((hum_int << 8) + hum_dec) * 100;
+- } else if (temp_dec == 0 && hum_dec == 0) { /* DHT11 */
+- dht11->temperature = temp_int * 1000;
+- dht11->humidity = hum_int * 1000;
++ } else if (temp_dec < 10 && hum_dec < 10) { /* DHT11 */
++ dht11->temperature = temp_int * 1000 + temp_dec * 100;
++ dht11->humidity = hum_int * 1000 + hum_dec * 100;
+ } else {
+ dev_err(dht11->dev,
+ "Don't know how to decode data: %d %d %d %d\n",
--- /dev/null
+From c3393ac1098d1f191e37eed73bf366ebc88ac4ee Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Sep 2024 14:49:05 +0100
+Subject: [PATCH] drm/vc4: Correct condition for ignoring a plane to src rect
+ =0, not <1.0
+
+The logic for dropping a plane less than zero didn't account for the
+possibility that a plane could be being upscaled with a src_rect with
+width/height < 1 pixel, but not 0 subpixels.
+
+Check for not 0 subpixels, not < 1, in both vc4 and vc6 paths.
+
+Fixes: dac616899f87 ("drm/vc4: Drop planes that have 0 destination size")
+Fixes: f73b18eb0d48 ("drm/vc4: Drop planes that are completely off-screen")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1160,7 +1160,8 @@ static int vc4_plane_mode_set(struct drm
+ width = vc4_state->src_w[0] >> 16;
+ height = vc4_state->src_h[0] >> 16;
+
+- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) {
++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] ||
++ !vc4_state->crtc_w || !vc4_state->crtc_h) {
+ /* 0 source size probably means the plane is offscreen */
+ vc4_state->dlist_initialized = 1;
+ return 0;
+@@ -1698,7 +1699,8 @@ static int vc6_plane_mode_set(struct drm
+ width = vc4_state->src_w[0] >> 16;
+ height = vc4_state->src_h[0] >> 16;
+
+- if (!width || !height || !vc4_state->crtc_w || !vc4_state->crtc_h) {
++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0] ||
++ !vc4_state->crtc_w || !vc4_state->crtc_h) {
+ /* 0 source size probably means the plane is offscreen.
+ * 0 destination size is a redundant plane.
+ */
--- /dev/null
+From ca621585c573cae54dc1235d90822e8bcef2f73d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Sep 2024 15:23:33 +0100
+Subject: [PATCH] drm/vc4: Use the TPZ scaling filter for 1x1 source images
+
+The documentation says that the TPZ filter can not upscale,
+and requesting a scaling factor > 1:1 will output the original
+image in the top left, and repeat the right/bottom most pixels
+thereafter.
+That fits perfectly with upscaling a 1x1 image which is done
+a fair amount by some compositors to give solid colour, and it
+saves a large amount of LBM (TPZ is based on src size, whilst
+PPF is based on dest size).
+
+Select TPZ filter for images with source rectangle <=1.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -265,7 +265,11 @@ static enum vc4_scaling_mode vc4_get_sca
+ {
+ if (dst == src >> 16)
+ return VC4_SCALING_NONE;
+- if (3 * dst >= 2 * (src >> 16))
++
++ if (src <= (1 << 16))
++ /* Source rectangle <= 1 pixel can use TPZ for resize/upscale */
++ return VC4_SCALING_TPZ;
++ else if (3 * dst >= 2 * (src >> 16))
+ return VC4_SCALING_PPF;
+ else
+ return VC4_SCALING_TPZ;
+@@ -560,12 +564,17 @@ static void vc4_write_tpz(struct vc4_pla
+
+ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
+
+- scale = src / dst;
++ if ((dst << 16) < src) {
++ scale = src / dst;
+
+- /* The specs note that while the reciprocal would be defined
+- * as (1<<32)/scale, ~0 is close enough.
+- */
+- recip = ~0 / scale;
++ /* The specs note that while the reciprocal would be defined
++ * as (1<<32)/scale, ~0 is close enough.
++ */
++ recip = ~0 / scale;
++ } else {
++ scale = (1 << 16) + 1;
++ recip = (1 << 16) - 1;
++ }
+
+ vc4_dlist_write(vc4_state,
+ /*
--- /dev/null
+From 68b0ff3549148e614e1733d773cee8e689c763c6 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 20 Aug 2024 16:25:10 +0100
+Subject: [PATCH] drm: Set non-desktop property to true for writeback and
+ virtual connectors
+
+The non-desktop property "Indicates the output should be ignored for
+purposes of displaying a standard desktop environment or console."
+
+That sounds like it should be true for all writeback and virtual
+connectors as you shouldn't render a desktop to them, so set it
+by default.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -361,7 +361,8 @@ static int __drm_connector_init(struct d
+
+ drm_object_attach_property(&connector->base,
+ config->non_desktop_property,
+- 0);
++ (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1;
+ drm_object_attach_property(&connector->base,
+ config->tile_property,
+ 0);
--- /dev/null
+From 8181e682d6f4ef209845ec24f0a1eb37764d6731 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 21 Oct 2022 14:26:12 +0100
+Subject: [PATCH] drm: Increase plane_mask to 64bit.
+
+The limit of 32 planes per DRM device is dictated by the use
+of planes_mask returning a u32.
+
+Change to a u64 such that 64 planes can be supported by a device.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_atomic.c | 2 +-
+ drivers/gpu/drm/drm_framebuffer.c | 2 +-
+ drivers/gpu/drm/drm_mode_config.c | 2 +-
+ drivers/gpu/drm/drm_plane.c | 2 +-
+ drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c | 2 +-
+ include/drm/drm_crtc.h | 2 +-
+ include/drm/drm_plane.h | 4 ++--
+ 7 files changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/drm_atomic.c
++++ b/drivers/gpu/drm/drm_atomic.c
+@@ -451,7 +451,7 @@ static void drm_atomic_crtc_print_state(
+ drm_printf(p, "\tactive_changed=%d\n", state->active_changed);
+ drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed);
+ drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed);
+- drm_printf(p, "\tplane_mask=%x\n", state->plane_mask);
++ drm_printf(p, "\tplane_mask=%llx\n", state->plane_mask);
+ drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask);
+ drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask);
+ drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode));
+--- a/drivers/gpu/drm/drm_framebuffer.c
++++ b/drivers/gpu/drm/drm_framebuffer.c
+@@ -959,7 +959,7 @@ static int atomic_remove_fb(struct drm_f
+ struct drm_connector *conn __maybe_unused;
+ struct drm_connector_state *conn_state;
+ int i, ret;
+- unsigned plane_mask;
++ u64 plane_mask;
+ bool disable_crtcs = false;
+
+ retry_disable:
+--- a/drivers/gpu/drm/drm_mode_config.c
++++ b/drivers/gpu/drm/drm_mode_config.c
+@@ -636,7 +636,7 @@ void drm_mode_config_validate(struct drm
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+- u32 primary_with_crtc = 0, cursor_with_crtc = 0;
++ u64 primary_with_crtc = 0, cursor_with_crtc = 0;
+ unsigned int num_primary = 0;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+--- a/drivers/gpu/drm/drm_plane.c
++++ b/drivers/gpu/drm/drm_plane.c
+@@ -249,7 +249,7 @@ static int __drm_universal_plane_init(st
+ int ret;
+
+ /* plane index is used with 32bit bitmasks */
+- if (WARN_ON(config->num_total_plane >= 32))
++ if (WARN_ON(config->num_total_plane >= 64))
+ return -EINVAL;
+
+ /*
+--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
++++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c
+@@ -230,7 +230,7 @@ static int ipu_crtc_atomic_check(struct
+ {
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+ crtc);
+- u32 primary_plane_mask = drm_plane_mask(crtc->primary);
++ u64 primary_plane_mask = drm_plane_mask(crtc->primary);
+
+ if (crtc_state->active && (primary_plane_mask & crtc_state->plane_mask) == 0)
+ return -EINVAL;
+--- a/include/drm/drm_crtc.h
++++ b/include/drm/drm_crtc.h
+@@ -192,7 +192,7 @@ struct drm_crtc_state {
+ * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to
+ * this CRTC.
+ */
+- u32 plane_mask;
++ u64 plane_mask;
+
+ /**
+ * @connector_mask: Bitmask of drm_connector_mask(connector) of
+--- a/include/drm/drm_plane.h
++++ b/include/drm/drm_plane.h
+@@ -915,9 +915,9 @@ static inline unsigned int drm_plane_ind
+ * drm_plane_mask - find the mask of a registered plane
+ * @plane: plane to find mask for
+ */
+-static inline u32 drm_plane_mask(const struct drm_plane *plane)
++static inline u64 drm_plane_mask(const struct drm_plane *plane)
+ {
+- return 1 << drm_plane_index(plane);
++ return 1ULL << drm_plane_index(plane);
+ }
+
+ struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx);
--- /dev/null
+From 5dc4cef7d7fcda4ea59b9e456a835fa54336af6b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 21 Oct 2022 14:27:45 +0100
+Subject: [PATCH] drm/vc4: Increase number of overlay planes from 16 to 48
+
+The HVS can accept an arbitrary number of planes, provided
+that the overall pixel read load is within limits, and
+the display list can fit into the dlist memory.
+
+Now that DRM will support 64 planes per device, increase
+the number of overlay planes from 16 to 48 so that the
+dlist complexity can be increased (eg 4x4 video wall on
+each of 3 displays).
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_plane.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -362,7 +362,7 @@ static int __drm_connector_init(struct d
+ drm_object_attach_property(&connector->base,
+ config->non_desktop_property,
+ (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
+- connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1;
++ connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ? 0 : 1);
+ drm_object_attach_property(&connector->base,
+ config->tile_property,
+ 0);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2517,7 +2517,7 @@ struct drm_plane *vc4_plane_init(struct
+ return plane;
+ }
+
+-#define VC4_NUM_OVERLAY_PLANES 16
++#define VC4_NUM_OVERLAY_PLANES 48
+
+ int vc4_plane_create_additional_planes(struct drm_device *drm)
+ {
--- /dev/null
+From dd340cb082a020fbd42b794493ffd063dd8e15b4 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 15 Aug 2023 15:44:34 +0100
+Subject: [PATCH] drm/vc4: Assign 32 overlay planes to writeback only
+
+Instead of having 48 generic overlay planes, assign 32 to the
+writeback connector so that there is no ambiguity in wlroots
+when trying to find a plane for composition using the writeback
+connector vs display.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 34 +++++++++++++++++++++++++++++++--
+ 1 file changed, 32 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2517,13 +2517,28 @@ struct drm_plane *vc4_plane_init(struct
+ return plane;
+ }
+
+-#define VC4_NUM_OVERLAY_PLANES 48
++#define VC4_NUM_OVERLAY_PLANES 16
++#define VC4_NUM_TXP_OVERLAY_PLANES 32
+
+ int vc4_plane_create_additional_planes(struct drm_device *drm)
+ {
+ struct drm_plane *cursor_plane;
+ struct drm_crtc *crtc;
+ unsigned int i;
++ struct drm_crtc *txp_crtc;
++ uint32_t non_txp_crtc_mask;
++
++ drm_for_each_crtc(crtc, drm) {
++ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++
++ if (vc4_crtc->feeds_txp) {
++ txp_crtc = crtc;
++ break;
++ }
++ }
++
++ non_txp_crtc_mask = GENMASK(drm->mode_config.num_crtc - 1, 0) -
++ drm_crtc_mask(txp_crtc);
+
+ /* Set up some arbitrary number of planes. We're not limited
+ * by a set number of physical registers, just the space in
+@@ -2537,7 +2552,22 @@ int vc4_plane_create_additional_planes(s
+ for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) {
+ struct drm_plane *plane =
+ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
+- GENMASK(drm->mode_config.num_crtc - 1, 0));
++ non_txp_crtc_mask);
++
++ if (IS_ERR(plane))
++ continue;
++
++ /* Create zpos property. Max of all the overlays + 1 primary +
++ * 1 cursor plane on a crtc.
++ */
++ drm_plane_create_zpos_property(plane, i + 1, 1,
++ VC4_NUM_OVERLAY_PLANES + 1);
++ }
++
++ for (i = 0; i < VC4_NUM_TXP_OVERLAY_PLANES; i++) {
++ struct drm_plane *plane =
++ vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
++ drm_crtc_mask(txp_crtc));
+
+ if (IS_ERR(plane))
+ continue;
--- /dev/null
+From b3b3d12cf0734318a0fed0b33e13d714188369db Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 22 Oct 2024 17:17:31 +0100
+Subject: [PATCH] drm: Add a DRM_MODE_TRANSPOSE option to the DRM rotation
+ property
+
+Some hardware will implement transpose as a rotation operation,
+which when combined with X and Y reflect can result in a rotation,
+but is a discrete operation in its own right.
+
+Add an option for transpose only.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_blend.c | 3 +++
+ include/uapi/drm/drm_mode.h | 1 +
+ 2 files changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/drm_blend.c
++++ b/drivers/gpu/drm/drm_blend.c
+@@ -263,6 +263,8 @@ EXPORT_SYMBOL(drm_plane_create_alpha_pro
+ * "reflect-x"
+ * DRM_MODE_REFLECT_Y:
+ * "reflect-y"
++ * DRM_MODE_TRANSPOSE:
++ * "transpose"
+ *
+ * Rotation is the specified amount in degrees in counter clockwise direction,
+ * the X and Y axis are within the source rectangle, i.e. the X/Y axis before
+@@ -280,6 +282,7 @@ int drm_plane_create_rotation_property(s
+ { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
+ { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" },
+ { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" },
++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" },
+ };
+ struct drm_property *prop;
+
+--- a/include/uapi/drm/drm_mode.h
++++ b/include/uapi/drm/drm_mode.h
+@@ -203,6 +203,7 @@ extern "C" {
+ */
+ #define DRM_MODE_REFLECT_X (1<<4)
+ #define DRM_MODE_REFLECT_Y (1<<5)
++#define DRM_MODE_TRANSPOSE (1<<6)
+
+ /*
+ * DRM_MODE_REFLECT_MASK
--- /dev/null
+From 8fec3ff870499256f2c18fe7983f6ed3fea4faaf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 22 Oct 2024 17:22:40 +0100
+Subject: [PATCH] drm: Add a rotation parameter to connectors.
+
+Some connectors, particularly writeback, can implement flip
+or transpose operations as writing back to memory.
+
+Add a connector rotation property to control this.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c | 4 +++
+ drivers/gpu/drm/drm_blend.c | 50 ++++++++++++++++++++++++-------
+ include/drm/drm_blend.h | 5 ++++
+ include/drm/drm_connector.h | 11 +++++++
+ 4 files changed, 60 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -811,6 +811,8 @@ static int drm_atomic_connector_set_prop
+ state->max_requested_bpc = val;
+ } else if (property == connector->privacy_screen_sw_state_property) {
+ state->privacy_screen_sw_state = val;
++ } else if (property == connector->rotation_property) {
++ state->rotation = val;
+ } else if (connector->funcs->atomic_set_property) {
+ return connector->funcs->atomic_set_property(connector,
+ state, property, val);
+@@ -900,6 +902,8 @@ drm_atomic_connector_get_property(struct
+ *val = state->max_requested_bpc;
+ } else if (property == connector->privacy_screen_sw_state_property) {
+ *val = state->privacy_screen_sw_state;
++ } else if (property == connector->rotation_property) {
++ *val = state->rotation;
+ } else if (connector->funcs->atomic_get_property) {
+ return connector->funcs->atomic_get_property(connector,
+ state, property, val);
+--- a/drivers/gpu/drm/drm_blend.c
++++ b/drivers/gpu/drm/drm_blend.c
+@@ -235,6 +235,16 @@ int drm_plane_create_alpha_property(stru
+ }
+ EXPORT_SYMBOL(drm_plane_create_alpha_property);
+
++static const struct drm_prop_enum_list drm_rotate_props[] = {
++ { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" },
++ { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" },
++ { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" },
++ { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
++ { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" },
++ { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" },
++ { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" },
++};
++
+ /**
+ * drm_plane_create_rotation_property - create a new rotation property
+ * @plane: drm plane
+@@ -275,15 +285,6 @@ int drm_plane_create_rotation_property(s
+ unsigned int rotation,
+ unsigned int supported_rotations)
+ {
+- static const struct drm_prop_enum_list props[] = {
+- { __builtin_ffs(DRM_MODE_ROTATE_0) - 1, "rotate-0" },
+- { __builtin_ffs(DRM_MODE_ROTATE_90) - 1, "rotate-90" },
+- { __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" },
+- { __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
+- { __builtin_ffs(DRM_MODE_REFLECT_X) - 1, "reflect-x" },
+- { __builtin_ffs(DRM_MODE_REFLECT_Y) - 1, "reflect-y" },
+- { __builtin_ffs(DRM_MODE_TRANSPOSE) - 1, "transpose" },
+- };
+ struct drm_property *prop;
+
+ WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0);
+@@ -291,7 +292,8 @@ int drm_plane_create_rotation_property(s
+ WARN_ON(rotation & ~supported_rotations);
+
+ prop = drm_property_create_bitmask(plane->dev, 0, "rotation",
+- props, ARRAY_SIZE(props),
++ drm_rotate_props,
++ ARRAY_SIZE(drm_rotate_props),
+ supported_rotations);
+ if (!prop)
+ return -ENOMEM;
+@@ -307,6 +309,34 @@ int drm_plane_create_rotation_property(s
+ }
+ EXPORT_SYMBOL(drm_plane_create_rotation_property);
+
++int drm_connector_create_rotation_property(struct drm_connector *conn,
++ unsigned int rotation,
++ unsigned int supported_rotations)
++{
++ struct drm_property *prop;
++
++ WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0);
++ WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK));
++ WARN_ON(rotation & ~supported_rotations);
++
++ prop = drm_property_create_bitmask(conn->dev, 0, "rotation",
++ drm_rotate_props,
++ ARRAY_SIZE(drm_rotate_props),
++ supported_rotations);
++ if (!prop)
++ return -ENOMEM;
++
++ drm_object_attach_property(&conn->base, prop, rotation);
++
++ if (conn->state)
++ conn->state->rotation = rotation;
++
++ conn->rotation_property = prop;
++
++ return 0;
++}
++EXPORT_SYMBOL(drm_connector_create_rotation_property);
++
+ /**
+ * drm_rotation_simplify() - Try to simplify the rotation
+ * @rotation: Rotation to be simplified
+--- a/include/drm/drm_blend.h
++++ b/include/drm/drm_blend.h
+@@ -34,6 +34,7 @@
+ struct drm_device;
+ struct drm_atomic_state;
+ struct drm_plane;
++struct drm_connector;
+
+ static inline bool drm_rotation_90_or_270(unsigned int rotation)
+ {
+@@ -58,4 +59,8 @@ int drm_atomic_normalize_zpos(struct drm
+ struct drm_atomic_state *state);
+ int drm_plane_create_blend_mode_property(struct drm_plane *plane,
+ unsigned int supported_modes);
++
++int drm_connector_create_rotation_property(struct drm_connector *conn,
++ unsigned int rotation,
++ unsigned int supported_rotations);
+ #endif
+--- a/include/drm/drm_connector.h
++++ b/include/drm/drm_connector.h
+@@ -1029,6 +1029,11 @@ struct drm_connector_state {
+ * DRM blob property for HDR output metadata
+ */
+ struct drm_property_blob *hdr_output_metadata;
++
++ /**
++ * @rotation: Connector property to rotate the maximum output image.
++ */
++ u32 rotation;
+ };
+
+ /**
+@@ -1696,6 +1701,12 @@ struct drm_connector {
+ */
+ struct drm_property *privacy_screen_hw_state_property;
+
++ /**
++ * @rotation_property: Optional DRM property controlling rotation of the
++ * output.
++ */
++ struct drm_property *rotation_property;
++
+ #define DRM_CONNECTOR_POLL_HPD (1 << 0)
+ #define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
+ #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
--- /dev/null
+From 8346446098032c62d1de891a97c7f62264b18f81 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 14 Aug 2024 16:41:07 +0100
+Subject: [PATCH] drm/vc4: txp: Add a rotation property to the writeback
+ connector
+
+The txp block can implement transpose as it writes out the image
+data, so expose that through the new connector rotation property.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -15,6 +15,7 @@
+
+ #include <drm/drm_atomic.h>
+ #include <drm/drm_atomic_helper.h>
++#include <drm/drm_blend.h>
+ #include <drm/drm_drv.h>
+ #include <drm/drm_edid.h>
+ #include <drm/drm_fb_dma_helper.h>
+@@ -259,10 +260,15 @@ static int vc4_txp_connector_atomic_chec
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+
+ fb = conn_state->writeback_job->fb;
+- if (fb->width != crtc_state->mode.hdisplay ||
+- fb->height != crtc_state->mode.vdisplay) {
+- DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
+- fb->width, fb->height);
++ if ((conn_state->rotation == DRM_MODE_ROTATE_0 &&
++ fb->width != crtc_state->mode.hdisplay &&
++ fb->height != crtc_state->mode.vdisplay) ||
++ (conn_state->rotation == (DRM_MODE_ROTATE_0 | DRM_MODE_TRANSPOSE) &&
++ fb->width != crtc_state->mode.vdisplay &&
++ fb->height != crtc_state->mode.hdisplay)) {
++ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u vs mode %ux%u\n",
++ fb->width, fb->height,
++ crtc_state->mode.hdisplay, crtc_state->mode.vdisplay);
+ return -EINVAL;
+ }
+
+@@ -330,6 +336,9 @@ static void vc4_txp_connector_atomic_com
+ */
+ ctrl |= TXP_ALPHA_INVERT;
+
++ if (conn_state->rotation & DRM_MODE_TRANSPOSE)
++ ctrl |= TXP_TRANSPOSE;
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -608,6 +617,10 @@ static int vc4_txp_bind(struct device *d
+ if (ret)
+ return ret;
+
++ drm_connector_create_rotation_property(&txp->connector.base, DRM_MODE_ROTATE_0,
++ DRM_MODE_ROTATE_0 |
++ DRM_MODE_TRANSPOSE);
++
+ ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
+ dev_name(dev), txp);
+ if (ret)
--- /dev/null
+From a2fa911d90495762047c05dec4241308ae61ca36 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Sep 2024 18:05:00 +0100
+Subject: [PATCH] dmaengine: dw-axi-dmac: Allow client-chosen width
+
+For devices where transfer lengths are not known upfront, there is a
+danger when the destination is wider than the source that partial words
+can be lost at the end of a transfer. Ideally the controller would be
+able to flush the residue, but it can't - it's not even possible to tell
+that there is any.
+
+Instead, allow the client driver to avoid the problem by setting a
+smaller width.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -724,6 +724,18 @@ static int dw_axi_dma_set_hw_desc(struct
+ case DMA_DEV_TO_MEM:
+ reg_burst_msize = axi_dma_encode_msize(chan->config.src_maxburst);
+ reg_width = __ffs(chan->config.src_addr_width);
++ /*
++ * For devices where transfer lengths are not known upfront,
++ * there is a danger when the destination is wider than the
++ * source that partial words can be lost at the end of a transfer.
++ * Ideally the controller would be able to flush the residue, but
++ * it can't - it's not even possible to tell that there is any.
++ * Instead, allow the client driver to avoid the problem by setting
++ * a smaller width.
++ */
++ if (chan->config.dst_addr_width &&
++ (chan->config.dst_addr_width < mem_width))
++ mem_width = chan->config.dst_addr_width;
+ device_addr = phys_to_dma(chan->chip->dev, chan->config.src_addr);
+ ctllo = reg_width << CH_CTL_L_SRC_WIDTH_POS |
+ mem_width << CH_CTL_L_DST_WIDTH_POS |
--- /dev/null
+From 5cf7209c294a58029984880d4858e2d3c7e46a3c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Sep 2024 18:12:12 +0100
+Subject: [PATCH] spi: dw: Let the DMAC set the transfer widths
+
+SPI transfers are of defined length, unlike some UART traffic, so it is
+safe to let the DMA controller choose a suitable memory width.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-dw-dma.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/spi/spi-dw-dma.c
++++ b/drivers/spi/spi-dw-dma.c
+@@ -330,7 +330,6 @@ static int dw_spi_dma_config_tx(struct d
+ txconf.direction = DMA_MEM_TO_DEV;
+ txconf.dst_addr = dws->dma_addr;
+ txconf.dst_maxburst = dws->txburst;
+- txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+ txconf.device_fc = false;
+
+@@ -431,7 +430,6 @@ static int dw_spi_dma_config_rx(struct d
+ rxconf.direction = DMA_DEV_TO_MEM;
+ rxconf.src_addr = dws->dma_addr;
+ rxconf.src_maxburst = dws->rxburst;
+- rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+ rxconf.device_fc = false;
+
--- /dev/null
+From 8894298105f4cb41dfa41e0b0d3c40c3f7b92c44 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Sep 2024 18:22:24 +0100
+Subject: [PATCH] serial: pl011: Request a memory width of 1 byte
+
+In order to avoid losing residue bytes when a receive is terminated
+early, set the destination width to single bytes.
+
+Link: https://github.com/raspberrypi/linux/issues/6365
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/amba-pl011.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -468,6 +468,7 @@ static void pl011_dma_probe(struct uart_
+ .src_addr = uap->port.mapbase +
+ pl011_reg_to_offset(uap, REG_DR),
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
++ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .direction = DMA_DEV_TO_MEM,
+ .src_maxburst = uap->fifosize >> 2,
+ .device_fc = false,
--- /dev/null
+From 66aef6ce3557edd9d58d794e4a800c5be49ca0e7 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 11 Nov 2024 10:30:38 +0000
+Subject: [PATCH] drivers: usb: xhci: set HID bit in streaming endpoint
+ contexts
+
+The xHC may commence Host Initiated Data Moves for streaming endpoints -
+see USB3.2 spec s8.12.1.4.2.4. However, this behaviour is typically
+counterproductive as the submission of UAS URBs in {Status, Data,
+Command} order and 1 outstanding IO per stream ID means the device never
+enters Move Data after a HIMD for Status or Data stages with the same
+stream ID. For OUT transfers this is especially inefficient as the host
+will start transmitting multiple bulk packets as a burst, all of which
+get NAKed by the device - wasting bandwidth.
+
+Also, some buggy UAS adapters don't properly handle the EP flow control
+state this creates - e.g. RTL9210.
+
+Set Host Initiated Data Move Disable to always defer stream selection to
+the device. xHC implementations may treat this field as "don't care,
+forced to 1" anyway - xHCI 1.2 s4.12.1.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/usb/host/xhci-mem.c | 8 ++++++++
+ drivers/usb/host/xhci.h | 2 ++
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -716,6 +716,14 @@ void xhci_setup_streams_ep_input_ctx(str
+ ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK);
+ ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams)
+ | EP_HAS_LSA);
++
++ /*
++ * Set Host Initiated Data Move Disable to always defer stream
++ * selection to the device. xHC implementations may treat this
++ * field as "don't care, forced to 1" anyway - xHCI 1.2 s4.12.1.
++ */
++ ep_ctx->ep_info2 |= EP_HID;
++
+ ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma);
+ }
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -492,6 +492,8 @@ struct xhci_ep_ctx {
+ #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10)
+ /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */
+ #define EP_HAS_LSA (1 << 15)
++/* Host initiated data move disable in info2 */
++#define EP_HID (1 << 7)
+ /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */
+ #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff)
+
--- /dev/null
+From 35e50ee3d66e014d869f0d7a3468bef964d26d32 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 14 Nov 2024 13:14:02 +0000
+Subject: [PATCH] media: i2c: imx477: Add options for slightly modifying the
+ link freq
+
+The default link frequency of 450MHz has been noted to interfere
+with GPS if they are in close proximty.
+Add the option for 453 and 456MHz to move the signal slightly out
+of the band. (447MHz can not be offered as corruption is then observed
+on the 133x992 10bit mode).
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+
+fixup imx477 gps
+---
+ drivers/media/i2c/imx477.c | 86 +++++++++++++++++++++++++++++---------
+ 1 file changed, 67 insertions(+), 19 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -164,8 +164,48 @@ struct imx477_mode {
+ struct imx477_reg_list reg_list;
+ };
+
+-static const s64 imx477_link_freq_menu[] = {
+- IMX477_DEFAULT_LINK_FREQ,
++/* Link frequency setup */
++enum {
++ IMX477_LINK_FREQ_450MHZ,
++ IMX477_LINK_FREQ_453MHZ,
++ IMX477_LINK_FREQ_456MHZ,
++};
++
++static const s64 link_freqs[] = {
++ [IMX477_LINK_FREQ_450MHZ] = 450000000,
++ [IMX477_LINK_FREQ_453MHZ] = 453000000,
++ [IMX477_LINK_FREQ_456MHZ] = 456000000,
++};
++
++/* 450MHz is the nominal "default" link frequency */
++static const struct imx477_reg link_450Mhz_regs[] = {
++ {0x030E, 0x00},
++ {0x030F, 0x96},
++};
++
++static const struct imx477_reg link_453Mhz_regs[] = {
++ {0x030E, 0x00},
++ {0x030F, 0x97},
++};
++
++static const struct imx477_reg link_456Mhz_regs[] = {
++ {0x030E, 0x00},
++ {0x030F, 0x98},
++};
++
++static const struct imx477_reg_list link_freq_regs[] = {
++ [IMX477_LINK_FREQ_450MHZ] = {
++ .regs = link_450Mhz_regs,
++ .num_of_regs = ARRAY_SIZE(link_450Mhz_regs)
++ },
++ [IMX477_LINK_FREQ_453MHZ] = {
++ .regs = link_453Mhz_regs,
++ .num_of_regs = ARRAY_SIZE(link_453Mhz_regs)
++ },
++ [IMX477_LINK_FREQ_456MHZ] = {
++ .regs = link_456Mhz_regs,
++ .num_of_regs = ARRAY_SIZE(link_456Mhz_regs)
++ },
+ };
+
+ static const struct imx477_reg mode_common_regs[] = {
+@@ -558,8 +598,6 @@ static const struct imx477_reg mode_4056
+ {0x0309, 0x0c},
+ {0x030b, 0x02},
+ {0x030d, 0x02},
+- {0x030e, 0x00},
+- {0x030f, 0x96},
+ {0x0310, 0x01},
+ {0x0820, 0x07},
+ {0x0821, 0x08},
+@@ -659,8 +697,6 @@ static const struct imx477_reg mode_2028
+ {0x0309, 0x0c},
+ {0x030b, 0x02},
+ {0x030d, 0x02},
+- {0x030e, 0x00},
+- {0x030f, 0x96},
+ {0x0310, 0x01},
+ {0x0820, 0x07},
+ {0x0821, 0x08},
+@@ -760,8 +796,6 @@ static const struct imx477_reg mode_2028
+ {0x0309, 0x0c},
+ {0x030b, 0x02},
+ {0x030d, 0x02},
+- {0x030e, 0x00},
+- {0x030f, 0x96},
+ {0x0310, 0x01},
+ {0x0820, 0x07},
+ {0x0821, 0x08},
+@@ -890,8 +924,6 @@ static const struct imx477_reg mode_1332
+ {0x0309, 0x0a},
+ {0x030b, 0x02},
+ {0x030d, 0x02},
+- {0x030e, 0x00},
+- {0x030f, 0x96},
+ {0x0310, 0x01},
+ {0x0820, 0x07},
+ {0x0821, 0x08},
+@@ -1121,6 +1153,8 @@ struct imx477 {
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+
++ unsigned int link_freq_idx;
++
+ /* Current mode */
+ const struct imx477_mode *mode;
+
+@@ -1712,7 +1746,7 @@ static int imx477_get_selection(struct v
+ static int imx477_start_streaming(struct imx477 *imx477)
+ {
+ struct i2c_client *client = v4l2_get_subdevdata(&imx477->sd);
+- const struct imx477_reg_list *reg_list;
++ const struct imx477_reg_list *reg_list, *freq_regs;
+ const struct imx477_reg_list *extra_regs;
+ int ret, tm;
+
+@@ -1725,6 +1759,13 @@ static int imx477_start_streaming(struct
+ extra_regs->num_of_regs);
+ }
+
++ if (!ret) {
++ /* Update the link frequency registers */
++ freq_regs = &link_freq_regs[imx477->link_freq_idx];
++ ret = imx477_write_regs(imx477, freq_regs->regs,
++ freq_regs->num_of_regs);
++ }
++
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set common settings\n",
+ __func__);
+@@ -2010,9 +2051,8 @@ static int imx477_init_controls(struct i
+ /* LINK_FREQ is also read only */
+ imx477->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops,
+- V4L2_CID_LINK_FREQ,
+- ARRAY_SIZE(imx477_link_freq_menu) - 1, 0,
+- imx477_link_freq_menu);
++ V4L2_CID_LINK_FREQ, 1, 0,
++ &link_freqs[imx477->link_freq_idx]);
+ if (imx477->link_freq)
+ imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+@@ -2110,13 +2150,14 @@ static void imx477_free_controls(struct
+ mutex_destroy(&imx477->mutex);
+ }
+
+-static int imx477_check_hwcfg(struct device *dev)
++static int imx477_check_hwcfg(struct device *dev, struct imx477 *imx477)
+ {
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint ep_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ int ret = -EINVAL;
++ int i;
+
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ if (!endpoint) {
+@@ -2141,11 +2182,18 @@ static int imx477_check_hwcfg(struct dev
+ goto error_out;
+ }
+
+- if (ep_cfg.nr_of_link_frequencies != 1 ||
+- ep_cfg.link_frequencies[0] != IMX477_DEFAULT_LINK_FREQ) {
++ for (i = 0; i < ARRAY_SIZE(link_freqs); i++) {
++ if (link_freqs[i] == ep_cfg.link_frequencies[0]) {
++ imx477->link_freq_idx = i;
++ break;
++ }
++ }
++
++ if (i == ARRAY_SIZE(link_freqs)) {
+ dev_err(dev, "Link frequency not supported: %lld\n",
+ ep_cfg.link_frequencies[0]);
+- goto error_out;
++ ret = -EINVAL;
++ goto error_out;
+ }
+
+ ret = 0;
+@@ -2206,7 +2254,7 @@ static int imx477_probe(struct i2c_clien
+ (const struct imx477_compatible_data *)match->data;
+
+ /* Check the hardware configuration in device tree */
+- if (imx477_check_hwcfg(dev))
++ if (imx477_check_hwcfg(dev, imx477))
+ return -EINVAL;
+
+ /* Default the trigger mode from OF to -1, which means invalid */
--- /dev/null
+From 7e253a062d5a14de13ccfb410570975099c238be Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 14 Nov 2024 13:15:24 +0000
+Subject: [PATCH] dtoverlays: Add link-frequency override to imx477/378 overlay
+
+Copy of the imx708 change.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 4 ++++
+ arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 1 +
+ 2 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2780,6 +2780,8 @@ Params: rotation Mounting
+ camera clamping I/Os such as XVS to 0V.
+ sync-source Configure as vsync source
+ sync-sink Configure as vsync sink
++ link-frequency Allowable link frequency values to use in Hz:
++ 450000000 (default), 453000000, 456000000.
+
+
+ Name: imx462
+@@ -2822,6 +2824,8 @@ Params: rotation Mounting
+ camera clamping I/Os such as XVS to 0V.
+ sync-source Configure as vsync source
+ sync-sink Configure as vsync sink
++ link-frequency Allowable link frequency values to use in Hz:
++ 450000000 (default), 453000000, 456000000.
+
+
+ Name: imx500
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -80,6 +80,7 @@
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+ <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
+ always-on = <0>, "+99";
++ link-frequency = <&cam_endpoint>,"link-frequencies#0";
+ };
+ };
+
--- /dev/null
+From 59a8855b51c1d8acf37d3c80f34782d71f474617 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 13 Nov 2024 10:37:22 +0000
+Subject: [PATCH] dmaengine: dw-axi-dmac: Only start idle channels
+
+Attempting to start a non-idle channel causes an error message to be
+logged, and is inefficient. Test for emptiness of the desc_issued list
+before doing so.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -536,9 +536,11 @@ static void dma_chan_issue_pending(struc
+ {
+ struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+ unsigned long flags;
++ bool was_empty;
+
+ spin_lock_irqsave(&chan->vc.lock, flags);
+- if (vchan_issue_pending(&chan->vc))
++ was_empty = list_empty(&chan->vc.desc_issued);
++ if (vchan_issue_pending(&chan->vc) && was_empty)
+ axi_chan_start_first_queued(chan);
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
+ }
--- /dev/null
+From 0b76dec8dfba8c1a4793dff0c86bf73d088a812e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 1 Nov 2024 09:12:01 +0000
+Subject: [PATCH] dts: bcm2712-rpi: Add RP1 firmware and mailboxes
+
+Declare the communications channel to RP1.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 4 +--
+ .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 4 +--
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 4 +++
+ arch/arm64/boot/dts/broadcom/rp1.dtsi | 27 +++++++++++++++++++
+ 4 files changed, 35 insertions(+), 4 deletions(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -195,7 +195,7 @@ i2c_rp1boot: &_i2c3 { };
+ // This is the RP1 peripheral space
+ ranges = <0xc0 0x40000000
+ 0x02000000 0x00 0x00000000
+- 0x00 0x00400000>;
++ 0x00 0x00410000>;
+
+ dma-ranges =
+ // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+@@ -207,7 +207,7 @@ i2c_rp1boot: &_i2c3 { };
+ // This allows the RP1 DMA controller to address RP1 hardware
+ <0xc0 0x40000000
+ 0x02000000 0x0 0x00000000
+- 0x0 0x00400000>,
++ 0x0 0x00410000>,
+
+ // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+ <0x00 0x00000000
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -176,7 +176,7 @@ i2c_rp1boot: &_i2c3 { };
+ // This is the RP1 peripheral space
+ ranges = <0xc0 0x40000000
+ 0x02000000 0x00 0x00000000
+- 0x00 0x00400000>;
++ 0x00 0x00410000>;
+
+ dma-ranges =
+ // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+@@ -188,7 +188,7 @@ i2c_rp1boot: &_i2c3 { };
+ // This allows the RP1 DMA controller to address RP1 hardware
+ <0xc0 0x40000000
+ 0x02000000 0x0 0x00000000
+- 0x0 0x00400000>,
++ 0x0 0x00410000>,
+
+ // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
+ <0x00 0x00000000
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -294,6 +294,10 @@ pciex4: &pcie2 { };
+ status = "okay";
+ };
+
++&rp1_mbox {
++ status = "okay";
++};
++
+ /* Add some gpiomem nodes to make the devices accessible to userspace.
+ * /dev/gpiomem<n> should expose the registers for the interface with DT alias
+ * gpio<n>.
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -13,6 +13,14 @@
+
+ // ranges and dma-ranges must be provided by the includer
+
++ rp1_mbox: mailbox@8000 {
++ compatible = "raspberrypi,rp1-mbox";
++ status = "disabled";
++ reg = <0xc0 0x40008000 0x0 0x4000>; // SYSCFG
++ interrupts = <RP1_INT_SYSCFG IRQ_TYPE_LEVEL_HIGH>;
++ #mbox-cells = <1>;
++ };
++
+ rp1_clocks: clocks@18000 {
+ compatible = "raspberrypi,rp1-clocks";
+ #clock-cells = <1>;
+@@ -1183,6 +1191,19 @@
+ assigned-clocks = <&rp1_clocks RP1_CLK_DPI>;
+ assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>;
+ };
++
++ sram: sram@400000 {
++ compatible = "mmio-sram";
++ reg = <0xc0 0x40400000 0x0 0x10000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0 0xc0 0x40400000 0x10000>;
++
++ rp1_fw_shmem: shmem@ff00 {
++ compatible = "raspberrypi,rp1-shmem";
++ reg = <0xff00 0x100>; // firmware mailbox buffer
++ };
++ };
+ };
+ };
+
+@@ -1281,6 +1302,12 @@
+ };
+
+ / {
++ rp1_firmware: rp1_firmware {
++ compatible = "raspberrypi,rp1-firmware", "simple-mfd";
++ mboxes = <&rp1_mbox 0>;
++ shmem = <&rp1_fw_shmem>;
++ };
++
+ rp1_vdd_3v3: rp1_vdd_3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd-3v3";
--- /dev/null
+From 3e3c1b9922b22d362a4a9133361597ac80b974bb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 1 Nov 2024 09:13:53 +0000
+Subject: [PATCH] dts: bcm2712-rpi: Add the RP1 PIO device
+
+Declare the device that proxies RP1's PIO hardware.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 5 +++++
+ arch/arm64/boot/dts/broadcom/rp1.dtsi | 12 ++++++++++++
+ 2 files changed, 17 insertions(+)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi
+@@ -97,6 +97,10 @@
+ };
+ };
+
++pio: &rp1_pio {
++ status = "okay";
++};
++
+ / {
+ chosen: chosen {
+ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe cgroup_disable=memory numa_policy=interleave";
+@@ -129,6 +133,7 @@
+ i2c12 = &i2c_rp1boot;
+ mailbox = &mailbox;
+ mmc0 = &sdio1;
++ pio0 = &pio;
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial10 = &uart10;
+--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
++++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
+@@ -1028,6 +1028,18 @@
+ status = "disabled";
+ };
+
++ rp1_pio: pio@178000 {
++ reg = <0xc0 0x40178000 0x0 0x20>;
++ compatible = "raspberrypi,rp1-pio";
++ firmware = <&rp1_firmware>;
++ dmas = <&rp1_dma RP1_DMA_PIO_CH0_TX>, <&rp1_dma RP1_DMA_PIO_CH0_RX>,
++ <&rp1_dma RP1_DMA_PIO_CH1_TX>, <&rp1_dma RP1_DMA_PIO_CH1_RX>,
++ <&rp1_dma RP1_DMA_PIO_CH2_TX>, <&rp1_dma RP1_DMA_PIO_CH2_RX>,
++ <&rp1_dma RP1_DMA_PIO_CH3_TX>, <&rp1_dma RP1_DMA_PIO_CH3_RX>;
++ dma-names = "tx0", "rx0", "tx1", "rx1", "tx2", "rx2", "tx3", "rx3";
++ status = "disabled";
++ };
++
+ rp1_mmc0: mmc@180000 {
+ reg = <0xc0 0x40180000 0x0 0x100>;
+ compatible = "raspberrypi,rp1-dwcmshc";
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
-@@ -454,6 +454,17 @@ config PWM_PCA9685
+@@ -465,6 +465,17 @@ config PWM_PCA9685
To compile this driver as a module, choose M here: the module
will be called pwm-pca9685.
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
-@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
+@@ -42,6 +42,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o
obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
--- /dev/null
+From ba7e2e3d03a432acbc338c6c03e46dcd97cfa1b3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 7 Nov 2024 11:41:33 +0000
+Subject: [PATCH] overlays: Add pwm-pio overlay
+
+Add an overlay to enable a single-channel PIO-assisted PWM interface on any
+header pin.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 8 ++++
+ arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++
+ .../arm/boot/dts/overlays/pwm-pio-overlay.dts | 39 +++++++++++++++++++
+ 4 files changed, 52 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pwm-pio-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -219,6 +219,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ pwm-2chan.dtbo \
+ pwm-gpio.dtbo \
+ pwm-ir-tx.dtbo \
++ pwm-pio.dtbo \
+ pwm1.dtbo \
+ qca7000.dtbo \
+ qca7000-uart0.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3926,6 +3926,14 @@ Params: gpio_pin Output G
+ func Pin function (default 2 = Alt5)
+
+
++Name: pwm-pio
++Info: Configures a GPIO pin as PIO-assisted PWM output. Unlike hardware PWM,
++ this can be used on any RP1 GPIO in bank 0 (0-27). Up to 4 are
++ supported, assuming nothing else is using PIO. Pi 5 only.
++Load: dtoverlay=pwm-pio,<param>=<val>
++Params: gpio Output GPIO (0-27, default 4)
++
++
+ Name: pwm1
+ Info: Configures one or two PWM channel on PWM1 (BCM2711 only)
+ N.B.:
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -240,6 +240,10 @@
+ bcm2712;
+ };
+
++ pwm-pio {
++ bcm2712;
++ };
++
+ pwm1 {
+ bcm2711;
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pwm-pio-overlay.dts
+@@ -0,0 +1,39 @@
++// SPDX-License-Identifier: GPL-2.0
++// Device tree overlay for RP1 PIO PWM.
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&gpio>;
++ __overlay__ {
++ pwm_pio_pins: pwm_pio_pins@4 {
++ brcm,pins = <4>; /* gpio 4 */
++ function = "pio";
++ bias-disable;
++ };
++ };
++ };
++
++ fragment@1 {
++ target-path = "/";
++ __overlay__ {
++ pwm_pio: pwm_pio@4 {
++ compatible = "raspberrypi,pwm-pio-rp1";
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pio_pins>;
++ gpios = <&gpio 4 0>;
++ };
++ };
++ };
++
++ __overrides__ {
++ gpio = <&pwm_pio>,"gpios:4",
++ <&pwm_pio_pins>,"brcm,pins:0",
++ /* modify reg values to allow multiple instantiation */
++ <&pwm_pio>,"reg:0",
++ <&pwm_pio_pins>,"reg:0";
++ };
++};
From b4472d09b1ffdafd8132803ffbec62596e559fd8 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 18 Nov 2024 09:10:52 +0000
-Subject: [PATCH 1394/1482] misc: rp1-pio: Add compat_ioctl method
+Subject: [PATCH] misc: rp1-pio: Add compat_ioctl method
Provide a compat_ioctl method, to support running a 64-bit kernel with
a 32-bit userland.
--- a/drivers/misc/rp1-pio.c
+++ b/drivers/misc/rp1-pio.c
-@@ -1023,11 +1023,75 @@ static long rp1_pio_ioctl(struct file *f
+@@ -996,11 +996,75 @@ static long rp1_pio_ioctl(struct file *f
return ret;
}
-From 24cb07b0c0724a22e474d12e7c2d5b834bf3b076 Mon Sep 17 00:00:00 2001
+From 0e4968617aad7d0f88e0a630499202eaae407a19 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Tue, 26 Mar 2024 15:57:46 +0000
-Subject: [PATCH 0998/1085] i2c: designware: Add support for bus clear feature
+Subject: [PATCH] i2c: designware: Add support for bus clear feature
Newer versions of the DesignWare I2C block support the detection of
stuck signals, and a mechanism to recover from them. Add the required
DW_IC_TX_ABRT_10ADDR1_NOACK | \
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
-@@ -212,6 +212,7 @@ static int i2c_dw_set_timings_master(str
+@@ -215,6 +215,7 @@ static int i2c_dw_set_timings_master(str
*/
static int i2c_dw_init_master(struct dw_i2c_dev *dev)
{
int ret;
ret = i2c_dw_acquire_lock(dev);
-@@ -235,6 +236,17 @@ static int i2c_dw_init_master(struct dw_
+@@ -238,6 +239,17 @@ static int i2c_dw_init_master(struct dw_
regmap_write(dev->map, DW_IC_HS_SCL_LCNT, dev->hs_lcnt);
}
/* Write SDA hold time if supported */
if (dev->sda_hold_time)
regmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);
-@@ -1071,6 +1083,7 @@ int i2c_dw_probe_master(struct dw_i2c_de
+@@ -1074,6 +1086,7 @@ int i2c_dw_probe_master(struct dw_i2c_de
struct i2c_adapter *adap = &dev->adapter;
unsigned long irq_flags;
unsigned int ic_con;
int ret;
init_completion(&dev->cmd_complete);
-@@ -1106,7 +1119,11 @@ int i2c_dw_probe_master(struct dw_i2c_de
+@@ -1109,7 +1122,11 @@ int i2c_dw_probe_master(struct dw_i2c_de
if (ret)
return ret;
--- /dev/null
+From 32511f035b086bca254d8adab234cef3541492b4 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 17 Oct 2024 11:37:29 +0100
+Subject: [PATCH] drivers: media: pci: Update Hailo accelerator device driver
+ to v4.19
+
+Sourced from https://github.com/hailo-ai/hailort-drivers/
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/pci/hailo/Makefile | 4 +-
+ drivers/media/pci/hailo/common/fw_operation.c | 50 ++-
+ drivers/media/pci/hailo/common/fw_operation.h | 8 +-
+ .../media/pci/hailo/common/fw_validation.c | 10 +-
+ .../media/pci/hailo/common/fw_validation.h | 7 +-
+ .../pci/hailo/common/hailo_ioctl_common.h | 28 +-
+ .../media/pci/hailo/common/hailo_resource.c | 23 +-
+ .../media/pci/hailo/common/hailo_resource.h | 2 +-
+ drivers/media/pci/hailo/common/pcie_common.c | 380 +++++++++---------
+ drivers/media/pci/hailo/common/pcie_common.h | 38 +-
+ drivers/media/pci/hailo/common/soc_structs.h | 79 ++++
+ drivers/media/pci/hailo/common/utils.h | 23 +-
+ drivers/media/pci/hailo/common/vdma_common.c | 93 +++--
+ drivers/media/pci/hailo/common/vdma_common.h | 22 +-
+ drivers/media/pci/hailo/src/fops.c | 284 ++-----------
+ drivers/media/pci/hailo/src/fops.h | 5 +-
+ drivers/media/pci/hailo/src/nnc.c | 299 ++++++++++++++
+ drivers/media/pci/hailo/src/nnc.h | 22 +
+ drivers/media/pci/hailo/src/pci_soc_ioctl.c | 155 -------
+ drivers/media/pci/hailo/src/pcie.c | 166 +++-----
+ drivers/media/pci/hailo/src/pcie.h | 26 +-
+ drivers/media/pci/hailo/src/soc.c | 244 +++++++++++
+ .../pci/hailo/src/{pci_soc_ioctl.h => soc.h} | 13 +-
+ drivers/media/pci/hailo/src/sysfs.c | 2 +-
+ drivers/media/pci/hailo/src/sysfs.h | 2 +-
+ drivers/media/pci/hailo/src/utils.c | 26 --
+ drivers/media/pci/hailo/utils/compact.h | 2 +-
+ drivers/media/pci/hailo/utils/fw_common.h | 2 +-
+ .../pci/hailo/utils/integrated_nnc_utils.c | 10 +-
+ .../pci/hailo/utils/integrated_nnc_utils.h | 2 +-
+ drivers/media/pci/hailo/utils/logs.c | 2 +-
+ drivers/media/pci/hailo/utils/logs.h | 2 +-
+ drivers/media/pci/hailo/vdma/ioctl.c | 18 +-
+ drivers/media/pci/hailo/vdma/ioctl.h | 6 +-
+ drivers/media/pci/hailo/vdma/memory.c | 12 +-
+ drivers/media/pci/hailo/vdma/memory.h | 2 +-
+ drivers/media/pci/hailo/vdma/vdma.c | 39 +-
+ drivers/media/pci/hailo/vdma/vdma.h | 5 +-
+ 38 files changed, 1224 insertions(+), 889 deletions(-)
+ create mode 100644 drivers/media/pci/hailo/common/soc_structs.h
+ create mode 100644 drivers/media/pci/hailo/src/nnc.c
+ create mode 100644 drivers/media/pci/hailo/src/nnc.h
+ delete mode 100755 drivers/media/pci/hailo/src/pci_soc_ioctl.c
+ create mode 100644 drivers/media/pci/hailo/src/soc.c
+ rename drivers/media/pci/hailo/src/{pci_soc_ioctl.h => soc.h} (53%)
+ mode change 100755 => 100644
+ delete mode 100644 drivers/media/pci/hailo/src/utils.c
+
+--- a/drivers/media/pci/hailo/Makefile
++++ b/drivers/media/pci/hailo/Makefile
+@@ -8,9 +8,9 @@ obj-$(CONFIG_MEDIA_PCI_HAILO) := hailo_p
+
+ hailo_pci-objs += src/pcie.o
+ hailo_pci-objs += src/fops.o
+-hailo_pci-objs += src/utils.o
+ hailo_pci-objs += src/sysfs.o
+-hailo_pci-objs += src/pci_soc_ioctl.o
++hailo_pci-objs += src/nnc.o
++hailo_pci-objs += src/soc.o
+
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_validation.o
+ hailo_pci-objs += $(COMMON_SRC_DIRECTORY)/fw_operation.o
+--- a/drivers/media/pci/hailo/common/fw_operation.c
++++ b/drivers/media/pci/hailo/common/fw_operation.c
+@@ -1,7 +1,7 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+-**/
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
+
+ #include "fw_operation.h"
+
+@@ -15,7 +15,10 @@ typedef struct {
+ u32 chip_offset;
+ } FW_DEBUG_BUFFER_HEADER_t;
+
+-#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t))
++#define DEBUG_BUFFER_DATA_SIZE (DEBUG_BUFFER_TOTAL_SIZE - sizeof(FW_DEBUG_BUFFER_HEADER_t))
++#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640)
++#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024)
++#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE)
+
+ int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification)
+ {
+@@ -35,6 +38,21 @@ int hailo_read_firmware_notification(str
+ return 0;
+ }
+
++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource,
++ struct hailo_d2h_notification *notification)
++{
++ struct hailo_resource notification_resource;
++
++ if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resource->size) {
++ return -EINVAL;
++ }
++
++ notification_resource.address = resource->address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET,
++ notification_resource.size = sizeof(struct hailo_d2h_notification);
++
++ return hailo_read_firmware_notification(¬ification_resource, notification);
++}
++
+ static inline size_t calculate_log_ready_to_read(FW_DEBUG_BUFFER_HEADER_t *header)
+ {
+ size_t ready_to_read = 0;
+@@ -100,4 +118,30 @@ long hailo_read_firmware_log(struct hail
+
+ params->read_bytes = ready_to_read;
+ return 0;
++}
++
++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params)
++{
++ long err = 0;
++ struct hailo_resource log_resource = {resource->address, DEBUG_BUFFER_TOTAL_SIZE};
++
++ if (HAILO_CPU_ID_CPU0 == params->cpu_id) {
++ log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET;
++ } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) {
++ log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET;
++ } else {
++ return -EINVAL;
++ }
++
++ if (0 == params->buffer_size) {
++ params->read_bytes = 0;
++ return 0;
++ }
++
++ err = hailo_read_firmware_log(&log_resource, params);
++ if (0 != err) {
++ return err;
++ }
++
++ return 0;
+ }
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/fw_operation.h
++++ b/drivers/media/pci/hailo/common/fw_operation.h
+@@ -1,7 +1,7 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2022 Hailo Technologies Ltd. All rights reserved.
+-**/
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
+
+ #ifndef _HAILO_COMMON_FIRMWARE_OPERATION_H_
+ #define _HAILO_COMMON_FIRMWARE_OPERATION_H_
+@@ -16,8 +16,12 @@ extern "C" {
+
+ int hailo_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification);
+
++int hailo_pcie_read_firmware_notification(struct hailo_resource *resource, struct hailo_d2h_notification *notification);
++
+ long hailo_read_firmware_log(struct hailo_resource *fw_logger_resource, struct hailo_read_log_params *params);
+
++long hailo_pcie_read_firmware_log(struct hailo_resource *resource, struct hailo_read_log_params *params);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--- a/drivers/media/pci/hailo/common/fw_validation.c
++++ b/drivers/media/pci/hailo/common/fw_validation.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "fw_validation.h"
+@@ -85,15 +85,15 @@ exit:
+ }
+
+ int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address,
+- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert)
++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert)
+ {
+
+- secure_boot_certificate_t *firmware_cert = NULL;
++ secure_boot_certificate_header_t *firmware_cert = NULL;
+ int err = -EINVAL;
+ u32 consumed_firmware_offset = *outer_consumed_firmware_offset;
+
+- firmware_cert = (secure_boot_certificate_t *) (firmware_base_address + consumed_firmware_offset);
+- CONSUME_FIRMWARE(sizeof(secure_boot_certificate_t), -EINVAL);
++ firmware_cert = (secure_boot_certificate_header_t *) (firmware_base_address + consumed_firmware_offset);
++ CONSUME_FIRMWARE(sizeof(secure_boot_certificate_header_t), -EINVAL);
+
+ if ((MAXIMUM_FIRMWARE_CERT_KEY_SIZE < firmware_cert->key_size) ||
+ (MAXIMUM_FIRMWARE_CERT_CONTENT_SIZE < firmware_cert->content_size)) {
+--- a/drivers/media/pci/hailo/common/fw_validation.h
++++ b/drivers/media/pci/hailo/common/fw_validation.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef PCIE_COMMON_FIRMWARE_HEADER_UTILS_H_
+@@ -44,8 +44,7 @@ typedef struct {
+ typedef struct {
+ u32 key_size;
+ u32 content_size;
+- u8 certificates_data[0];
+-} secure_boot_certificate_t;
++} secure_boot_certificate_header_t;
+
+ #ifdef _MSC_VER
+ #pragma warning(pop)
+@@ -60,6 +59,6 @@ int FW_VALIDATION__validate_fw_header(ui
+ firmware_header_t **out_firmware_header, enum hailo_board_type board_type);
+
+ int FW_VALIDATION__validate_cert_header(uintptr_t firmware_base_address,
+- size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_t **out_firmware_cert);
++ size_t firmware_size, u32 *outer_consumed_firmware_offset, secure_boot_certificate_header_t **out_firmware_cert);
+
+ #endif
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/hailo_ioctl_common.h
++++ b/drivers/media/pci/hailo/common/hailo_ioctl_common.h
+@@ -1,13 +1,13 @@
+ // SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) AND MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_IOCTL_COMMON_H_
+ #define _HAILO_IOCTL_COMMON_H_
+
+ #define HAILO_DRV_VER_MAJOR 4
+-#define HAILO_DRV_VER_MINOR 18
++#define HAILO_DRV_VER_MINOR 19
+ #define HAILO_DRV_VER_REVISION 0
+
+ #define _STRINGIFY_EXPANDED( x ) #x
+@@ -17,10 +17,11 @@
+
+ // This value is not easily changeable.
+ // For example: the channel interrupts ioctls assume we have up to 32 channels
+-#define MAX_VDMA_CHANNELS_PER_ENGINE (32)
+-#define MAX_VDMA_ENGINES (3)
+-#define SIZE_OF_VDMA_DESCRIPTOR (16)
+-#define VDMA_DEST_CHANNELS_START (16)
++#define MAX_VDMA_CHANNELS_PER_ENGINE (32)
++#define VDMA_CHANNELS_PER_ENGINE_PER_DIRECTION (16)
++#define MAX_VDMA_ENGINES (3)
++#define SIZE_OF_VDMA_DESCRIPTOR (16)
++#define VDMA_DEST_CHANNELS_START (16)
+
+ #define HAILO_VDMA_MAX_ONGOING_TRANSFERS (128)
+ #define HAILO_VDMA_MAX_ONGOING_TRANSFERS_MASK (HAILO_VDMA_MAX_ONGOING_TRANSFERS - 1)
+@@ -37,8 +38,8 @@
+ #define FW_ACCESS_APP_CPU_CONTROL_MASK (1 << FW_ACCESS_CONTROL_INTERRUPT_SHIFT)
+ #define FW_ACCESS_DRIVER_SHUTDOWN_SHIFT (2)
+ #define FW_ACCESS_DRIVER_SHUTDOWN_MASK (1 << FW_ACCESS_DRIVER_SHUTDOWN_SHIFT)
+-#define FW_ACCESS_SOC_CONNECT_SHIFT (3)
+-#define FW_ACCESS_SOC_CONNECT_MASK (1 << FW_ACCESS_SOC_CONNECT_SHIFT)
++#define FW_ACCESS_SOC_CONTROL_SHIFT (3)
++#define FW_ACCESS_SOC_CONTROL_MASK (1 << FW_ACCESS_SOC_CONTROL_SHIFT)
+
+ #define INVALID_VDMA_CHANNEL (0xff)
+
+@@ -245,6 +246,12 @@ struct hailo_desc_list_release_params {
+ uintptr_t desc_handle; // in
+ };
+
++struct hailo_write_action_list_params {
++ uint8_t *data; // in
++ size_t size; // in
++ uint64_t dma_address; // out
++};
++
+ /* structure used in ioctl HAILO_DESC_LIST_BIND_VDMA_BUFFER */
+ struct hailo_desc_list_program_params {
+ size_t buffer_handle; // in
+@@ -508,6 +515,7 @@ struct hailo_vdma_launch_transfer_params
+
+ /* structure used in ioctl HAILO_SOC_CONNECT */
+ struct hailo_soc_connect_params {
++ uint16_t port_number; // in
+ uint8_t input_channel_index; // out
+ uint8_t output_channel_index; // out
+ uintptr_t input_desc_handle; // in
+@@ -522,6 +530,7 @@ struct hailo_soc_close_params {
+
+ /* structure used in ioctl HAILO_PCI_EP_ACCEPT */
+ struct hailo_pci_ep_accept_params {
++ uint16_t port_number; // in
+ uint8_t input_channel_index; // out
+ uint8_t output_channel_index; // out
+ uintptr_t input_desc_handle; // in
+@@ -562,6 +571,7 @@ struct tCompatibleHailoIoctlData
+ struct hailo_soc_close_params SocCloseParams;
+ struct hailo_pci_ep_accept_params AcceptParams;
+ struct hailo_pci_ep_close_params PciEpCloseParams;
++ struct hailo_write_action_list_params WriteActionListParams;
+ } Buffer;
+ };
+ #endif // _MSC_VER
+@@ -632,6 +642,7 @@ enum hailo_nnc_ioctl_code {
+ HAILO_DISABLE_NOTIFICATION_CODE,
+ HAILO_READ_LOG_CODE,
+ HAILO_RESET_NN_CORE_CODE,
++ HAILO_WRITE_ACTION_LIST_CODE,
+
+ // Must be last
+ HAILO_NNC_IOCTL_MAX_NR
+@@ -642,6 +653,7 @@ enum hailo_nnc_ioctl_code {
+ #define HAILO_DISABLE_NOTIFICATION _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_DISABLE_NOTIFICATION_CODE)
+ #define HAILO_READ_LOG _IOWR_(HAILO_NNC_IOCTL_MAGIC, HAILO_READ_LOG_CODE, struct hailo_read_log_params)
+ #define HAILO_RESET_NN_CORE _IO_(HAILO_NNC_IOCTL_MAGIC, HAILO_RESET_NN_CORE_CODE)
++#define HAILO_WRITE_ACTION_LIST _IOW_(HAILO_NNC_IOCTL_MAGIC, HAILO_WRITE_ACTION_LIST_CODE, struct hailo_write_action_list_params)
+
+ enum hailo_soc_ioctl_code {
+ HAILO_SOC_IOCTL_CONNECT_CODE,
+--- a/drivers/media/pci/hailo/common/hailo_resource.c
++++ b/drivers/media/pci/hailo/common/hailo_resource.c
+@@ -1,24 +1,31 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "hailo_resource.h"
+
++#include "utils.h"
++
+ #include <linux/io.h>
+ #include <linux/errno.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+
++#define ALIGN_TO_32_BIT(addr) ((addr) & (~((uintptr_t)0x3)))
+
+ u8 hailo_resource_read8(struct hailo_resource *resource, size_t offset)
+ {
+- return ioread8((u8*)resource->address + offset);
++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++ return (u8)READ_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, val);
+ }
+
+ u16 hailo_resource_read16(struct hailo_resource *resource, size_t offset)
+ {
+- return ioread16((u8*)resource->address + offset);
++ u32 val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++ return (u16)READ_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, val);
+ }
+
+ u32 hailo_resource_read32(struct hailo_resource *resource, size_t offset)
+@@ -28,12 +35,18 @@ u32 hailo_resource_read32(struct hailo_r
+
+ void hailo_resource_write8(struct hailo_resource *resource, size_t offset, u8 value)
+ {
+- iowrite8(value, (u8*)resource->address + offset);
++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++ iowrite32(WRITE_BITS_AT_OFFSET(BYTE_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value),
++ (u8*)ALIGN_TO_32_BIT(resource->address + offset));
+ }
+
+ void hailo_resource_write16(struct hailo_resource *resource, size_t offset, u16 value)
+ {
+- iowrite16(value, (u8*)resource->address + offset);
++ u32 initial_val = ioread32((u8*)ALIGN_TO_32_BIT(resource->address + offset));
++ u64 offset_in_bits = BITS_IN_BYTE * ((resource->address + offset) - ALIGN_TO_32_BIT(resource->address + offset));
++ iowrite32(WRITE_BITS_AT_OFFSET(WORD_SIZE * BITS_IN_BYTE, offset_in_bits, initial_val, value),
++ (u8*)ALIGN_TO_32_BIT(resource->address + offset));
+ }
+
+ void hailo_resource_write32(struct hailo_resource *resource, size_t offset, u32 value)
+--- a/drivers/media/pci/hailo/common/hailo_resource.h
++++ b/drivers/media/pci/hailo/common/hailo_resource.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_COMMON_HAILO_RESOURCE_H_
+--- a/drivers/media/pci/hailo/common/pcie_common.c
++++ b/drivers/media/pci/hailo/common/pcie_common.c
+@@ -1,10 +1,11 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "pcie_common.h"
+ #include "fw_operation.h"
++#include "soc_structs.h"
+
+ #include <linux/errno.h>
+ #include <linux/bug.h>
+@@ -35,10 +36,6 @@
+ #define FIRMWARE_LOAD_WAIT_MAX_RETRIES (100)
+ #define FIRMWARE_LOAD_SLEEP_MS (50)
+
+-#define PCIE_APP_CPU_DEBUG_OFFSET (8*1024)
+-#define PCIE_CORE_CPU_DEBUG_OFFSET (PCIE_APP_CPU_DEBUG_OFFSET + DEBUG_BUFFER_TOTAL_SIZE)
+-
+-#define PCIE_D2H_NOTIFICATION_SRAM_OFFSET (0x640 + 0x640)
+ #define PCIE_REQUEST_SIZE_OFFSET (0x640)
+
+ #define PCIE_CONFIG_VENDOR_OFFSET (0x0098)
+@@ -59,7 +56,6 @@ struct hailo_fw_addresses {
+ u32 app_fw_code_ram_base;
+ u32 boot_key_cert;
+ u32 boot_cont_cert;
+- u32 boot_fw_trigger;
+ u32 core_code_ram_base;
+ u32 core_fw_header;
+ u32 atr0_trsl_addr1;
+@@ -69,13 +65,11 @@ struct hailo_fw_addresses {
+
+ struct loading_stage {
+ const struct hailo_file_batch *batch;
++ u32 trigger_address;
+ };
+
+ struct hailo_board_compatibility {
+ struct hailo_fw_addresses fw_addresses;
+- const char *fw_filename;
+- const struct hailo_config_constants board_cfg;
+- const struct hailo_config_constants fw_cfg;
+ const struct loading_stage stages[MAX_LOADING_STAGES];
+ };
+
+@@ -85,28 +79,32 @@ static const struct hailo_file_batch hai
+ .address = 0xA0000,
+ .max_size = 0x8004,
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ {
+ .filename = "hailo/hailo10h/u-boot.dtb.signed",
+ .address = 0xA8004,
+ .max_size = 0x20000,
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ {
+ .filename = "hailo/hailo10h/scu_fw.bin",
+ .address = 0x20000,
+ .max_size = 0x40000,
+ .is_mandatory = true,
+- .has_header = true
++ .has_header = true,
++ .has_core = false
+ },
+ {
+ .filename = NULL,
+ .address = 0x00,
+ .max_size = 0x00,
+ .is_mandatory = false,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ }
+ };
+
+@@ -116,36 +114,140 @@ static const struct hailo_file_batch hai
+ .address = 0x85000000,
+ .max_size = 0x1000000,
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ {
+ .filename = "hailo/hailo10h/u-boot-tfa.itb",
+ .address = 0x86000000,
+ .max_size = 0x1000000,
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ {
+ .filename = "hailo/hailo10h/fitImage",
+ .address = 0x87000000,
+ .max_size = 0x1000000,
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ {
+ .filename = "hailo/hailo10h/core-image-minimal-hailo10-m2.ext4.gz",
+ .address = 0x88000000,
+ .max_size = 0x20000000, // Max size 512MB
+ .is_mandatory = true,
+- .has_header = false
++ .has_header = false,
++ .has_core = false
+ },
+ };
+
++// If loading linux from EMMC - only need few files from second batch (u-boot-spl.bin and u-boot-tfa.itb)
++static const struct hailo_file_batch hailo10h_files_stg2_linux_in_emmc[] = {
++ {
++ .filename = "hailo/hailo10h/u-boot-spl.bin",
++ .address = 0x85000000,
++ .max_size = 0x1000000,
++ .is_mandatory = true,
++ .has_header = false,
++ .has_core = false
++ },
++ {
++ .filename = "hailo/hailo10h/u-boot-tfa.itb",
++ .address = 0x86000000,
++ .max_size = 0x1000000,
++ .is_mandatory = true,
++ .has_header = false,
++ .has_core = false
++ },
++ {
++ .filename = NULL,
++ .address = 0x00,
++ .max_size = 0x00,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ },
++};
++
++static const struct hailo_file_batch hailo8_files_stg1[] = {
++ {
++ .filename = "hailo/hailo8_fw.4.19.0.bin",
++ .address = 0x20000,
++ .max_size = 0x50000,
++ .is_mandatory = true,
++ .has_header = true,
++ .has_core = true
++ },
++ {
++ .filename = "hailo/hailo8_board_cfg.bin",
++ .address = 0x60001000,
++ .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ },
++ {
++ .filename = "hailo/hailo8_fw_cfg.bin",
++ .address = 0x60001500,
++ .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ },
++ {
++ .filename = NULL,
++ .address = 0x00,
++ .max_size = 0x00,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ }
++};
++
++static const struct hailo_file_batch hailo10h_legacy_files_stg1[] = {
++ {
++ .filename = "hailo/hailo15_fw.bin",
++ .address = 0x20000,
++ .max_size = 0x100000,
++ .is_mandatory = true,
++ .has_header = true,
++ .has_core = true
++ },
++ {
++ .filename = NULL,
++ .address = 0x00,
++ .max_size = 0x00,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ }
++};
++
++static const struct hailo_file_batch pluto_files_stg1[] = {
++ {
++ .filename = "hailo/pluto_fw.bin",
++ .address = 0x20000,
++ .max_size = 0x100000,
++ .is_mandatory = true,
++ .has_header = true,
++ .has_core = true
++ },
++ {
++ .filename = NULL,
++ .address = 0x00,
++ .max_size = 0x00,
++ .is_mandatory = false,
++ .has_header = false,
++ .has_core = false
++ }
++};
++
+ static const struct hailo_board_compatibility compat[HAILO_BOARD_TYPE_COUNT] = {
+ [HAILO_BOARD_TYPE_HAILO8] = {
+ .fw_addresses = {
+ .boot_fw_header = 0xE0030,
+- .boot_fw_trigger = 0xE0980,
+ .boot_key_cert = 0xE0048,
+ .boot_cont_cert = 0xE0390,
+ .app_fw_code_ram_base = 0x60000,
+@@ -155,22 +257,16 @@ static const struct hailo_board_compatib
+ .raise_ready_offset = 0x1684,
+ .boot_status = 0xe0000,
+ },
+- .fw_filename = "hailo/hailo8_fw.bin",
+- .board_cfg = {
+- .filename = "hailo/hailo8_board_cfg.bin",
+- .address = 0x60001000,
+- .max_size = PCIE_HAILO8_BOARD_CFG_MAX_SIZE,
+- },
+- .fw_cfg = {
+- .filename = "hailo/hailo8_fw_cfg.bin",
+- .address = 0x60001500,
+- .max_size = PCIE_HAILO8_FW_CFG_MAX_SIZE,
++ .stages = {
++ {
++ .batch = hailo8_files_stg1,
++ .trigger_address = 0xE0980
++ },
+ },
+ },
+ [HAILO_BOARD_TYPE_HAILO10H_LEGACY] = {
+ .fw_addresses = {
+ .boot_fw_header = 0x88000,
+- .boot_fw_trigger = 0x88c98,
+ .boot_key_cert = 0x88018,
+ .boot_cont_cert = 0x886a8,
+ .app_fw_code_ram_base = 0x20000,
+@@ -180,22 +276,16 @@ static const struct hailo_board_compatib
+ .raise_ready_offset = 0x1754,
+ .boot_status = 0x80000,
+ },
+- .fw_filename = "hailo/hailo15_fw.bin",
+- .board_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
+- },
+- .fw_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
++ .stages = {
++ {
++ .batch = hailo10h_legacy_files_stg1,
++ .trigger_address = 0x88c98
++ },
+ },
+ },
+ [HAILO_BOARD_TYPE_HAILO10H] = {
+ .fw_addresses = {
+ .boot_fw_header = 0x88000,
+- .boot_fw_trigger = 0x88c98,
+ .boot_key_cert = 0x88018,
+ .boot_cont_cert = 0x886a8,
+ .app_fw_code_ram_base = 0x20000,
+@@ -205,23 +295,18 @@ static const struct hailo_board_compatib
+ .raise_ready_offset = 0x1754,
+ .boot_status = 0x80000,
+ },
+- .fw_filename = NULL,
+- .board_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
+- },
+- .fw_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
+- },
+ .stages = {
+ {
+ .batch = hailo10h_files_stg1,
++ .trigger_address = 0x88c98
+ },
+ {
+ .batch = hailo10h_files_stg2,
++ .trigger_address = 0x84000000
++ },
++ {
++ .batch = hailo10h_files_stg2_linux_in_emmc,
++ .trigger_address = 0x84000000
+ },
+ },
+ },
+@@ -230,7 +315,6 @@ static const struct hailo_board_compatib
+ [HAILO_BOARD_TYPE_PLUTO] = {
+ .fw_addresses = {
+ .boot_fw_header = 0x88000,
+- .boot_fw_trigger = 0x88c98,
+ .boot_key_cert = 0x88018,
+ .boot_cont_cert = 0x886a8,
+ .app_fw_code_ram_base = 0x20000,
+@@ -241,16 +325,11 @@ static const struct hailo_board_compatib
+ .raise_ready_offset = 0x174c,
+ .boot_status = 0x80000,
+ },
+- .fw_filename = "hailo/pluto_fw.bin",
+- .board_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
+- },
+- .fw_cfg = {
+- .filename = NULL,
+- .address = 0,
+- .max_size = 0,
++ .stages = {
++ {
++ .batch = pluto_files_stg1,
++ .trigger_address = 0x88c98
++ },
+ },
+ }
+ };
+@@ -340,21 +419,6 @@ void hailo_pcie_write_firmware_driver_sh
+ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, fw_access_value);
+ }
+
+-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources,
+- struct hailo_d2h_notification *notification)
+-{
+- struct hailo_resource notification_resource;
+-
+- if (PCIE_D2H_NOTIFICATION_SRAM_OFFSET > resources->fw_access.size) {
+- return -EINVAL;
+- }
+-
+- notification_resource.address = resources->fw_access.address + PCIE_D2H_NOTIFICATION_SRAM_OFFSET,
+- notification_resource.size = sizeof(struct hailo_d2h_notification);
+-
+- return hailo_read_firmware_notification(¬ification_resource, notification);
+-}
+-
+ int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index)
+ {
+ size_t offset = 0;
+@@ -388,7 +452,7 @@ static void write_memory_chunk(struct ha
+ u32 ATR_INDEX = 0;
+ BUG_ON(dest_offset + len > (u32)resources->fw_access.size);
+
+- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)dest, ATR_INDEX);
++ (void)hailo_pcie_configure_atr_table(&resources->config, dest, ATR_INDEX);
+ (void)hailo_resource_write_buffer(&resources->fw_access, dest_offset, len, src);
+ }
+
+@@ -398,13 +462,13 @@ static void read_memory_chunk(
+ u32 ATR_INDEX = 0;
+ BUG_ON(src_offset + len > (u32)resources->fw_access.size);
+
+- (void)hailo_pcie_configure_atr_table(&resources->config, (u64)src, ATR_INDEX);
++ (void)hailo_pcie_configure_atr_table(&resources->config, src, ATR_INDEX);
+ (void)hailo_resource_read_buffer(&resources->fw_access, src_offset, len, dest);
+ }
+
+ // Note: this function modify the device ATR table (that is also used by the firmware for control and vdma).
+ // Use with caution, and restore the original atr if needed.
+-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
++static void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len)
+ {
+ struct hailo_atr_config previous_atr = {0};
+ hailo_ptr_t base_address = (dest & ~ATR_TABLE_SIZE_MASK);
+@@ -417,8 +481,8 @@ void write_memory(struct hailo_pcie_reso
+
+ if (base_address != dest) {
+ // Data is not aligned, write the first chunk
+- chunk_len = min(base_address + ATR_TABLE_SIZE - dest, len);
+- write_memory_chunk(resources, base_address, dest - base_address, src, chunk_len);
++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - dest), len);
++ write_memory_chunk(resources, base_address, (u32)(dest - base_address), src, chunk_len);
+ offset += chunk_len;
+ }
+
+@@ -447,8 +511,8 @@ static void read_memory(struct hailo_pci
+
+ if (base_address != src) {
+ // Data is not aligned, write the first chunk
+- chunk_len = min(base_address + ATR_TABLE_SIZE - src, len);
+- read_memory_chunk(resources, base_address, src - base_address, dest, chunk_len);
++ chunk_len = min((u32)(base_address + ATR_TABLE_SIZE - src), len);
++ read_memory_chunk(resources, base_address, (u32)(src - base_address), dest, chunk_len);
+ offset += chunk_len;
+ }
+
+@@ -463,12 +527,12 @@ static void read_memory(struct hailo_pci
+ }
+
+ static void hailo_write_app_firmware(struct hailo_pcie_resources *resources, firmware_header_t *fw_header,
+- secure_boot_certificate_t *fw_cert)
++ secure_boot_certificate_header_t *fw_cert)
+ {
+ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+- void *fw_code = (void*)((u8*)fw_header + sizeof(firmware_header_t));
+- void *key_data = &fw_cert->certificates_data[0];
+- void *content_data = &fw_cert->certificates_data[fw_cert->key_size];
++ u8 *fw_code = ((u8*)fw_header + sizeof(firmware_header_t));
++ u8 *key_data = ((u8*)fw_cert + sizeof(secure_boot_certificate_header_t));
++ u8 *content_data = key_data + fw_cert->key_size;
+
+ write_memory(resources, fw_addresses->boot_fw_header, fw_header, sizeof(firmware_header_t));
+
+@@ -487,13 +551,11 @@ static void hailo_write_core_firmware(st
+ write_memory(resources, fw_addresses->core_fw_header, fw_header, sizeof(firmware_header_t));
+ }
+
+-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources)
++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address)
+ {
+- const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+ u32 pcie_finished = 1;
+
+- write_memory(resources, fw_addresses->boot_fw_trigger,
+- (void*)&pcie_finished, sizeof(pcie_finished));
++ write_memory(resources, address, (void*)&pcie_finished, sizeof(pcie_finished));
+ }
+
+ u32 hailo_get_boot_status(struct hailo_pcie_resources *resources)
+@@ -501,8 +563,7 @@ u32 hailo_get_boot_status(struct hailo_p
+ u32 boot_status = 0;
+ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+
+- read_memory(resources, fw_addresses->boot_status,
+- &boot_status, sizeof(boot_status));
++ read_memory(resources, fw_addresses->boot_status, &boot_status, sizeof(boot_status));
+
+ return boot_status;
+ }
+@@ -517,11 +578,11 @@ u32 hailo_get_boot_status(struct hailo_p
+ */
+ static int FW_VALIDATION__validate_fw_headers(uintptr_t firmware_base_address, size_t firmware_size,
+ firmware_header_t **out_app_firmware_header, firmware_header_t **out_core_firmware_header,
+- secure_boot_certificate_t **out_firmware_cert, enum hailo_board_type board_type)
++ secure_boot_certificate_header_t **out_firmware_cert, enum hailo_board_type board_type)
+ {
+ firmware_header_t *app_firmware_header = NULL;
+ firmware_header_t *core_firmware_header = NULL;
+- secure_boot_certificate_t *firmware_cert = NULL;
++ secure_boot_certificate_header_t *firmware_cert = NULL;
+ int err = -EINVAL;
+ u32 consumed_firmware_offset = 0;
+
+@@ -571,25 +632,25 @@ exit:
+ return err;
+ }
+
+-static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *files_batch, struct device *dev)
++static int write_single_file(struct hailo_pcie_resources *resources, const struct hailo_file_batch *file_info, struct device *dev)
+ {
+ const struct firmware *firmware = NULL;
+ firmware_header_t *app_firmware_header = NULL;
+- secure_boot_certificate_t *firmware_cert = NULL;
++ secure_boot_certificate_header_t *firmware_cert = NULL;
+ firmware_header_t *core_firmware_header = NULL;
+ int err = 0;
+
+- err = request_firmware_direct(&firmware, files_batch->filename, dev);
++ err = request_firmware_direct(&firmware, file_info->filename, dev);
+ if (err < 0) {
+ return err;
+ }
+
+- if (firmware->size > files_batch->max_size) {
++ if (firmware->size > file_info->max_size) {
+ release_firmware(firmware);
+ return -EFBIG;
+ }
+
+- if (files_batch->has_header) {
++ if (file_info->has_header) {
+ err = FW_VALIDATION__validate_fw_headers((uintptr_t)firmware->data, firmware->size,
+ &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type);
+ if (err < 0) {
+@@ -598,8 +659,11 @@ static int write_single_file(struct hail
+ }
+
+ hailo_write_app_firmware(resources, app_firmware_header, firmware_cert);
++ if (file_info->has_core) {
++ hailo_write_core_firmware(resources, core_firmware_header);
++ }
+ } else {
+- write_memory(resources, files_batch->address, (void*)firmware->data, firmware->size);
++ write_memory(resources, file_info->address, (void*)firmware->data, firmware->size);
+ }
+
+ release_firmware(firmware);
+@@ -632,31 +696,13 @@ int hailo_pcie_write_firmware_batch(stru
+ dev_notice(dev, "File %s written successfully\n", files_batch[file_index].filename);
+ }
+
+- return 0;
+-}
+-
+-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size)
+-{
+- firmware_header_t *app_firmware_header = NULL;
+- secure_boot_certificate_t *firmware_cert = NULL;
+- firmware_header_t *core_firmware_header = NULL;
+-
+- int err = FW_VALIDATION__validate_fw_headers((uintptr_t)fw_data, fw_size,
+- &app_firmware_header, &core_firmware_header, &firmware_cert, resources->board_type);
+- if (err < 0) {
+- return err;
+- }
+-
+- hailo_write_app_firmware(resources, app_firmware_header, firmware_cert);
+- hailo_write_core_firmware(resources, core_firmware_header);
+-
+- hailo_trigger_firmware_boot(resources);
++ hailo_trigger_firmware_boot(resources, compat[resources->board_type].stages[stage].trigger_address);
+
+ return 0;
+ }
+
+ // TODO: HRT-14147 - remove this function
+-bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources)
++static bool hailo_pcie_is_device_ready_for_boot(struct hailo_pcie_resources *resources)
+ {
+ return hailo_get_boot_status(resources) == BOOT_STATUS_UNINITIALIZED;
+ }
+@@ -691,32 +737,6 @@ bool hailo_pcie_wait_for_firmware(struct
+ return false;
+ }
+
+-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data,
+- const size_t config_size, const struct hailo_config_constants *config_consts)
+-{
+- if (config_size > config_consts->max_size) {
+- return -EINVAL;
+- }
+-
+- write_memory(resources, config_consts->address, config_data, (u32)config_size);
+- return 0;
+-}
+-
+-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type) {
+- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+- return &compat[board_type].board_cfg;
+-}
+-
+-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type) {
+- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+- return &compat[board_type].fw_cfg;
+-}
+-
+-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type) {
+- BUG_ON(board_type >= HAILO_BOARD_TYPE_COUNT || board_type < 0);
+- return compat[board_type].fw_filename;
+-}
+-
+ void hailo_pcie_update_channel_interrupts_mask(struct hailo_pcie_resources* resources, u32 channels_bitmap)
+ {
+ size_t i = 0;
+@@ -745,7 +765,7 @@ void hailo_pcie_enable_interrupts(struct
+ hailo_resource_write32(&resources->config, BCS_SOURCE_INTERRUPT_PER_CHANNEL, 0xFFFFFFFF);
+
+ mask |= (BCS_ISTATUS_HOST_FW_IRQ_CONTROL_MASK | BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION |
+- BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED);
++ BCS_ISTATUS_HOST_DRIVER_DOWN | BCS_ISTATUS_SOC_CONNECT_ACCEPTED | BCS_ISTATUS_SOC_CLOSED_IRQ);
+ hailo_resource_write32(&resources->config, BSC_IMASK_HOST, mask);
+ }
+
+@@ -754,45 +774,15 @@ void hailo_pcie_disable_interrupts(struc
+ hailo_resource_write32(&resources->config, BSC_IMASK_HOST, 0);
+ }
+
+-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params)
+-{
+- long err = 0;
+- struct hailo_resource log_resource = {resources->fw_access.address, DEBUG_BUFFER_TOTAL_SIZE};
+-
+- if (HAILO_CPU_ID_CPU0 == params->cpu_id) {
+- log_resource.address += PCIE_APP_CPU_DEBUG_OFFSET;
+- } else if (HAILO_CPU_ID_CPU1 == params->cpu_id) {
+- log_resource.address += PCIE_CORE_CPU_DEBUG_OFFSET;
+- } else {
+- return -EINVAL;
+- }
+-
+- if (0 == params->buffer_size) {
+- params->read_bytes = 0;
+- return 0;
+- }
+-
+- err = hailo_read_firmware_log(&log_resource, params);
+- if (0 != err) {
+- return err;
+- }
+-
+- return 0;
+-}
+-
+ static int direct_memory_transfer(struct hailo_pcie_resources *resources,
+ struct hailo_memory_transfer_params *params)
+ {
+- if (params->address > U32_MAX) {
+- return -EFAULT;
+- }
+-
+ switch (params->transfer_direction) {
+ case TRANSFER_READ:
+- read_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
++ read_memory(resources, params->address, params->buffer, (u32)params->count);
+ break;
+ case TRANSFER_WRITE:
+- write_memory(resources, (u32)params->address, params->buffer, (u32)params->count);
++ write_memory(resources, params->address, params->buffer, (u32)params->count);
+ break;
+ default:
+ return -EINVAL;
+@@ -845,16 +835,18 @@ int hailo_set_device_type(struct hailo_p
+ return 0;
+ }
+
+-// On PCIe, just return the address
+-static u64 encode_dma_address(dma_addr_t dma_address, u8 channel_id)
++// On PCIe, just return the start address
++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id)
+ {
+ (void)channel_id;
+- return (u64)dma_address;
++ (void)dma_address_end;
++ (void)step;
++ return (u64)dma_address_start;
+ }
+
+ struct hailo_vdma_hw hailo_pcie_vdma_hw = {
+ .hw_ops = {
+- .encode_desc_dma_address = encode_dma_address
++ .encode_desc_dma_address_range = hailo_pcie_encode_desc_dma_address_range,
+ },
+ .ddr_data_id = HAILO_PCIE_HOST_DMA_DATA_ID,
+ .device_interrupts_bitmask = HAILO_PCIE_DMA_DEVICE_INTERRUPTS_BITMASK,
+@@ -862,11 +854,19 @@ struct hailo_vdma_hw hailo_pcie_vdma_hw
+ .src_channels_bitmask = HAILO_PCIE_DMA_SRC_CHANNELS_BITMASK,
+ };
+
+-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources)
++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources,
++ const struct hailo_pcie_soc_request *request)
+ {
+ const struct hailo_fw_addresses *fw_addresses = &(compat[resources->board_type].fw_addresses);
+- const u32 soc_connect_value = FW_ACCESS_SOC_CONNECT_MASK;
++ BUILD_BUG_ON_MSG((sizeof(*request) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes");
+
+- // Write shutdown flag to FW
+- hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, soc_connect_value);
+-}
+\ No newline at end of file
++ hailo_resource_write_buffer(&resources->fw_access, 0, sizeof(*request), (void*)request);
++ hailo_resource_write32(&resources->fw_access, fw_addresses->raise_ready_offset, FW_ACCESS_SOC_CONTROL_MASK);
++}
++
++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources,
++ struct hailo_pcie_soc_response *response)
++{
++ BUILD_BUG_ON_MSG((sizeof(*response) % sizeof(u32)) != 0, "Request must be a multiple of 4 bytes");
++ hailo_resource_read_buffer(&resources->fw_access, 0, sizeof(*response), response);
++}
+--- a/drivers/media/pci/hailo/common/pcie_common.h
++++ b/drivers/media/pci/hailo/common/pcie_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_COMMON_PCIE_COMMON_H_
+@@ -12,6 +12,7 @@
+ #include "fw_operation.h"
+ #include "utils.h"
+ #include "vdma_common.h"
++#include "soc_structs.h"
+
+ #include <linux/types.h>
+ #include <linux/firmware.h>
+@@ -21,6 +22,7 @@
+ #define BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION (0x02000000)
+ #define BCS_ISTATUS_HOST_DRIVER_DOWN (0x08000000)
+ #define BCS_ISTATUS_SOC_CONNECT_ACCEPTED (0x10000000)
++#define BCS_ISTATUS_SOC_CLOSED_IRQ (0x20000000)
+ #define BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK (0x000000FF)
+ #define BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK (0x0000FF00)
+
+@@ -42,7 +44,7 @@
+ #define PCI_DEVICE_ID_HAILO_HAILO15 0x45C4
+ #define PCI_DEVICE_ID_HAILO_PLUTO 0x43a2
+
+-typedef u32 hailo_ptr_t;
++typedef u64 hailo_ptr_t;
+
+ struct hailo_pcie_resources {
+ struct hailo_resource config; // BAR0
+@@ -63,7 +65,8 @@ struct hailo_atr_config {
+ enum loading_stages {
+ FIRST_STAGE = 0,
+ SECOND_STAGE = 1,
+- MAX_LOADING_STAGES = 2
++ SECOND_STAGE_LINUX_IN_EMMC = 2,
++ MAX_LOADING_STAGES = 3
+ };
+
+ enum hailo_pcie_interrupt_masks {
+@@ -71,6 +74,7 @@ enum hailo_pcie_interrupt_masks {
+ FW_NOTIFICATION = BCS_ISTATUS_HOST_FW_IRQ_NOTIFICATION,
+ DRIVER_DOWN = BCS_ISTATUS_HOST_DRIVER_DOWN,
+ SOC_CONNECT_ACCEPTED = BCS_ISTATUS_SOC_CONNECT_ACCEPTED,
++ SOC_CLOSED_IRQ = BCS_ISTATUS_SOC_CLOSED_IRQ,
+ VDMA_SRC_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_SRC_IRQ_MASK,
+ VDMA_DEST_IRQ_MASK = BCS_ISTATUS_HOST_VDMA_DEST_IRQ_MASK
+ };
+@@ -80,18 +84,13 @@ struct hailo_pcie_interrupt_source {
+ u32 vdma_channels_bitmap;
+ };
+
+-struct hailo_config_constants {
+- const char *filename;
+- u32 address;
+- size_t max_size;
+-};
+-
+ struct hailo_file_batch {
+ const char *filename;
+ u32 address;
+ size_t max_size;
+ bool is_mandatory;
+ bool has_header;
++ bool has_core;
+ };
+
+ // TODO: HRT-6144 - Align Windows/Linux to QNX
+@@ -130,27 +129,15 @@ void hailo_pcie_disable_interrupts(struc
+ int hailo_pcie_write_firmware_control(struct hailo_pcie_resources *resources, const struct hailo_fw_control *command);
+ int hailo_pcie_read_firmware_control(struct hailo_pcie_resources *resources, struct hailo_fw_control *command);
+
+-int hailo_pcie_write_firmware(struct hailo_pcie_resources *resources, const void *fw_data, size_t fw_size);
+ int hailo_pcie_write_firmware_batch(struct device *dev, struct hailo_pcie_resources *resources, u32 stage);
+ bool hailo_pcie_is_firmware_loaded(struct hailo_pcie_resources *resources);
+ bool hailo_pcie_wait_for_firmware(struct hailo_pcie_resources *resources);
+
+-int hailo_pcie_read_firmware_notification(struct hailo_pcie_resources *resources,
+- struct hailo_d2h_notification *notification);
+-
+-int hailo_pcie_write_config_common(struct hailo_pcie_resources *resources, const void* config_data,
+- const size_t config_size, const struct hailo_config_constants *config_consts);
+-const struct hailo_config_constants* hailo_pcie_get_board_config_constants(const enum hailo_board_type board_type);
+-const struct hailo_config_constants* hailo_pcie_get_user_config_constants(const enum hailo_board_type board_type);
+-const char* hailo_pcie_get_fw_filename(const enum hailo_board_type board_type);
+-
+-long hailo_pcie_read_firmware_log(struct hailo_pcie_resources *resources, struct hailo_read_log_params *params);
+ int hailo_pcie_memory_transfer(struct hailo_pcie_resources *resources, struct hailo_memory_transfer_params *params);
+
+ bool hailo_pcie_is_device_connected(struct hailo_pcie_resources *resources);
+ void hailo_pcie_write_firmware_driver_shutdown(struct hailo_pcie_resources *resources);
+-void write_memory(struct hailo_pcie_resources *resources, hailo_ptr_t dest, const void *src, u32 len);
+-void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources);
++void hailo_trigger_firmware_boot(struct hailo_pcie_resources *resources, u32 address);
+
+ int hailo_set_device_type(struct hailo_pcie_resources *resources);
+
+@@ -159,7 +146,12 @@ u32 hailo_get_boot_status(struct hailo_p
+ int hailo_pcie_configure_atr_table(struct hailo_resource *bridge_config, u64 trsl_addr, u32 atr_index);
+ void hailo_pcie_read_atr_table(struct hailo_resource *bridge_config, struct hailo_atr_config *atr, u32 atr_index);
+
+-void hailo_soc_write_soc_connect(struct hailo_pcie_resources *resources);
++u64 hailo_pcie_encode_desc_dma_address_range(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id);
++
++void hailo_pcie_soc_write_request(struct hailo_pcie_resources *resources,
++ const struct hailo_pcie_soc_request *request);
++void hailo_pcie_soc_read_response(struct hailo_pcie_resources *resources,
++ struct hailo_pcie_soc_response *response);
+
+ #ifdef __cplusplus
+ }
+--- /dev/null
++++ b/drivers/media/pci/hailo/common/soc_structs.h
+@@ -0,0 +1,79 @@
++// SPDX-License-Identifier: MIT
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * Contains definitions for pcie soc to pcie ep communication
++ */
++
++#ifndef __HAILO_COMMON_SOC_STRUCTS__
++#define __HAILO_COMMON_SOC_STRUCTS__
++
++#include <linux/types.h>
++
++#pragma pack(push, 1)
++
++struct hailo_pcie_soc_connect_request {
++ u16 port;
++};
++
++struct hailo_pcie_soc_connect_response {
++ u8 input_channel_index;
++ u8 output_channel_index;
++};
++
++
++struct hailo_pcie_soc_close_request {
++ u32 channels_bitmap;
++};
++
++struct hailo_pcie_soc_close_response {
++ u8 reserved;
++};
++
++enum hailo_pcie_soc_control_code {
++ // Start from big initial value to ensure the right code was used (using 0
++ // as initiale may cause confusion if the code was not set correctly).
++ HAILO_PCIE_SOC_CONTROL_CODE_CONNECT = 0x100,
++ HAILO_PCIE_SOC_CONTROL_CODE_CLOSE,
++ HAILO_PCIE_SOC_CONTROL_CODE_INVALID,
++};
++
++#define HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES (16)
++#define HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES (16)
++
++// IRQ to signal the PCIe that the EP was closed/released
++#define PCI_EP_SOC_CLOSED_IRQ (0x00000020)
++#define PCI_EP_SOC_CONNECT_RESPONSE (0x00000010)
++
++struct hailo_pcie_soc_request {
++ u32 control_code;
++ union {
++ struct hailo_pcie_soc_connect_request connect;
++ struct hailo_pcie_soc_close_request close;
++ u8 pad[HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES];
++ };
++};
++
++struct hailo_pcie_soc_response {
++ u32 control_code;
++ s32 status;
++ union {
++ struct hailo_pcie_soc_connect_response connect;
++ struct hailo_pcie_soc_close_response close;
++ u8 pad[HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES];
++ };
++};
++
++#pragma pack(pop)
++
++// Compile time validate function. Don't need to call it.
++static inline void __validate_soc_struct_sizes(void)
++{
++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_request) !=
++ sizeof(u32) + HAILO_PCIE_SOC_MAX_REQUEST_SIZE_BYTES, "Invalid request size");
++ BUILD_BUG_ON_MSG(sizeof(struct hailo_pcie_soc_response) !=
++ sizeof(u32) + sizeof(s32) + HAILO_PCIE_SOC_MAX_RESPONSE_SIZE_BYTES, "Invalid response size");
++}
++
++#endif /* __HAILO_COMMON_SOC_STRUCTS__ */
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/common/utils.h
++++ b/drivers/media/pci/hailo/common/utils.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_DRIVER_UTILS_H_
+@@ -8,6 +8,11 @@
+
+ #include <linux/bitops.h>
+
++#define DWORD_SIZE (4)
++#define WORD_SIZE (2)
++#define BYTE_SIZE (1)
++#define BITS_IN_BYTE (8)
++
+ #define hailo_clear_bit(bit, pval) { *(pval) &= ~(1 << bit); }
+ #define hailo_test_bit(pos,var_addr) ((*var_addr) & (1<<(pos)))
+
+@@ -50,6 +55,22 @@ static inline uint8_t ceil_log2(uint32_t
+ return result;
+ }
+
++// Gets the nearest power of 2 >= value, for any value <= MAX_POWER_OF_2_VALUE. Otherwise POWER_OF_2_ERROR is returned.
++#define MAX_POWER_OF_2_VALUE (0x80000000)
++#define POWER_OF_2_ERROR ((uint32_t)-1)
++static inline uint32_t get_nearest_powerof_2(uint32_t value)
++{
++ uint32_t power_of_2 = 1;
++ if (value > MAX_POWER_OF_2_VALUE) {
++ return POWER_OF_2_ERROR;
++ }
++
++ while (value > power_of_2) {
++ power_of_2 <<= 1;
++ }
++ return power_of_2;
++}
++
+ #ifndef DIV_ROUND_UP
+ #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+ #endif
+--- a/drivers/media/pci/hailo/common/vdma_common.c
++++ b/drivers/media/pci/hailo/common/vdma_common.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "vdma_common.h"
+@@ -62,11 +62,6 @@
+ #define VDMA_CHANNEL_NUM_PROCESSED_MASK ((1 << VDMA_CHANNEL_NUM_PROCESSED_WIDTH) - 1)
+ #define VDMA_CHANNEL_NUM_ONGOING_MASK VDMA_CHANNEL_NUM_PROCESSED_MASK
+
+-#define DWORD_SIZE (4)
+-#define WORD_SIZE (2)
+-#define BYTE_SIZE (1)
+-#define BITS_IN_BYTE (8)
+-
+ #define TIMESTAMPS_CIRC_SPACE(timestamp_list) \
+ CIRC_SPACE((timestamp_list).head, (timestamp_list).tail, CHANNEL_IRQ_TIMESTAMPS_SIZE)
+ #define TIMESTAMPS_CIRC_CNT(timestamp_list) \
+@@ -150,7 +145,7 @@ static bool validate_last_desc_status(st
+ return true;
+ }
+
+-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
++static void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
+ u8 data_id)
+ {
+ descriptor->PageSize_DescControl = (u32)((page_size << DESCRIPTOR_PAGE_SIZE_SHIFT) +
+@@ -174,33 +169,45 @@ static int program_descriptors_in_chunk(
+ u32 max_desc_index,
+ u8 channel_id)
+ {
+- const u32 desc_per_chunk = DIV_ROUND_UP(chunk_size, desc_list->desc_page_size);
++ const u16 page_size = desc_list->desc_page_size;
++ const u8 ddr_data_id = vdma_hw->ddr_data_id;
++ const u32 descs_to_program = DIV_ROUND_UP(chunk_size, page_size);
++ const u32 starting_desc_index = desc_index;
++ const u32 residue_size = chunk_size % page_size;
+ struct hailo_vdma_descriptor *dma_desc = NULL;
+- u16 size_to_program = 0;
+- u32 index = 0;
+ u64 encoded_addr = 0;
+
+- for (index = 0; index < desc_per_chunk; index++) {
+- if (desc_index > max_desc_index) {
+- return -ERANGE;
+- }
+-
+- encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address(chunk_addr, channel_id);
+- if (INVALID_VDMA_ADDRESS == encoded_addr) {
+- return -EFAULT;
+- }
++ if (descs_to_program == 0) {
++ // Nothing to program
++ return 0;
++ }
+
+- dma_desc = &desc_list->desc_list[desc_index % desc_list->desc_count];
+- size_to_program = chunk_size > desc_list->desc_page_size ?
+- desc_list->desc_page_size : (u16)chunk_size;
+- hailo_vdma_program_descriptor(dma_desc, encoded_addr, size_to_program, vdma_hw->ddr_data_id);
++ // We iterate through descriptors [desc_index, desc_index + descs_to_program)
++ if (desc_index + descs_to_program > max_desc_index + 1) {
++ return -ERANGE;
++ }
+
+- chunk_addr += size_to_program;
+- chunk_size -= size_to_program;
+- desc_index++;
++ encoded_addr = vdma_hw->hw_ops.encode_desc_dma_address_range(chunk_addr, chunk_addr + chunk_size, page_size, channel_id);
++ if (INVALID_VDMA_ADDRESS == encoded_addr) {
++ return -EFAULT;
+ }
+
+- return (int)desc_per_chunk;
++ // Program all descriptors except the last one
++ for (desc_index = starting_desc_index; desc_index < starting_desc_index + descs_to_program - 1; desc_index++) {
++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation.
++ hailo_vdma_program_descriptor(
++ &desc_list->desc_list[desc_index & desc_list->desc_count_mask],
++ encoded_addr, page_size, ddr_data_id);
++ encoded_addr += page_size;
++ }
++
++ // Handle the last descriptor outside of the loop
++ // 'desc_index & desc_list_len_mask' is used instead of modulo; see hailo_vdma_descriptors_list documentation.
++ dma_desc = &desc_list->desc_list[desc_index & desc_list->desc_count_mask];
++ hailo_vdma_program_descriptor(dma_desc, encoded_addr,
++ (residue_size == 0) ? page_size : (u16)residue_size, ddr_data_id);
++
++ return (int)descs_to_program;
+ }
+
+ static unsigned long get_interrupts_bitmask(struct hailo_vdma_hw *vdma_hw,
+@@ -236,11 +243,11 @@ static int bind_and_program_descriptors_
+ {
+ const u8 channel_id = get_channel_id(channel_index);
+ int desc_programmed = 0;
++ int descs_programmed_in_chunk = 0;
+ u32 max_desc_index = 0;
+ u32 chunk_size = 0;
+ struct scatterlist *sg_entry = NULL;
+ unsigned int i = 0;
+- int ret = 0;
+ size_t buffer_current_offset = 0;
+ dma_addr_t chunk_start_addr = 0;
+ u32 program_size = buffer->size;
+@@ -272,14 +279,14 @@ static int bind_and_program_descriptors_
+ (u32)(sg_dma_len(sg_entry));
+ chunk_size = min((u32)program_size, chunk_size);
+
+- ret = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list,
++ descs_programmed_in_chunk = program_descriptors_in_chunk(vdma_hw, chunk_start_addr, chunk_size, desc_list,
+ starting_desc, max_desc_index, channel_id);
+- if (ret < 0) {
+- return ret;
++ if (descs_programmed_in_chunk < 0) {
++ return descs_programmed_in_chunk;
+ }
+
+- desc_programmed += ret;
+- starting_desc = starting_desc + ret;
++ desc_programmed += descs_programmed_in_chunk;
++ starting_desc = starting_desc + descs_programmed_in_chunk;
+ program_size -= chunk_size;
+ buffer_current_offset += sg_dma_len(sg_entry);
+ }
+@@ -583,21 +590,23 @@ void hailo_vdma_engine_disable_channels(
+ engine->enabled_channels &= ~bitmap;
+
+ for_each_vdma_channel(engine, channel, channel_index) {
+- channel_state_init(&channel->state);
++ if (hailo_test_bit(channel_index, &bitmap)) {
++ channel_state_init(&channel->state);
+
+- while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) {
+- struct hailo_ongoing_transfer transfer;
+- ongoing_transfer_pop(channel, &transfer);
+-
+- if (channel->last_desc_list == NULL) {
+- pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index);
+- continue;
++ while (ONGOING_TRANSFERS_CIRC_CNT(channel->ongoing_transfers) > 0) {
++ struct hailo_ongoing_transfer transfer;
++ ongoing_transfer_pop(channel, &transfer);
++
++ if (channel->last_desc_list == NULL) {
++ pr_err("Channel %d has ongoing transfers but no desc list\n", channel->index);
++ continue;
++ }
++
++ clear_dirty_descs(channel, &transfer);
+ }
+
+- clear_dirty_descs(channel, &transfer);
++ channel->last_desc_list = NULL;
+ }
+-
+- channel->last_desc_list = NULL;
+ }
+ }
+
+--- a/drivers/media/pci/hailo/common/vdma_common.h
++++ b/drivers/media/pci/hailo/common/vdma_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: MIT
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_COMMON_VDMA_COMMON_H_
+@@ -30,7 +30,13 @@ struct hailo_vdma_descriptor {
+
+ struct hailo_vdma_descriptors_list {
+ struct hailo_vdma_descriptor *desc_list;
+- u32 desc_count; // Must be power of 2 if is_circular is set.
++ // Must be power of 2 if is_circular is set.
++ u32 desc_count;
++ // The nearest power of 2 to desc_count (including desc_count), minus 1.
++ // * If the list is circular, then 'index & desc_count_mask' can be used instead of modulo.
++ // * Otherwise, we can't wrap around the list anyway. However, for any index < desc_count, 'index & desc_count_mask'
++ // will return the same value.
++ u32 desc_count_mask;
+ u16 desc_page_size;
+ bool is_circular;
+ };
+@@ -113,9 +119,10 @@ struct hailo_vdma_engine {
+ };
+
+ struct hailo_vdma_hw_ops {
+- // Accepts some dma_addr_t mapped to the device and encodes it using
+- // hw specific encode. returns INVALID_VDMA_ADDRESS on failure.
+- u64 (*encode_desc_dma_address)(dma_addr_t dma_address, u8 channel_id);
++ // Accepts start, end and step of an address range (of type dma_addr_t).
++ // Returns the encoded base address or INVALID_VDMA_ADDRESS if the range/step is invalid.
++ // All addresses in the range of [returned_addr, returned_addr + step, returned_addr + 2*step, ..., dma_address_end) are valid.
++ u64 (*encode_desc_dma_address_range)(dma_addr_t dma_address_start, dma_addr_t dma_address_end, u32 step, u8 channel_id);
+ };
+
+ struct hailo_vdma_hw {
+@@ -136,12 +143,9 @@ struct hailo_vdma_hw {
+ for (index = 0, element = &array[index]; index < size; index++, element = &array[index])
+
+ #define for_each_vdma_channel(engine, channel, channel_index) \
+- _for_each_element_array(engine->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \
++ _for_each_element_array((engine)->channels, MAX_VDMA_CHANNELS_PER_ENGINE, \
+ channel, channel_index)
+
+-void hailo_vdma_program_descriptor(struct hailo_vdma_descriptor *descriptor, u64 dma_address, size_t page_size,
+- u8 data_id);
+-
+ /**
+ * Program the given descriptors list to map the given buffer.
+ *
+--- a/drivers/media/pci/hailo/src/fops.c
++++ b/drivers/media/pci/hailo/src/fops.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include <linux/version.h>
+@@ -19,14 +19,14 @@
+ #include <linux/sched/signal.h>
+ #endif
+
+-#include "utils.h"
+ #include "fops.h"
+ #include "vdma_common.h"
+ #include "utils/logs.h"
+ #include "vdma/memory.h"
+ #include "vdma/ioctl.h"
+ #include "utils/compact.h"
+-#include "pci_soc_ioctl.h"
++#include "nnc.h"
++#include "soc.h"
+
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 )
+@@ -48,13 +48,6 @@
+ // On pcie driver there is only one dma engine
+ #define DEFAULT_VDMA_ENGINE_INDEX (0)
+
+-#if !defined(HAILO_EMULATOR)
+-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5)
+-#else /* !defined(HAILO_EMULATOR) */
+-#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000)
+-#endif /* !defined(HAILO_EMULATOR) */
+-
+-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp);
+
+ static struct hailo_file_context *create_file_context(struct hailo_pcie_board *board, struct file *filp)
+ {
+@@ -124,7 +117,7 @@ int hailo_pcie_fops_open(struct inode *i
+
+ previous_power_state = pBoard->pDev->current_state;
+ if (PCI_D0 != previous_power_state) {
+- hailo_info(pBoard, "Waking up board");
++ hailo_info(pBoard, "Waking up board change state from %d to PCI_D0\n", previous_power_state);
+ err = pci_set_power_state(pBoard->pDev, PCI_D0);
+ if (err < 0) {
+ hailo_err(pBoard, "Failed waking up board %d", err);
+@@ -148,7 +141,11 @@ int hailo_pcie_fops_open(struct inode *i
+ interrupts_enabled_by_filp = true;
+ }
+
+- err = hailo_add_notification_wait(pBoard, filp);
++ if (pBoard->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++ err = hailo_nnc_file_context_init(pBoard, context);
++ } else {
++ err = hailo_soc_file_context_init(pBoard, context);
++ }
+ if (err < 0) {
+ goto l_release_irq;
+ }
+@@ -166,6 +163,7 @@ l_release_irq:
+
+ l_revert_power_state:
+ if (pBoard->pDev->current_state != previous_power_state) {
++ hailo_info(pBoard, "Power changing state from %d to %d\n", previous_power_state, pBoard->pDev->current_state);
+ if (pci_set_power_state(pBoard->pDev, previous_power_state) < 0) {
+ hailo_err(pBoard, "Failed setting power state back to %d\n", (int)previous_power_state);
+ }
+@@ -180,34 +178,6 @@ l_exit:
+ return err;
+ }
+
+-int hailo_pcie_driver_down(struct hailo_pcie_board *board)
+-{
+- long completion_result = 0;
+- int err = 0;
+-
+- reinit_completion(&board->driver_down.reset_completed);
+-
+- hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
+-
+- // Wait for response
+- completion_result =
+- wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS));
+- if (completion_result <= 0) {
+- if (0 == completion_result) {
+- hailo_err(board, "hailo_pcie_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS);
+- err = -ETIMEDOUT;
+- } else {
+- hailo_info(board, "hailo_pcie_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n",
+- completion_result);
+- err = completion_result;
+- }
+- goto l_exit;
+- }
+-
+-l_exit:
+- return err;
+-}
+-
+ int hailo_pcie_fops_release(struct inode *inode, struct file *filp)
+ {
+ struct hailo_pcie_board *board = (struct hailo_pcie_board *)filp->private_data;
+@@ -234,12 +204,10 @@ int hailo_pcie_fops_release(struct inode
+ hailo_err(board, "Invalid file context\n");
+ }
+
+- hailo_pcie_clear_notification_wait_list(board, filp);
+-
+- if (filp == board->vdma.used_by_filp) {
+- if (hailo_pcie_driver_down(board)) {
+- hailo_err(board, "Failed sending FW shutdown event");
+- }
++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++ hailo_nnc_file_context_finalize(board, context);
++ } else {
++ hailo_soc_file_context_finalize(board, context);
+ }
+
+ hailo_vdma_file_context_finalize(&context->vdma_context, &board->vdma, filp);
+@@ -250,6 +218,7 @@ int hailo_pcie_fops_release(struct inode
+ hailo_disable_interrupts(board);
+
+ if (power_mode_enabled()) {
++ hailo_info(board, "Power change state to PCI_D3hot\n");
+ if (board->pDev && pci_set_power_state(board->pDev, PCI_D3hot) < 0) {
+ hailo_err(board, "Failed setting power state to D3hot");
+ }
+@@ -301,44 +270,23 @@ static long hailo_memory_transfer_ioctl(
+ return err;
+ }
+
+-static long hailo_read_log_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg)
+-{
+- long err = 0;
+- struct hailo_read_log_params params;
+-
+- if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) {
+- hailo_err(pBoard, "HAILO_READ_LOG, copy_from_user fail\n");
+- return -ENOMEM;
+- }
+-
+- if (0 > (err = hailo_pcie_read_firmware_log(&pBoard->pcie_resources, ¶ms))) {
+- hailo_err(pBoard, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
+- return err;
+- }
+-
+- if (copy_to_user((void*)arg, ¶ms, sizeof(params))) {
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+ static void firmware_notification_irq_handler(struct hailo_pcie_board *board)
+ {
+ struct hailo_notification_wait *notif_wait_cursor = NULL;
+ int err = 0;
+ unsigned long irq_saved_flags = 0;
+
+- spin_lock_irqsave(&board->notification_read_spinlock, irq_saved_flags);
+- err = hailo_pcie_read_firmware_notification(&board->pcie_resources, &board->notification_cache);
+- spin_unlock_irqrestore(&board->notification_read_spinlock, irq_saved_flags);
++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags);
++ err = hailo_pcie_read_firmware_notification(&board->pcie_resources.fw_access, &board->nnc.notification_cache);
++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags);
+
+ if (err < 0) {
+ hailo_err(board, "Failed reading firmware notification");
+ }
+ else {
++ // TODO: HRT-14502 move interrupt handling to nnc
+ rcu_read_lock();
+- list_for_each_entry_rcu(notif_wait_cursor, &board->notification_wait_list, notification_wait_list)
++ list_for_each_entry_rcu(notif_wait_cursor, &board->nnc.notification_wait_list, notification_wait_list)
+ {
+ complete(¬if_wait_cursor->notification_completion);
+ }
+@@ -374,7 +322,7 @@ irqreturn_t hailo_irqhandler(int irq, vo
+
+ // wake fw_control if needed
+ if (irq_source.interrupt_bitmask & FW_CONTROL) {
+- complete(&board->fw_control.completion);
++ complete(&board->nnc.fw_control.completion);
+ }
+
+ // wake driver_down if needed
+@@ -392,7 +340,14 @@ irqreturn_t hailo_irqhandler(int irq, vo
+ }
+
+ if (irq_source.interrupt_bitmask & SOC_CONNECT_ACCEPTED) {
+- complete_all(&board->soc_connect_accepted);
++ complete_all(&board->soc.control_resp_ready);
++ }
++
++ if (irq_source.interrupt_bitmask & SOC_CLOSED_IRQ) {
++ hailo_info(board, "hailo_irqhandler - SOC_CLOSED_IRQ\n");
++ // always use bitmap=0xFFFFFFFF - it is ok to wake all interrupts since each handler will check if the stream was aborted or not.
++ hailo_vdma_wakeup_interrupts(&board->vdma, &board->vdma.vdma_engines[DEFAULT_VDMA_ENGINE_INDEX],
++ 0xFFFFFFFF);
+ }
+
+ if (0 != irq_source.vdma_channels_bitmap) {
+@@ -404,170 +359,11 @@ irqreturn_t hailo_irqhandler(int irq, vo
+ return return_value;
+ }
+
+-static long hailo_get_notification_wait_thread(struct hailo_pcie_board *pBoard, struct file *filp,
+- struct hailo_notification_wait **current_waiting_thread)
+-{
+- struct hailo_notification_wait *cursor = NULL;
+- // note: safe to access without rcu because the notification_wait_list is closed only on file release
+- list_for_each_entry(cursor, &pBoard->notification_wait_list, notification_wait_list)
+- {
+- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
+- *current_waiting_thread = cursor;
+- return 0;
+- }
+- }
+-
+- return -EFAULT;
+-}
+-
+-static long hailo_add_notification_wait(struct hailo_pcie_board *board, struct file *filp)
+-{
+- struct hailo_notification_wait *new_notification_wait = NULL;
+- if (!(new_notification_wait = kmalloc(sizeof(*new_notification_wait), GFP_KERNEL))) {
+- hailo_err(board, "Failed to allocate notification wait structure.\n");
+- return -ENOMEM;
+- }
+- new_notification_wait->tgid = current->tgid;
+- new_notification_wait->filp = filp;
+- new_notification_wait->is_disabled = false;
+- init_completion(&new_notification_wait->notification_completion);
+- list_add_rcu(&new_notification_wait->notification_wait_list, &board->notification_wait_list);
+- return 0;
+-}
+-
+-static long hailo_read_notification_ioctl(struct hailo_pcie_board *pBoard, unsigned long arg, struct file *filp,
+- bool* should_up_board_mutex)
+-{
+- long err = 0;
+- struct hailo_notification_wait *current_waiting_thread = NULL;
+- struct hailo_d2h_notification *notification = &pBoard->notification_to_user;
+- unsigned long irq_saved_flags;
+-
+- err = hailo_get_notification_wait_thread(pBoard, filp, ¤t_waiting_thread);
+- if (0 != err) {
+- goto l_exit;
+- }
+- up(&pBoard->mutex);
+-
+- if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) {
+- hailo_info(pBoard,
+- "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n",
+- err, current_waiting_thread->tgid);
+- *should_up_board_mutex = false;
+- goto l_exit;
+- }
+-
+- if (down_interruptible(&pBoard->mutex)) {
+- hailo_info(pBoard, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n");
+- *should_up_board_mutex = false;
+- err = -ERESTARTSYS;
+- goto l_exit;
+- }
+-
+- // Check if was disabled
+- if (current_waiting_thread->is_disabled) {
+- hailo_info(pBoard, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid);
+- err = -EINVAL;
+- goto l_exit;
+- }
+-
+- reinit_completion(¤t_waiting_thread->notification_completion);
+-
+- spin_lock_irqsave(&pBoard->notification_read_spinlock, irq_saved_flags);
+- notification->buffer_len = pBoard->notification_cache.buffer_len;
+- memcpy(notification->buffer, pBoard->notification_cache.buffer, notification->buffer_len);
+- spin_unlock_irqrestore(&pBoard->notification_read_spinlock, irq_saved_flags);
+-
+- if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) {
+- hailo_err(pBoard, "HAILO_READ_NOTIFICATION copy_to_user fail\n");
+- err = -ENOMEM;
+- goto l_exit;
+- }
+-
+-l_exit:
+- return err;
+-}
+-
+-static long hailo_disable_notification(struct hailo_pcie_board *pBoard, struct file *filp)
+-{
+- struct hailo_notification_wait *cursor = NULL;
+-
+- hailo_info(pBoard, "HAILO_DISABLE_NOTIFICATION: disable notification");
+- rcu_read_lock();
+- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) {
+- if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
+- cursor->is_disabled = true;
+- complete(&cursor->notification_completion);
+- break;
+- }
+- }
+- rcu_read_unlock();
+-
+- return 0;
+-}
+-
+-static int hailo_fw_control(struct hailo_pcie_board *pBoard, unsigned long arg, bool* should_up_board_mutex)
+-{
+- struct hailo_fw_control *command = &pBoard->fw_control.command;
+- long completion_result = 0;
+- int err = 0;
+-
+- up(&pBoard->mutex);
+- *should_up_board_mutex = false;
+-
+- if (down_interruptible(&pBoard->fw_control.mutex)) {
+- hailo_info(pBoard, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid);
+- return -ERESTARTSYS;
+- }
+-
+- if (copy_from_user(command, (void __user*)arg, sizeof(*command))) {
+- hailo_err(pBoard, "hailo_fw_control, copy_from_user fail\n");
+- err = -ENOMEM;
+- goto l_exit;
+- }
+-
+- reinit_completion(&pBoard->fw_control.completion);
+-
+- err = hailo_pcie_write_firmware_control(&pBoard->pcie_resources, command);
+- if (err < 0) {
+- hailo_err(pBoard, "Failed writing fw control to pcie\n");
+- goto l_exit;
+- }
+-
+- // Wait for response
+- completion_result = wait_for_completion_interruptible_timeout(&pBoard->fw_control.completion, msecs_to_jiffies(command->timeout_ms));
+- if (completion_result <= 0) {
+- if (0 == completion_result) {
+- hailo_err(pBoard, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms);
+- err = -ETIMEDOUT;
+- } else {
+- hailo_info(pBoard, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result);
+- err = -EINTR;
+- }
+- goto l_exit;
+- }
+-
+- err = hailo_pcie_read_firmware_control(&pBoard->pcie_resources, command);
+- if (err < 0) {
+- hailo_err(pBoard, "Failed reading fw control from pcie\n");
+- goto l_exit;
+- }
+-
+- if (copy_to_user((void __user*)arg, command, sizeof(*command))) {
+- hailo_err(pBoard, "hailo_fw_control, copy_to_user fail\n");
+- err = -ENOMEM;
+- goto l_exit;
+- }
+-
+-l_exit:
+- up(&pBoard->fw_control.mutex);
+- return err;
+-}
+-
+ static long hailo_query_device_properties(struct hailo_pcie_board *board, unsigned long arg)
+ {
+ struct hailo_device_properties props = {
+ .desc_max_page_size = board->desc_max_page_size,
++ .board_type = board->pcie_resources.board_type,
+ .allocation_mode = board->allocation_mode,
+ .dma_type = HAILO_DMA_TYPE_PCIE,
+ .dma_engines_count = board->vdma.vdma_engines_count,
+@@ -618,24 +414,6 @@ static long hailo_general_ioctl(struct h
+ }
+ }
+
+-static long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
+- struct file *filp, bool *should_up_board_mutex)
+-{
+- switch (cmd) {
+- case HAILO_FW_CONTROL:
+- return hailo_fw_control(board, arg, should_up_board_mutex);
+- case HAILO_READ_NOTIFICATION:
+- return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex);
+- case HAILO_DISABLE_NOTIFICATION:
+- return hailo_disable_notification(board, filp);
+- case HAILO_READ_LOG:
+- return hailo_read_log_ioctl(board, arg);
+- default:
+- hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
+- return -ENOTTY;
+- }
+-}
+-
+ long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg)
+ {
+ long err = 0;
+@@ -694,7 +472,7 @@ long hailo_pcie_fops_unlockedioctl(struc
+ hailo_err(board, "Ioctl %d is not supported on this accelerator type\n", _IOC_TYPE(cmd));
+ err = -EINVAL;
+ } else {
+- err = hailo_soc_ioctl(board, &context->vdma_context, &board->vdma, cmd, arg);
++ err = hailo_soc_ioctl(board, context, &board->vdma, cmd, arg);
+ }
+ break;
+ case HAILO_NNC_IOCTL_MAGIC:
+--- a/drivers/media/pci/hailo/src/fops.h
++++ b/drivers/media/pci/hailo/src/fops.h
+@@ -1,16 +1,17 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_PCI_FOPS_H_
+ #define _HAILO_PCI_FOPS_H_
+
++#include "pcie.h"
++
+ int hailo_pcie_fops_open(struct inode* inode, struct file* filp);
+ int hailo_pcie_fops_release(struct inode* inode, struct file* filp);
+ long hailo_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg);
+ int hailo_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma);
+-int hailo_pcie_driver_down(struct hailo_pcie_board *board);
+ void hailo_pcie_ep_init(struct hailo_pcie_board *board);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/nnc.c
+@@ -0,0 +1,299 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * A Hailo PCIe NNC device is a device contains a NNC (neural network core) and some basic FW.
++ * The device supports sending controls, receiving notification and reading the FW log.
++ */
++
++#include "nnc.h"
++#include "hailo_ioctl_common.h"
++
++#include "utils/logs.h"
++#include "utils/compact.h"
++
++#include <linux/uaccess.h>
++
++#if !defined(HAILO_EMULATOR)
++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (5)
++#else /* !defined(HAILO_EMULATOR) */
++#define DEFAULT_SHUTDOWN_TIMEOUT_MS (1000)
++#endif /* !defined(HAILO_EMULATOR) */
++
++void hailo_nnc_init(struct hailo_pcie_nnc *nnc)
++{
++ sema_init(&nnc->fw_control.mutex, 1);
++ spin_lock_init(&nnc->notification_read_spinlock);
++ init_completion(&nnc->fw_control.completion);
++ INIT_LIST_HEAD(&nnc->notification_wait_list);
++ memset(&nnc->notification_cache, 0, sizeof(nnc->notification_cache));
++}
++
++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc)
++{
++ struct hailo_notification_wait *cursor = NULL;
++
++ // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed
++ rcu_read_lock();
++ list_for_each_entry_rcu(cursor, &nnc->notification_wait_list, notification_wait_list) {
++ cursor->is_disabled = true;
++ complete(&cursor->notification_completion);
++ }
++ rcu_read_unlock();
++}
++
++static int hailo_fw_control(struct hailo_pcie_board *board, unsigned long arg, bool* should_up_board_mutex)
++{
++ struct hailo_fw_control *command = &board->nnc.fw_control.command;
++ long completion_result = 0;
++ int err = 0;
++
++ up(&board->mutex);
++ *should_up_board_mutex = false;
++
++ if (down_interruptible(&board->nnc.fw_control.mutex)) {
++ hailo_info(board, "hailo_fw_control down_interruptible fail tgid:%d (process was interrupted or killed)\n", current->tgid);
++ return -ERESTARTSYS;
++ }
++
++ if (copy_from_user(command, (void __user*)arg, sizeof(*command))) {
++ hailo_err(board, "hailo_fw_control, copy_from_user fail\n");
++ err = -ENOMEM;
++ goto l_exit;
++ }
++
++ reinit_completion(&board->nnc.fw_control.completion);
++
++ err = hailo_pcie_write_firmware_control(&board->pcie_resources, command);
++ if (err < 0) {
++ hailo_err(board, "Failed writing fw control to pcie\n");
++ goto l_exit;
++ }
++
++ // Wait for response
++ completion_result = wait_for_completion_interruptible_timeout(&board->nnc.fw_control.completion, msecs_to_jiffies(command->timeout_ms));
++ if (completion_result <= 0) {
++ if (0 == completion_result) {
++ hailo_err(board, "hailo_fw_control, timeout waiting for control (timeout_ms=%d)\n", command->timeout_ms);
++ err = -ETIMEDOUT;
++ } else {
++ hailo_info(board, "hailo_fw_control, wait for completion failed with err=%ld (process was interrupted or killed)\n", completion_result);
++ err = -EINTR;
++ }
++ goto l_exit;
++ }
++
++ err = hailo_pcie_read_firmware_control(&board->pcie_resources, command);
++ if (err < 0) {
++ hailo_err(board, "Failed reading fw control from pcie\n");
++ goto l_exit;
++ }
++
++ if (copy_to_user((void __user*)arg, command, sizeof(*command))) {
++ hailo_err(board, "hailo_fw_control, copy_to_user fail\n");
++ err = -ENOMEM;
++ goto l_exit;
++ }
++
++l_exit:
++ up(&board->nnc.fw_control.mutex);
++ return err;
++}
++
++static long hailo_get_notification_wait_thread(struct hailo_pcie_board *board, struct file *filp,
++ struct hailo_notification_wait **current_waiting_thread)
++{
++ struct hailo_notification_wait *cursor = NULL;
++ // note: safe to access without rcu because the notification_wait_list is closed only on file release
++ list_for_each_entry(cursor, &board->nnc.notification_wait_list, notification_wait_list)
++ {
++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
++ *current_waiting_thread = cursor;
++ return 0;
++ }
++ }
++
++ return -EFAULT;
++}
++
++static long hailo_read_notification_ioctl(struct hailo_pcie_board *board, unsigned long arg, struct file *filp,
++ bool* should_up_board_mutex)
++{
++ long err = 0;
++ struct hailo_notification_wait *current_waiting_thread = NULL;
++ struct hailo_d2h_notification *notification = &board->nnc.notification_to_user;
++ unsigned long irq_saved_flags;
++
++ err = hailo_get_notification_wait_thread(board, filp, ¤t_waiting_thread);
++ if (0 != err) {
++ goto l_exit;
++ }
++ up(&board->mutex);
++
++ if (0 > (err = wait_for_completion_interruptible(¤t_waiting_thread->notification_completion))) {
++ hailo_info(board,
++ "HAILO_READ_NOTIFICATION - wait_for_completion_interruptible error. err=%ld. tgid=%d (process was interrupted or killed)\n",
++ err, current_waiting_thread->tgid);
++ *should_up_board_mutex = false;
++ goto l_exit;
++ }
++
++ if (down_interruptible(&board->mutex)) {
++ hailo_info(board, "HAILO_READ_NOTIFICATION - down_interruptible error (process was interrupted or killed)\n");
++ *should_up_board_mutex = false;
++ err = -ERESTARTSYS;
++ goto l_exit;
++ }
++
++ // Check if was disabled
++ if (current_waiting_thread->is_disabled) {
++ hailo_info(board, "HAILO_READ_NOTIFICATION, can't find notification wait for tgid=%d\n", current->tgid);
++ err = -EINVAL;
++ goto l_exit;
++ }
++
++ reinit_completion(¤t_waiting_thread->notification_completion);
++
++ spin_lock_irqsave(&board->nnc.notification_read_spinlock, irq_saved_flags);
++ notification->buffer_len = board->nnc.notification_cache.buffer_len;
++ memcpy(notification->buffer, board->nnc.notification_cache.buffer, notification->buffer_len);
++ spin_unlock_irqrestore(&board->nnc.notification_read_spinlock, irq_saved_flags);
++
++ if (copy_to_user((void __user*)arg, notification, sizeof(*notification))) {
++ hailo_err(board, "HAILO_READ_NOTIFICATION copy_to_user fail\n");
++ err = -ENOMEM;
++ goto l_exit;
++ }
++
++l_exit:
++ return err;
++}
++
++static long hailo_disable_notification(struct hailo_pcie_board *board, struct file *filp)
++{
++ struct hailo_notification_wait *cursor = NULL;
++
++ hailo_info(board, "HAILO_DISABLE_NOTIFICATION: disable notification");
++ rcu_read_lock();
++ list_for_each_entry_rcu(cursor, &board->nnc.notification_wait_list, notification_wait_list) {
++ if ((current->tgid == cursor->tgid) && (filp == cursor->filp)) {
++ cursor->is_disabled = true;
++ complete(&cursor->notification_completion);
++ break;
++ }
++ }
++ rcu_read_unlock();
++
++ return 0;
++}
++
++static long hailo_read_log_ioctl(struct hailo_pcie_board *board, unsigned long arg)
++{
++ long err = 0;
++ struct hailo_read_log_params params;
++
++ if (copy_from_user(¶ms, (void __user*)arg, sizeof(params))) {
++ hailo_err(board, "HAILO_READ_LOG, copy_from_user fail\n");
++ return -ENOMEM;
++ }
++
++ if (0 > (err = hailo_pcie_read_firmware_log(&board->pcie_resources.fw_access, ¶ms))) {
++ hailo_err(board, "HAILO_READ_LOG, reading from log failed with error: %ld \n", err);
++ return err;
++ }
++
++ if (copy_to_user((void*)arg, ¶ms, sizeof(params))) {
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
++ struct file *filp, bool *should_up_board_mutex)
++{
++ switch (cmd) {
++ case HAILO_FW_CONTROL:
++ return hailo_fw_control(board, arg, should_up_board_mutex);
++ case HAILO_READ_NOTIFICATION:
++ return hailo_read_notification_ioctl(board, arg, filp, should_up_board_mutex);
++ case HAILO_DISABLE_NOTIFICATION:
++ return hailo_disable_notification(board, filp);
++ case HAILO_READ_LOG:
++ return hailo_read_log_ioctl(board, arg);
++ default:
++ hailo_err(board, "Invalid nnc ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++ return -ENOTTY;
++ }
++}
++
++
++static int add_notification_wait(struct hailo_pcie_board *board, struct file *filp)
++{
++ struct hailo_notification_wait *wait = kmalloc(sizeof(*wait), GFP_KERNEL);
++ if (!wait) {
++ hailo_err(board, "Failed to allocate notification wait structure.\n");
++ return -ENOMEM;
++ }
++ wait->tgid = current->tgid;
++ wait->filp = filp;
++ wait->is_disabled = false;
++ init_completion(&wait->notification_completion);
++ list_add_rcu(&wait->notification_wait_list, &board->nnc.notification_wait_list);
++ return 0;
++}
++
++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++ return add_notification_wait(board, context->filp);
++}
++
++static void clear_notification_wait_list(struct hailo_pcie_board *board, struct file *filp)
++{
++ struct hailo_notification_wait *cur = NULL, *next = NULL;
++ list_for_each_entry_safe(cur, next, &board->nnc.notification_wait_list, notification_wait_list) {
++ if (cur->filp == filp) {
++ list_del_rcu(&cur->notification_wait_list);
++ synchronize_rcu();
++ kfree(cur);
++ }
++ }
++}
++
++int hailo_nnc_driver_down(struct hailo_pcie_board *board)
++{
++ long completion_result = 0;
++ int err = 0;
++
++ reinit_completion(&board->driver_down.reset_completed);
++
++ hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
++
++ // Wait for response
++ completion_result =
++ wait_for_completion_timeout(&board->driver_down.reset_completed, msecs_to_jiffies(DEFAULT_SHUTDOWN_TIMEOUT_MS));
++ if (completion_result <= 0) {
++ if (0 == completion_result) {
++ hailo_err(board, "hailo_nnc_driver_down, timeout waiting for shutdown response (timeout_ms=%d)\n", DEFAULT_SHUTDOWN_TIMEOUT_MS);
++ err = -ETIMEDOUT;
++ } else {
++ hailo_info(board, "hailo_nnc_driver_down, wait for completion failed with err=%ld (process was interrupted or killed)\n",
++ completion_result);
++ err = completion_result;
++ }
++ goto l_exit;
++ }
++
++l_exit:
++ return err;
++}
++
++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++ clear_notification_wait_list(board, context->filp);
++
++ if (context->filp == board->vdma.used_by_filp) {
++ hailo_nnc_driver_down(board);
++ }
++}
+\ No newline at end of file
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/nnc.h
+@@ -0,0 +1,22 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#ifndef _HAILO_PCI_NNC_H_
++#define _HAILO_PCI_NNC_H_
++
++#include "pcie.h"
++
++void hailo_nnc_init(struct hailo_pcie_nnc *nnc);
++void hailo_nnc_finalize(struct hailo_pcie_nnc *nnc);
++
++long hailo_nnc_ioctl(struct hailo_pcie_board *board, unsigned int cmd, unsigned long arg,
++ struct file *filp, bool *should_up_board_mutex);
++
++int hailo_nnc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context);
++void hailo_nnc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context);
++
++int hailo_nnc_driver_down(struct hailo_pcie_board *board);
++
++#endif /* _HAILO_PCI_NNC_H_ */
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.c
++++ /dev/null
+@@ -1,155 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+- **/
+-#include "pci_soc_ioctl.h"
+-
+-#include "utils.h"
+-#include "vdma_common.h"
+-#include "utils/logs.h"
+-#include "vdma/memory.h"
+-
+-#define PCI_SOC_VDMA_ENGINE_INDEX (0)
+-#define PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS (10000)
+-
+-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg)
+-{
+- switch (cmd) {
+- case HAILO_SOC_CONNECT:
+- return hailo_soc_connect_ioctl(board, context, controller, arg);
+- case HAILO_SOC_CLOSE:
+- return hailo_soc_close_ioctl(board, controller, arg);
+- default:
+- hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
+- return -ENOTTY;
+- }
+-}
+-
+-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+- struct hailo_vdma_controller *controller, unsigned long arg)
+-{
+- struct hailo_soc_connect_params params;
+- struct hailo_vdma_channel *input_channel = NULL;
+- struct hailo_vdma_channel *output_channel = NULL;
+- struct hailo_vdma_engine *vdma_engine = NULL;
+- struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL;
+- struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL;
+- uint8_t depth = 0;
+- int err = 0;
+- long completion_result = 0;
+-
+- if (copy_from_user(¶ms, (void *)arg, sizeof(params))) {
+- hailo_err(board, "copy_from_user fail\n");
+- return -ENOMEM;
+- }
+-
+- // TODO: have pci_ep choose the channel indexes the soc will use - for now use 0 and 16
+- params.input_channel_index = 0;
+- params.output_channel_index = 16;
+-
+- reinit_completion(&board->soc_connect_accepted);
+- hailo_soc_write_soc_connect(&board->pcie_resources);
+-
+- // Wait for completion
+- completion_result = wait_for_completion_interruptible_timeout(&board->soc_connect_accepted,
+- msecs_to_jiffies(PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS));
+- if (0 > completion_result) {
+- if (0 == completion_result) {
+- hailo_err(board, "Timeout waiting for connect to be accepted (timeout_ms=%d)\n", PCI_SOC_WAIT_FOR_CONNECT_TIMEOUT_MS);
+- return -ETIMEDOUT;
+- } else {
+- hailo_info(board, "soc connect failed with err=%ld (process was interrupted or killed)\n",
+- completion_result);
+- return -EINTR;
+- }
+- }
+-
+- vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
+- input_channel = &vdma_engine->channels[params.input_channel_index];
+- output_channel = &vdma_engine->channels[params.output_channel_index];
+-
+- input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.input_desc_handle);
+- output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(context, params.output_desc_handle);
+- if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) {
+- hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n");
+- return -EINVAL;
+- }
+-
+- // Make sure channels that we are accepting are not already enabled
+- if (0 != (vdma_engine->enabled_channels & params.input_channel_index) ||
+- 0 != (vdma_engine->enabled_channels & params.output_channel_index)) {
+- hailo_dev_err(&board->pDev->dev, "Trying to accept already enabled channels\n");
+- return -EINVAL;
+- }
+-
+- if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) ||
+- !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) {
+- hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n");
+- return -EINVAL;
+- }
+-
+- // configure and start input channel
+- depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count);
+- // DMA Direction is only to get channel index - so
+- err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth,
+- board->vdma.hw->ddr_data_id);
+- if (err < 0) {
+- hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index);
+- return -EINVAL;
+- }
+-
+- // configure and start output channel
+- depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count);
+- // DMA Direction is only to get channel index - so
+- err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth,
+- board->vdma.hw->ddr_data_id);
+- if (err < 0) {
+- hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index);
+- // Close input channel
+- hailo_vdma_stop_channel(input_channel->host_regs);
+- return -EINVAL;
+- }
+-
+- if (copy_to_user((void *)arg, ¶ms, sizeof(params))) {
+- hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n");
+- return -ENOMEM;
+- }
+-
+- return 0;
+-}
+-
+-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg)
+-{
+- struct hailo_soc_close_params params;
+- struct hailo_vdma_channel *input_channel = NULL;
+- struct hailo_vdma_channel *output_channel = NULL;
+- struct hailo_vdma_engine *vdma_engine = NULL;
+-
+- if (copy_from_user(¶ms, (void *)arg, sizeof(params))) {
+- hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n");
+- return -ENOMEM;
+- }
+-
+- vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
+-
+- if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) {
+- hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index);
+- return -EINVAL;
+- }
+-
+- if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) {
+- hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index);
+- return -EINVAL;
+- }
+-
+- input_channel = &vdma_engine->channels[params.input_channel_index];
+- output_channel = &vdma_engine->channels[params.output_channel_index];
+-
+- // Close channels
+- hailo_vdma_stop_channel(input_channel->host_regs);
+- hailo_vdma_stop_channel(output_channel->host_regs);
+-
+- hailo_pcie_write_firmware_driver_shutdown(&board->pcie_resources);
+- return 0;
+-}
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pcie.c
++++ b/drivers/media/pci/hailo/src/pcie.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include <linux/version.h>
+@@ -22,6 +22,8 @@
+
+ #include "hailo_ioctl_common.h"
+ #include "pcie.h"
++#include "nnc.h"
++#include "soc.h"
+ #include "fops.h"
+ #include "sysfs.h"
+ #include "utils/logs.h"
+@@ -40,11 +42,12 @@ enum hailo_allocate_driver_buffer_driver
+ HAILO_FORCE_BUFFER_FROM_DRIVER = 2,
+ };
+
+-//Debug flag
++// Debug flag
+ static int force_desc_page_size = 0;
+ static bool g_is_power_mode_enabled = true;
+ static int force_allocation_from_driver = HAILO_NO_FORCE_BUFFER;
+ static bool force_hailo15_legacy_mode = false;
++static bool force_boot_linux_from_eemc = false;
+
+ #define DEVICE_NODE_NAME "hailo"
+ static int char_major = 0;
+@@ -206,7 +209,7 @@ static int hailo_pcie_disable_aspm(struc
+ /* Double-check ASPM control. If not disabled by the above, the
+ * BIOS is preventing that from happening (or CONFIG_PCIEASPM is
+ * not enabled); override by writing PCI config space directly.
+- */
++ */
+ err = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &pdev_aspmc);
+ if (err < 0) {
+ hailo_err(board, "Couldn't read LNKCTL capability\n");
+@@ -288,101 +291,59 @@ static void hailo_pcie_remove_board(stru
+ up(&g_hailo_add_board_mutex);
+ }
+
+-static int hailo_write_config(struct hailo_pcie_resources *resources, struct device *dev,
+- const struct hailo_config_constants *config_consts)
+-{
+- const struct firmware *config = NULL;
+- int err = 0;
+-
+- if (NULL == config_consts->filename) {
+- // Config not supported for platform
+- return 0;
+- }
+-
+- err = request_firmware_direct(&config, config_consts->filename, dev);
+- if (err < 0) {
+- hailo_dev_info(dev, "Config %s not found\n", config_consts->filename);
+- return 0;
+- }
+-
+- hailo_dev_notice(dev, "Writing config %s\n", config_consts->filename);
+-
+- err = hailo_pcie_write_config_common(resources, config->data, config->size, config_consts);
+- if (err < 0) {
+- if (-EINVAL == err) {
+- hailo_dev_warn(dev, "Config size %zu is bigger than max %zu\n", config->size, config_consts->max_size);
+- }
+- release_firmware(config);
+- return err;
+- }
+-
+- release_firmware(config);
+- return 0;
+-}
+-
+ static bool wait_for_firmware_completion(struct completion *fw_load_completion)
+ {
+ return (0 != wait_for_completion_timeout(fw_load_completion, msecs_to_jiffies(FIRMWARE_WAIT_TIMEOUT_MS)));
+ }
+
+-static int hailo_load_firmware(struct hailo_pcie_resources *resources,
++static int hailo_load_soc_firmware(struct hailo_pcie_resources *resources,
+ struct device *dev, struct completion *fw_load_completion)
+ {
+- const struct firmware *firmware = NULL;
+- int err = 0;
+ u32 boot_status = 0;
++ int err = 0;
++ u32 second_stage = force_boot_linux_from_eemc ? SECOND_STAGE_LINUX_IN_EMMC : SECOND_STAGE;
+
+ if (hailo_pcie_is_firmware_loaded(resources)) {
+- hailo_dev_warn(dev, "Firmware was already loaded\n");
++ hailo_dev_warn(dev, "Firmware batch was already loaded\n");
+ return 0;
+ }
+
+- reinit_completion(fw_load_completion);
+-
+- err = hailo_write_config(resources, dev, hailo_pcie_get_board_config_constants(resources->board_type));
+- if (err < 0) {
+- hailo_dev_err(dev, "Failed writing board config");
+- return err;
+- }
++ init_completion(fw_load_completion);
+
+- err = hailo_write_config(resources, dev, hailo_pcie_get_user_config_constants(resources->board_type));
++ err = hailo_pcie_write_firmware_batch(dev, resources, FIRST_STAGE);
+ if (err < 0) {
+- hailo_dev_err(dev, "Failed writing fw config");
++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+ return err;
+ }
+
+- // read firmware file
+- err = request_firmware_direct(&firmware, hailo_pcie_get_fw_filename(resources->board_type), dev);
+- if (err < 0) {
+- hailo_dev_warn(dev, "Firmware file not found (/lib/firmware/%s), please upload the firmware manually \n",
+- hailo_pcie_get_fw_filename(resources->board_type));
+- return 0;
++ if (!wait_for_firmware_completion(fw_load_completion)) {
++ boot_status = hailo_get_boot_status(resources);
++ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
++ return -ETIMEDOUT;
+ }
++ reinit_completion(fw_load_completion);
+
+- err = hailo_pcie_write_firmware(resources, firmware->data, firmware->size);
++ err = hailo_pcie_write_firmware_batch(dev, resources, second_stage);
+ if (err < 0) {
+- hailo_dev_err(dev, "Failed writing firmware. err %d\n", err);
+- release_firmware(firmware);
++ hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+ return err;
+ }
+
+- release_firmware(firmware);
+-
+ if (!wait_for_firmware_completion(fw_load_completion)) {
+ boot_status = hailo_get_boot_status(resources);
+ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+ return -ETIMEDOUT;
+ }
+
+- hailo_dev_notice(dev, "Firmware was loaded successfully\n");
++ hailo_dev_notice(dev, "Firmware Batch loaded successfully\n");
++
+ return 0;
+ }
+
+-static int hailo_load_firmware_batch(struct hailo_pcie_resources *resources,
++static int hailo_load_nnc_firmware(struct hailo_pcie_resources *resources,
+ struct device *dev, struct completion *fw_load_completion)
+ {
+ u32 boot_status = 0;
+- u32 pcie_finished = 1;
+ int err = 0;
+
+ if (hailo_pcie_is_firmware_loaded(resources)) {
+@@ -398,31 +359,13 @@ static int hailo_load_firmware_batch(str
+ return err;
+ }
+
+- hailo_trigger_firmware_boot(resources);
+-
+ if (!wait_for_firmware_completion(fw_load_completion)) {
+ boot_status = hailo_get_boot_status(resources);
+ hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+ return -ETIMEDOUT;
+ }
+- reinit_completion(fw_load_completion);
+
+- err = hailo_pcie_write_firmware_batch(dev, resources, SECOND_STAGE);
+- if (err < 0) {
+- hailo_dev_err(dev, "Failed writing firmware files. err %d\n", err);
+- return err;
+- }
+-
+- // TODO: HRT-13838 - Remove, move address to compat, make write_memory static
+- write_memory(resources, 0x84000000, (void*)&pcie_finished, sizeof(pcie_finished));
+-
+- if (!wait_for_firmware_completion(fw_load_completion)) {
+- boot_status = hailo_get_boot_status(resources);
+- hailo_dev_err(dev, "Timeout waiting for firmware file, boot status %u\n", boot_status);
+- return -ETIMEDOUT;
+- }
+-
+- hailo_dev_notice(dev, "Firmware Batch loaded successfully\n");
++ hailo_dev_notice(dev, "Firmware loaded successfully\n");
+
+ return 0;
+ }
+@@ -439,15 +382,13 @@ static int hailo_activate_board(struct h
+ return err;
+ }
+
+- switch (board->pcie_resources.board_type) {
+- case HAILO_BOARD_TYPE_HAILO10H:
+- err = hailo_load_firmware_batch(&board->pcie_resources, &board->pDev->dev,
++ switch (board->pcie_resources.accelerator_type) {
++ case HAILO_ACCELERATOR_TYPE_SOC:
++ err = hailo_load_soc_firmware(&board->pcie_resources, &board->pDev->dev,
+ &board->fw_loaded_completion);
+ break;
+- case HAILO_BOARD_TYPE_HAILO10H_LEGACY:
+- case HAILO_BOARD_TYPE_PLUTO:
+- case HAILO_BOARD_TYPE_HAILO8:
+- err = hailo_load_firmware(&board->pcie_resources, &board->pDev->dev,
++ case HAILO_ACCELERATOR_TYPE_NNC:
++ err = hailo_load_nnc_firmware(&board->pcie_resources, &board->pDev->dev,
+ &board->fw_loaded_completion);
+ break;
+ default:
+@@ -464,6 +405,7 @@ static int hailo_activate_board(struct h
+
+ if (power_mode_enabled()) {
+ // Setting the device to low power state, until the user opens the device
++ hailo_info(board, "Power change state to PCI_D3hot\n");
+ err = pci_set_power_state(board->pDev, PCI_D3hot);
+ if (err < 0) {
+ hailo_err(board, "Set power state failed %d\n", err);
+@@ -755,21 +697,17 @@ static int hailo_pcie_probe(struct pci_d
+
+ pBoard->interrupts_enabled = false;
+ init_completion(&pBoard->fw_loaded_completion);
+- init_completion(&pBoard->soc_connect_accepted);
+
+ sema_init(&pBoard->mutex, 1);
+ atomic_set(&pBoard->ref_count, 0);
+ INIT_LIST_HEAD(&pBoard->open_files_list);
+
+- sema_init(&pBoard->fw_control.mutex, 1);
+- spin_lock_init(&pBoard->notification_read_spinlock);
+- init_completion(&pBoard->fw_control.completion);
++ // Init both soc and nnc, since the interrupts are shared.
++ hailo_nnc_init(&pBoard->nnc);
++ hailo_soc_init(&pBoard->soc);
+
+ init_completion(&pBoard->driver_down.reset_completed);
+
+- INIT_LIST_HEAD(&pBoard->notification_wait_list);
+-
+- memset(&pBoard->notification_cache, 0, sizeof(pBoard->notification_cache));
+ memset(&pBoard->memory_transfer_params, 0, sizeof(pBoard->memory_transfer_params));
+
+ err = hailo_pcie_vdma_controller_init(&pBoard->vdma, &pBoard->pDev->dev,
+@@ -832,7 +770,6 @@ probe_exit:
+ static void hailo_pcie_remove(struct pci_dev* pDev)
+ {
+ struct hailo_pcie_board* pBoard = (struct hailo_pcie_board*) pci_get_drvdata(pDev);
+- struct hailo_notification_wait *cursor = NULL;
+
+ pci_notice(pDev, "Remove: Releasing board\n");
+
+@@ -864,13 +801,7 @@ static void hailo_pcie_remove(struct pci
+
+ pci_set_drvdata(pDev, NULL);
+
+- // Lock rcu_read_lock and send notification_completion to wake anyone waiting on the notification_wait_list when removed
+- rcu_read_lock();
+- list_for_each_entry_rcu(cursor, &pBoard->notification_wait_list, notification_wait_list) {
+- cursor->is_disabled = true;
+- complete(&cursor->notification_completion);
+- }
+- rcu_read_unlock();
++ hailo_nnc_finalize(&pBoard->nnc);
+
+ up(&pBoard->mutex);
+
+@@ -889,6 +820,15 @@ static void hailo_pcie_remove(struct pci
+
+ }
+
++inline int driver_down(struct hailo_pcie_board *board)
++{
++ if (board->pcie_resources.accelerator_type == HAILO_ACCELERATOR_TYPE_NNC) {
++ return hailo_nnc_driver_down(board);
++ } else {
++ return hailo_soc_driver_down(board);
++ }
++}
++
+ #ifdef CONFIG_PM_SLEEP
+ static int hailo_pcie_suspend(struct device *dev)
+ {
+@@ -899,17 +839,16 @@ static int hailo_pcie_suspend(struct dev
+ // lock board to wait for any pending operations
+ down(&board->mutex);
+
+- // Disable all interrupts. All interrupts from Hailo chip would be masked.
+- hailo_disable_interrupts(board);
+-
+- // Close all vDMA channels
+ if (board->vdma.used_by_filp != NULL) {
+- err = hailo_pcie_driver_down(board);
++ err = driver_down(board);
+ if (err < 0) {
+ dev_notice(dev, "Error while trying to call FW to close vdma channels\n");
+ }
+ }
+
++ // Disable all interrupts. All interrupts from Hailo chip would be masked.
++ hailo_disable_interrupts(board);
++
+ // Un validate all activae file contexts so every new action would return error to the user.
+ list_for_each_entry(cur, &board->open_files_list, open_files_list) {
+ cur->is_valid = false;
+@@ -919,8 +858,8 @@ static int hailo_pcie_suspend(struct dev
+ up(&board->mutex);
+
+ dev_notice(dev, "PM's suspend\n");
+- // Continue system suspend
+- return err;
++ // Success Oriented - Continue system suspend even in case of error (otherwise system will not suspend correctly)
++ return 0;
+ }
+
+ static int hailo_pcie_resume(struct device *dev)
+@@ -930,10 +869,10 @@ static int hailo_pcie_resume(struct devi
+
+ if ((err = hailo_activate_board(board)) < 0) {
+ dev_err(dev, "Failed activating board %d\n", err);
+- return err;
+ }
+
+ dev_notice(dev, "PM's resume\n");
++ // Success Oriented - Continue system resume even in case of error (otherwise system will not suspend correctly)
+ return 0;
+ }
+ #endif /* CONFIG_PM_SLEEP */
+@@ -954,7 +893,7 @@ static void hailo_pci_reset_prepare(stru
+ down(&board->mutex);
+ if (board->vdma.used_by_filp != NULL) {
+ // Try to close all vDMA channels before reset
+- err = hailo_pcie_driver_down(board);
++ err = driver_down(board);
+ if (err < 0) {
+ pci_err(pdev, "Error while trying to call FW to close vdma channels (errno %d)\n", err);
+ }
+@@ -1088,6 +1027,9 @@ MODULE_PARM_DESC(force_desc_page_size, "
+ module_param(force_hailo15_legacy_mode, bool, S_IRUGO);
+ MODULE_PARM_DESC(force_hailo15_legacy_mode, "Forces work with Hailo15 in legacy mode(relevant for emulators)");
+
++module_param(force_boot_linux_from_eemc, bool, S_IRUGO);
++MODULE_PARM_DESC(force_boot_linux_from_eemc, "Boot the linux image from eemc (Requires special Image)");
++
+ MODULE_AUTHOR("Hailo Technologies Ltd.");
+ MODULE_DESCRIPTION("Hailo PCIe driver");
+ MODULE_LICENSE("GPL v2");
+--- a/drivers/media/pci/hailo/src/pcie.h
++++ b/drivers/media/pci/hailo/src/pcie.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_PCI_PCIE_H_
+@@ -41,6 +41,19 @@ struct hailo_fw_boot {
+ };
+
+
++struct hailo_pcie_nnc {
++ struct hailo_fw_control_info fw_control;
++
++ spinlock_t notification_read_spinlock;
++ struct list_head notification_wait_list;
++ struct hailo_d2h_notification notification_cache;
++ struct hailo_d2h_notification notification_to_user;
++};
++
++struct hailo_pcie_soc {
++ struct completion control_resp_ready;
++};
++
+ // Context for each open file handle
+ // TODO: store board and use as actual context
+ struct hailo_file_context {
+@@ -48,6 +61,7 @@ struct hailo_file_context {
+ struct file *filp;
+ struct hailo_vdma_file_context vdma_context;
+ bool is_valid;
++ u32 soc_used_channels_bitmap;
+ };
+
+ struct hailo_pcie_board {
+@@ -57,21 +71,17 @@ struct hailo_pcie_board {
+ atomic_t ref_count;
+ struct list_head open_files_list;
+ struct hailo_pcie_resources pcie_resources;
+- struct hailo_fw_control_info fw_control;
++ struct hailo_pcie_nnc nnc;
++ struct hailo_pcie_soc soc;
+ struct hailo_pcie_driver_down_info driver_down;
+ struct semaphore mutex;
+ struct hailo_vdma_controller vdma;
+- spinlock_t notification_read_spinlock;
+- struct list_head notification_wait_list;
+- struct hailo_d2h_notification notification_cache;
+- struct hailo_d2h_notification notification_to_user;
++
+ struct hailo_memory_transfer_params memory_transfer_params;
+ u32 desc_max_page_size;
+ enum hailo_allocation_mode allocation_mode;
+ struct completion fw_loaded_completion;
+ bool interrupts_enabled;
+- // Only needed in accelerator type soc
+- struct completion soc_connect_accepted;
+ };
+
+ bool power_mode_enabled(void);
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/soc.c
+@@ -0,0 +1,244 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++/**
++ * A Hailo PCIe NNC device is a device contains a full SoC over PCIe. The SoC contains NNC (neural network core) and
++ * some application processor (pci_ep).
++ */
++
++#include "soc.h"
++
++#include "vdma_common.h"
++#include "utils/logs.h"
++#include "vdma/memory.h"
++
++#include <linux/uaccess.h>
++
++#define PCI_SOC_VDMA_ENGINE_INDEX (0)
++#define PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS (1000)
++#define PCI_SOC_INPUT_CHANNEL_BITMASK (0x000000FF)
++
++void hailo_soc_init(struct hailo_pcie_soc *soc)
++{
++ init_completion(&soc->control_resp_ready);
++}
++
++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg)
++{
++ switch (cmd) {
++ case HAILO_SOC_CONNECT:
++ return hailo_soc_connect_ioctl(board, context, controller, arg);
++ case HAILO_SOC_CLOSE:
++ return hailo_soc_close_ioctl(board, controller, context, arg);
++ default:
++ hailo_err(board, "Invalid pcie EP ioctl code 0x%x (nr: %d)\n", cmd, _IOC_NR(cmd));
++ return -ENOTTY;
++ }
++}
++
++static int soc_control(struct hailo_pcie_board *board,
++ const struct hailo_pcie_soc_request *request,
++ struct hailo_pcie_soc_response *response)
++{
++ int ret = 0;
++ reinit_completion(&board->soc.control_resp_ready);
++
++ hailo_pcie_soc_write_request(&board->pcie_resources, request);
++
++ ret = wait_for_completion_interruptible_timeout(&board->soc.control_resp_ready,
++ msecs_to_jiffies(PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS));
++ if (ret <= 0) {
++ if (0 == ret) {
++ hailo_err(board, "Timeout waiting for soc control (timeout_ms=%d)\n", PCI_SOC_CONTROL_CONNECT_TIMEOUT_MS);
++ return -ETIMEDOUT;
++ } else {
++ hailo_info(board, "soc control failed with err=%d (process was interrupted or killed)\n",
++ ret);
++ return ret;
++ }
++ }
++
++ hailo_pcie_soc_read_response(&board->pcie_resources, response);
++
++ if (response->status < 0) {
++ hailo_err(board, "soc control failed with status=%d\n", response->status);
++ return response->status;
++ }
++
++ if (response->control_code != request->control_code) {
++ hailo_err(board, "Invalid response control code %d (expected %d)\n",
++ response->control_code, request->control_code);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++ struct hailo_vdma_controller *controller, unsigned long arg)
++{
++ struct hailo_pcie_soc_request request = {0};
++ struct hailo_pcie_soc_response response = {0};
++ struct hailo_soc_connect_params params;
++ struct hailo_vdma_channel *input_channel = NULL;
++ struct hailo_vdma_channel *output_channel = NULL;
++ struct hailo_vdma_engine *vdma_engine = &controller->vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
++ struct hailo_descriptors_list_buffer *input_descriptors_buffer = NULL;
++ struct hailo_descriptors_list_buffer *output_descriptors_buffer = NULL;
++ uint8_t depth = 0;
++ int err = 0;
++
++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) {
++ hailo_err(board, "copy_from_user fail\n");
++ return -ENOMEM;
++ }
++
++ request = (struct hailo_pcie_soc_request) {
++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CONNECT,
++ .connect = {
++ .port = params.port_number
++ }
++ };
++ err = soc_control(board, &request, &response);
++ if (err < 0) {
++ return err;
++ }
++
++ params.input_channel_index = response.connect.input_channel_index;
++ params.output_channel_index = response.connect.output_channel_index;
++
++ if (!hailo_check_channel_index(params.input_channel_index, controller->hw->src_channels_bitmask, true)) {
++ hailo_dev_err(&board->pDev->dev, "Invalid input channel index %u\n", params.input_channel_index);
++ return -EINVAL;
++ }
++
++ if (!hailo_check_channel_index(params.output_channel_index, controller->hw->src_channels_bitmask, false)) {
++ hailo_dev_err(&board->pDev->dev, "Invalid output channel index %u\n", params.output_channel_index);
++ return -EINVAL;
++ }
++
++ input_channel = &vdma_engine->channels[params.input_channel_index];
++ output_channel = &vdma_engine->channels[params.output_channel_index];
++
++ input_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.input_desc_handle);
++ output_descriptors_buffer = hailo_vdma_find_descriptors_buffer(&context->vdma_context, params.output_desc_handle);
++ if (NULL == input_descriptors_buffer || NULL == output_descriptors_buffer) {
++ hailo_dev_err(&board->pDev->dev, "input / output descriptors buffer not found \n");
++ return -EINVAL;
++ }
++
++ if (!is_powerof2((size_t)input_descriptors_buffer->desc_list.desc_count) ||
++ !is_powerof2((size_t)output_descriptors_buffer->desc_list.desc_count)) {
++ hailo_dev_err(&board->pDev->dev, "Invalid desc list size\n");
++ return -EINVAL;
++ }
++
++ // configure and start input channel
++ depth = ceil_log2(input_descriptors_buffer->desc_list.desc_count);
++ // DMA Direction is only to get channel index - so
++ err = hailo_vdma_start_channel(input_channel->host_regs, input_descriptors_buffer->dma_address, depth,
++ board->vdma.hw->ddr_data_id);
++ if (err < 0) {
++ hailo_dev_err(&board->pDev->dev, "Error starting vdma input channel index %u\n", params.input_channel_index);
++ return -EINVAL;
++ }
++
++ // Store the input channels state in bitmap (open)
++ hailo_set_bit(params.input_channel_index, &context->soc_used_channels_bitmap);
++
++ // configure and start output channel
++ depth = ceil_log2(output_descriptors_buffer->desc_list.desc_count);
++ // DMA Direction is only to get channel index - so
++ err = hailo_vdma_start_channel(output_channel->host_regs, output_descriptors_buffer->dma_address, depth,
++ board->vdma.hw->ddr_data_id);
++ if (err < 0) {
++ hailo_dev_err(&board->pDev->dev, "Error starting vdma output channel index %u\n", params.output_channel_index);
++ // Close input channel
++ hailo_vdma_stop_channel(input_channel->host_regs);
++ return -EINVAL;
++ }
++
++ // Store the output channels state in bitmap (open)
++ hailo_set_bit(params.output_channel_index, &context->soc_used_channels_bitmap);
++
++ if (copy_to_user((void *)arg, ¶ms, sizeof(params))) {
++ hailo_dev_err(&board->pDev->dev, "copy_to_user fail\n");
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int close_channels(struct hailo_pcie_board *board, u32 channels_bitmap)
++{
++ struct hailo_pcie_soc_request request = {0};
++ struct hailo_pcie_soc_response response = {0};
++ struct hailo_vdma_engine *engine = &board->vdma.vdma_engines[PCI_SOC_VDMA_ENGINE_INDEX];
++ struct hailo_vdma_channel *channel = NULL;
++ u8 channel_index = 0;
++
++ hailo_info(board, "Closing channels bitmap 0x%x\n", channels_bitmap);
++ for_each_vdma_channel(engine, channel, channel_index) {
++ if (hailo_test_bit(channel_index, &channels_bitmap)) {
++ hailo_vdma_stop_channel(channel->host_regs);
++ }
++ }
++
++ request = (struct hailo_pcie_soc_request) {
++ .control_code = HAILO_PCIE_SOC_CONTROL_CODE_CLOSE,
++ .close = {
++ .channels_bitmap = channels_bitmap
++ }
++ };
++ return soc_control(board, &request, &response);
++}
++
++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller,
++ struct hailo_file_context *context, unsigned long arg)
++{
++ struct hailo_soc_close_params params;
++ u32 channels_bitmap = 0;
++ int err = 0;
++
++ if (copy_from_user(¶ms, (void *)arg, sizeof(params))) {
++ hailo_dev_err(&board->pDev->dev, "copy_from_user fail\n");
++ return -ENOMEM;
++ }
++
++ // TOOD: check channels are connected
++
++ channels_bitmap = (1 << params.input_channel_index) | (1 << params.output_channel_index);
++
++ err = close_channels(board, channels_bitmap);
++ if (0 != err) {
++ hailo_dev_err(&board->pDev->dev, "Error closing channels\n");
++ return err;
++ }
++
++ // Store the channel state in bitmap (closed)
++ hailo_clear_bit(params.input_channel_index, &context->soc_used_channels_bitmap);
++ hailo_clear_bit(params.output_channel_index, &context->soc_used_channels_bitmap);
++
++ return err;
++}
++
++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++ // Nothing to init yet
++ return 0;
++}
++
++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context)
++{
++ // close only channels connected by this (by bitmap)
++ if (context->soc_used_channels_bitmap != 0) {
++ close_channels(board, context->soc_used_channels_bitmap);
++ }
++}
++
++int hailo_soc_driver_down(struct hailo_pcie_board *board)
++{
++ return close_channels(board, 0xFFFFFFFF);
++}
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/pci_soc_ioctl.h
++++ /dev/null
+@@ -1,19 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+- **/
+-
+-#ifndef _HAILO_PCI_SOC_IOCTL_H_
+-#define _HAILO_PCI_SOC_IOCTL_H_
+-
+-#include "vdma/ioctl.h"
+-#include "pcie.h"
+-
+-
+-long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+- struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg);
+-long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_file_context *context,
+- struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, unsigned long arg);
+-
+-#endif // _HAILO_PCI_SOC_IOCTL_H_
+\ No newline at end of file
+--- /dev/null
++++ b/drivers/media/pci/hailo/src/soc.h
+@@ -0,0 +1,26 @@
++// SPDX-License-Identifier: GPL-2.0
++/**
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
++ **/
++
++#ifndef _HAILO_PCI_SOC_IOCTL_H_
++#define _HAILO_PCI_SOC_IOCTL_H_
++
++#include "vdma/ioctl.h"
++#include "pcie.h"
++
++
++void hailo_soc_init(struct hailo_pcie_soc *soc);
++
++long hailo_soc_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++ struct hailo_vdma_controller *controller, unsigned int cmd, unsigned long arg);
++long hailo_soc_connect_ioctl(struct hailo_pcie_board *board, struct hailo_file_context *context,
++ struct hailo_vdma_controller *controller, unsigned long arg);
++long hailo_soc_close_ioctl(struct hailo_pcie_board *board, struct hailo_vdma_controller *controller, struct hailo_file_context *context, unsigned long arg);
++
++int hailo_soc_file_context_init(struct hailo_pcie_board *board, struct hailo_file_context *context);
++void hailo_soc_file_context_finalize(struct hailo_pcie_board *board, struct hailo_file_context *context);
++
++int hailo_soc_driver_down(struct hailo_pcie_board *board);
++
++#endif // _HAILO_PCI_SOC_IOCTL_H_
+\ No newline at end of file
+--- a/drivers/media/pci/hailo/src/sysfs.c
++++ b/drivers/media/pci/hailo/src/sysfs.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "sysfs.h"
+--- a/drivers/media/pci/hailo/src/sysfs.h
++++ b/drivers/media/pci/hailo/src/sysfs.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_PCI_SYSFS_H_
+--- a/drivers/media/pci/hailo/src/utils.c
++++ /dev/null
+@@ -1,26 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
+- **/
+-
+-#include <linux/version.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/pci.h>
+-
+-#include "pcie.h"
+-#include "utils.h"
+-#include "utils/logs.h"
+-
+-
+-void hailo_pcie_clear_notification_wait_list(struct hailo_pcie_board *pBoard, struct file *filp)
+-{
+- struct hailo_notification_wait *cur = NULL, *next = NULL;
+- list_for_each_entry_safe(cur, next, &pBoard->notification_wait_list, notification_wait_list) {
+- if (cur->filp == filp) {
+- list_del_rcu(&cur->notification_wait_list);
+- synchronize_rcu();
+- kfree(cur);
+- }
+- }
+-}
+--- a/drivers/media/pci/hailo/utils/compact.h
++++ b/drivers/media/pci/hailo/utils/compact.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_PCI_COMPACT_H_
+--- a/drivers/media/pci/hailo/utils/fw_common.h
++++ b/drivers/media/pci/hailo/utils/fw_common.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_LINUX_COMMON_H_
+--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.c
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "integrated_nnc_utils.h"
+@@ -43,11 +43,19 @@ int hailo_ioremap_shmem(struct platform_
+ void __iomem * remap_ptr;
+
+ shmem = of_parse_phandle(pdev->dev.of_node, "shmem", index);
++ if (!shmem) {
++ hailo_dev_err(&pdev->dev, "Failed to find shmem node index: %d in device tree\n", index);
++ return -ENODEV;
++ }
++
+ ret = of_address_to_resource(shmem, 0, &res);
+ if (ret) {
+ hailo_dev_err(&pdev->dev, "hailo_ioremap_shmem, failed to get memory (index: %d)\n", index);
++ of_node_put(shmem);
+ return ret;
+ }
++
++ // Decrement the refcount of the node
+ of_node_put(shmem);
+
+ remap_ptr = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
+--- a/drivers/media/pci/hailo/utils/integrated_nnc_utils.h
++++ b/drivers/media/pci/hailo/utils/integrated_nnc_utils.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _INTEGRATED_NNC_UTILS_H_
+--- a/drivers/media/pci/hailo/utils/logs.c
++++ b/drivers/media/pci/hailo/utils/logs.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "logs.h"
+--- a/drivers/media/pci/hailo/utils/logs.h
++++ b/drivers/media/pci/hailo/utils/logs.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _COMMON_LOGS_H_
+--- a/drivers/media/pci/hailo/vdma/ioctl.c
++++ b/drivers/media/pci/hailo/vdma/ioctl.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #include "ioctl.h"
+@@ -12,7 +12,7 @@
+ #include <linux/uaccess.h>
+
+
+-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg)
++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context)
+ {
+ struct hailo_vdma_enable_channels_params input;
+ struct hailo_vdma_engine *engine = NULL;
+@@ -40,12 +40,15 @@ long hailo_vdma_enable_channels_ioctl(st
+ hailo_vdma_update_interrupts_mask(controller, engine_index);
+ hailo_dev_info(controller->dev, "Enabled interrupts for engine %u, channels bitmap 0x%x\n",
+ engine_index, channels_bitmap);
++
++ // Update the context with the enabled channels bitmap
++ context->enabled_channels_bitmap[engine_index] |= channels_bitmap;
+ }
+
+ return 0;
+ }
+
+-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg)
++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context)
+ {
+ struct hailo_vdma_disable_channels_params input;
+ struct hailo_vdma_engine *engine = NULL;
+@@ -77,6 +80,9 @@ long hailo_vdma_disable_channels_ioctl(s
+
+ hailo_dev_info(controller->dev, "Disabled channels for engine %u, bitmap 0x%x\n",
+ engine_index, channels_bitmap);
++
++ // Update the context with the disabled channels bitmap
++ context->enabled_channels_bitmap[engine_index] &= ~channels_bitmap;
+ }
+
+ // Wake up threads waiting
+@@ -204,7 +210,7 @@ long hailo_vdma_buffer_map_ioctl(struct
+ return -EFAULT;
+ }
+
+- hailo_dev_info(controller->dev, "address %lx tgid %d size: %zu\n",
++ hailo_dev_dbg(controller->dev, "address %lx tgid %d size: %zu\n",
+ buf_info.user_address, current->tgid, buf_info.size);
+
+ direction = get_dma_direction(buf_info.data_direction);
+@@ -231,7 +237,7 @@ long hailo_vdma_buffer_map_ioctl(struct
+ }
+
+ list_add(&mapped_buffer->mapped_user_buffer_list, &context->mapped_user_buffer_list);
+- hailo_dev_info(controller->dev, "buffer %lx (handle %zu) is mapped\n",
++ hailo_dev_dbg(controller->dev, "buffer %lx (handle %zu) is mapped\n",
+ buf_info.user_address, buf_info.mapped_handle);
+ return 0;
+ }
+@@ -247,7 +253,7 @@ long hailo_vdma_buffer_unmap_ioctl(struc
+ return -EFAULT;
+ }
+
+- hailo_dev_info(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle);
++ hailo_dev_dbg(controller->dev, "unmap user buffer handle %zu\n", buffer_unmap_params.mapped_handle);
+
+ mapped_buffer = hailo_vdma_find_mapped_user_buffer(context, buffer_unmap_params.mapped_handle);
+ if (mapped_buffer == NULL) {
+--- a/drivers/media/pci/hailo/vdma/ioctl.h
++++ b/drivers/media/pci/hailo/vdma/ioctl.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #ifndef _HAILO_VDMA_IOCTL_H_
+@@ -8,8 +8,8 @@
+
+ #include "vdma/vdma.h"
+
+-long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
+-long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg);
++long hailo_vdma_enable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context);
++long hailo_vdma_disable_channels_ioctl(struct hailo_vdma_controller *controller, unsigned long arg, struct hailo_vdma_file_context *context);
+ long hailo_vdma_interrupts_wait_ioctl(struct hailo_vdma_controller *controller, unsigned long arg,
+ struct semaphore *mutex, bool *should_up_board_mutex);
+
+--- a/drivers/media/pci/hailo/vdma/memory.c
++++ b/drivers/media/pci/hailo/vdma/memory.c
+@@ -1,11 +1,12 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #define pr_fmt(fmt) "hailo: " fmt
+
+ #include "memory.h"
++#include "utils.h"
+ #include "utils/compact.h"
+
+ #include <linux/slab.h>
+@@ -316,6 +317,11 @@ int hailo_desc_list_create(struct device
+ size_t buffer_size = 0;
+ const u64 align = VDMA_DESCRIPTOR_LIST_ALIGN; //First addr must be aligned on 64 KB (from the VDMA registers documentation)
+
++ if (MAX_POWER_OF_2_VALUE < descriptors_count) {
++ dev_err(dev, "Invalid descriptors count %u\n", descriptors_count);
++ return -EINVAL;
++ }
++
+ buffer_size = descriptors_count * sizeof(struct hailo_vdma_descriptor);
+ buffer_size = ALIGN(buffer_size, align);
+
+@@ -323,7 +329,7 @@ int hailo_desc_list_create(struct device
+ &descriptors->dma_address, GFP_KERNEL | __GFP_ZERO);
+ if (descriptors->kernel_address == NULL) {
+ dev_err(dev, "Failed to allocate descriptors list, desc_count 0x%x, buffer_size 0x%zx, This failure means there is not a sufficient amount of CMA memory "
+- "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficent memory.\n",
++ "(contiguous physical memory), This usually is caused by lack of general system memory. Please check you have sufficient memory.\n",
+ descriptors_count, buffer_size);
+ return -ENOMEM;
+ }
+@@ -333,6 +339,8 @@ int hailo_desc_list_create(struct device
+
+ descriptors->desc_list.desc_list = descriptors->kernel_address;
+ descriptors->desc_list.desc_count = descriptors_count;
++ // No need to check the return value of get_nearest_powerof_2 because we already checked the input
++ descriptors->desc_list.desc_count_mask = is_circular ? (descriptors_count - 1) : (get_nearest_powerof_2(descriptors_count) - 1);
+ descriptors->desc_list.desc_page_size = desc_page_size;
+ descriptors->desc_list.is_circular = is_circular;
+
+--- a/drivers/media/pci/hailo/vdma/memory.h
++++ b/drivers/media/pci/hailo/vdma/memory.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+ /**
+ * vDMA memory utility (including allocation and mappings)
+--- a/drivers/media/pci/hailo/vdma/vdma.c
++++ b/drivers/media/pci/hailo/vdma/vdma.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+
+ #define pr_fmt(fmt) "hailo: " fmt
+@@ -105,6 +105,9 @@ void hailo_vdma_file_context_init(struct
+ INIT_LIST_HEAD(&context->descriptors_buffer_list);
+ INIT_LIST_HEAD(&context->vdma_low_memory_buffer_list);
+ INIT_LIST_HEAD(&context->continuous_buffer_list);
++
++ BUILD_BUG_ON_MSG(MAX_VDMA_CHANNELS_PER_ENGINE > sizeof(context->enabled_channels_bitmap[0]) * BITS_IN_BYTE,
++ "Unexpected amount of VDMA channels per engine");
+ }
+
+ void hailo_vdma_update_interrupts_mask(struct hailo_vdma_controller *controller,
+@@ -119,21 +122,22 @@ void hailo_vdma_file_context_finalize(st
+ {
+ size_t engine_index = 0;
+ struct hailo_vdma_engine *engine = NULL;
+- const u32 channels_bitmap = 0xFFFFFFFF; // disable all channel interrupts
+ unsigned long irq_saved_flags = 0;
+ // In case of FLR, the vdma registers will be NULL
+ const bool is_device_up = (NULL != controller->dev);
+
+- if (filp == controller->used_by_filp) {
+- for_each_vdma_engine(controller, engine, engine_index) {
+- hailo_vdma_engine_disable_channels(engine, channels_bitmap);
++ for_each_vdma_engine(controller, engine, engine_index) {
++ if (context->enabled_channels_bitmap[engine_index]) {
++ hailo_dev_info(controller->dev, "Disabling channels for engine %zu, channels bitmap 0x%x\n", engine_index,
++ context->enabled_channels_bitmap[engine_index]);
++ hailo_vdma_engine_disable_channels(engine, context->enabled_channels_bitmap[engine_index]);
+
+ if (is_device_up) {
+ hailo_vdma_update_interrupts_mask(controller, engine_index);
+ }
+
+ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
+- hailo_vdma_engine_clear_channel_interrupts(engine, channels_bitmap);
++ hailo_vdma_engine_clear_channel_interrupts(engine, context->enabled_channels_bitmap[engine_index]);
+ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+ }
+ }
+@@ -148,10 +152,21 @@ void hailo_vdma_file_context_finalize(st
+ }
+ }
+
++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine,
++ u32 channels_bitmap)
++{
++ unsigned long irq_saved_flags = 0;
++
++ spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
++ hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap);
++ spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
++
++ wake_up_interruptible_all(&controller->interrupts_wq);
++}
++
+ void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller,
+ size_t engine_index, u32 channels_bitmap)
+ {
+- unsigned long irq_saved_flags = 0;
+ struct hailo_vdma_engine *engine = NULL;
+
+ BUG_ON(engine_index >= controller->vdma_engines_count);
+@@ -159,11 +174,7 @@ void hailo_vdma_irq_handler(struct hailo
+
+ hailo_vdma_engine_push_timestamps(engine, channels_bitmap);
+
+- spin_lock_irqsave(&controller->interrupts_lock, irq_saved_flags);
+- hailo_vdma_engine_set_channel_interrupts(engine, channels_bitmap);
+- spin_unlock_irqrestore(&controller->interrupts_lock, irq_saved_flags);
+-
+- wake_up_interruptible_all(&controller->interrupts_wq);
++ hailo_vdma_wakeup_interrupts(controller, engine, channels_bitmap);
+ }
+
+ long hailo_vdma_ioctl(struct hailo_vdma_file_context *context, struct hailo_vdma_controller *controller,
+@@ -171,9 +182,9 @@ long hailo_vdma_ioctl(struct hailo_vdma_
+ {
+ switch (cmd) {
+ case HAILO_VDMA_ENABLE_CHANNELS:
+- return hailo_vdma_enable_channels_ioctl(controller, arg);
++ return hailo_vdma_enable_channels_ioctl(controller, arg, context);
+ case HAILO_VDMA_DISABLE_CHANNELS:
+- return hailo_vdma_disable_channels_ioctl(controller, arg);
++ return hailo_vdma_disable_channels_ioctl(controller, arg, context);
+ case HAILO_VDMA_INTERRUPTS_WAIT:
+ return hailo_vdma_interrupts_wait_ioctl(controller, arg, mutex, should_up_board_mutex);
+ case HAILO_VDMA_INTERRUPTS_READ_TIMESTAMPS:
+--- a/drivers/media/pci/hailo/vdma/vdma.h
++++ b/drivers/media/pci/hailo/vdma/vdma.h
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /**
+- * Copyright (c) 2019-2022 Hailo Technologies Ltd. All rights reserved.
++ * Copyright (c) 2019-2024 Hailo Technologies Ltd. All rights reserved.
+ **/
+ /**
+ * Hailo vdma engine definitions
+@@ -130,6 +130,7 @@ struct hailo_vdma_file_context {
+ struct list_head descriptors_buffer_list;
+ struct list_head vdma_low_memory_buffer_list;
+ struct list_head continuous_buffer_list;
++ u32 enabled_channels_bitmap[MAX_VDMA_ENGINES];
+ };
+
+
+@@ -145,6 +146,8 @@ void hailo_vdma_file_context_init(struct
+ void hailo_vdma_file_context_finalize(struct hailo_vdma_file_context *context,
+ struct hailo_vdma_controller *controller, struct file *filp);
+
++void hailo_vdma_wakeup_interrupts(struct hailo_vdma_controller *controller, struct hailo_vdma_engine *engine,
++ u32 channels_bitmap);
+ void hailo_vdma_irq_handler(struct hailo_vdma_controller *controller, size_t engine_index,
+ u32 channels_bitmap);
+
--- /dev/null
+From dbf12796d1368286672529d7b03f81066a8c36f3 Mon Sep 17 00:00:00 2001
+From: Iker Pedrosa <ikerpedrosam@gmail.com>
+Date: Mon, 18 Nov 2024 10:55:33 +0100
+Subject: [PATCH] dtoverlays: enable SPI CS active-high
+
+The documentation isn't very clear explaining how to enable SPI CS
+active-high and it takes a long time to understand it. Adding a specific
+overlay as a simple example on how to invert this signal can help
+understand the solution.
+
+Link: https://forums.raspberrypi.com/viewtopic.php?t=378222
+Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 8 +++
+ .../overlays/spi0-1cs-inverted-overlay.dts | 59 +++++++++++++++++++
+ 3 files changed, 68 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -259,6 +259,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ spi-rtc.dtbo \
+ spi0-0cs.dtbo \
+ spi0-1cs.dtbo \
++ spi0-1cs-inverted.dtbo \
+ spi0-2cs.dtbo \
+ spi1-1cs.dtbo \
+ spi1-2cs.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4438,6 +4438,14 @@ Params: cs0_pin GPIO pin
+ it for other uses.
+
+
++Name: spi0-1cs-inverted
++Info: Only use one CS pin for SPI0 and set to active-high
++Load: dtoverlay=spi0-1cs-inverted,<param>=<val>
++Params: cs0_pin GPIO pin for CS0 (default 8)
++ no_miso Don't claim and use the MISO pin (9), freeing
++ it for other uses.
++
++
+ Name: spi0-2cs
+ Info: Change the CS pins for SPI0
+ Load: dtoverlay=spi0-2cs,<param>=<val>
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi0-1cs-inverted-overlay.dts
+@@ -0,0 +1,59 @@
++/dts-v1/;
++/plugin/;
++
++/*
++ * There are some devices that need an inverted Chip Select (CS) to select the
++ * device signal, as an example the AZDelivery 12864 display. That means that
++ * the CS polarity is active-high. To invert the CS signal the DT needs to set
++ * the cs-gpio to GPIO_ACTIVE_HIGH (0) in the controller and set the
++ * spi-cs-high in the peripheral property. On top of that, since this is a
++ * display the DT also needs to specify the write-only property.
++*/
++
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&spi0_cs_pins>;
++ frag0: __overlay__ {
++ brcm,pins = <8>;
++ };
++ };
++
++ fragment@1 {
++ target = <&spi0>;
++ frag1: __overlay__ {
++ cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
++ status = "okay";
++ };
++ };
++
++ fragment@2 {
++ target = <&spidev1>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@3 {
++ target = <&spi0_pins>;
++ __dormant__ {
++ brcm,pins = <10 11>;
++ };
++ };
++
++ fragment@4 {
++ target = <&spidev0>;
++ __overlay__ {
++ spi-cs-high;
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag0>,"brcm,pins:0",
++ <&frag1>,"cs-gpios:4";
++ no_miso = <0>,"=3";
++ };
++};
--- /dev/null
+From 57b528e557890f25e010b6bc7356b5a716c79db2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 12 Nov 2024 17:58:52 +0000
+Subject: [PATCH] drm/vc4: hvs: Defer updating the enable_bg_fill until vblank
+
+The register to enable/disable background fill was being set
+from atomic flush, however that will be applied immediately and
+can be a while before the vblank. If it was required for the
+current frame but not for the next one, that can result in
+corruption for part of the current frame.
+
+Store the state in vc4_hvs, and update it on vblank.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
+ drivers/gpu/drm/vc4/vc4_hvs.c | 18 ++++++++++--------
+ 2 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -339,6 +339,8 @@ struct vc4_hvs {
+ unsigned int enabled: 1;
+ } eof_irq[HVS_NUM_CHANNELS];
+
++ bool bg_fill[HVS_NUM_CHANNELS];
++
+ unsigned long max_core_rate;
+
+ /* Memory manager for CRTCs to allocate space in the display
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1470,14 +1470,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ /* This sets a black background color fill, as is the case
+ * with other DRM drivers.
+ */
+- if (enable_bg_fill)
+- HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+- HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
+- SCALER6(DISPX_CTRL1_BGENB));
+- else
+- HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+- HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
+- ~SCALER6(DISPX_CTRL1_BGENB));
++ hvs->bg_fill[channel] = enable_bg_fill;
+ } else {
+ /* we can actually run with a lower core clock when background
+ * fill is enabled on VC4_GEN_5 so leave it enabled always.
+@@ -1662,6 +1655,15 @@ static irqreturn_t vc6_hvs_eof_irq_handl
+ if (hvs->eof_irq[i].desc != irq)
+ continue;
+
++ if (hvs->bg_fill[i])
++ HVS_WRITE(SCALER6_DISPX_CTRL1(i),
++ HVS_READ(SCALER6_DISPX_CTRL1(i)) |
++ SCALER6(DISPX_CTRL1_BGENB));
++ else
++ HVS_WRITE(SCALER6_DISPX_CTRL1(i),
++ HVS_READ(SCALER6_DISPX_CTRL1(i)) &
++ ~SCALER6(DISPX_CTRL1_BGENB));
++
+ vc4_hvs_schedule_dlist_sweep(hvs, i);
+ return IRQ_HANDLED;
+ }
--- a/drivers/misc/rp1-pio.c
+++ b/drivers/misc/rp1-pio.c
-@@ -479,6 +479,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi
+@@ -476,6 +476,28 @@ int rp1_pio_sm_set_dmactrl(struct rp1_pi
}
EXPORT_SYMBOL_GPL(rp1_pio_sm_set_dmactrl);
int rp1_pio_gpio_init(struct rp1_pio_client *client, void *param)
{
struct rp1_gpio_init_args *args = param;
-@@ -851,6 +873,8 @@ struct handler_info {
+@@ -848,6 +870,8 @@ struct handler_info {
HANDLER(SM_PUT, sm_put),
HANDLER(SM_GET, sm_get),
HANDLER(SM_SET_DMACTRL, sm_set_dmactrl),
--- /dev/null
+From fa6ad4bcad4e8db18493a4af640b4b5c95434e70 Mon Sep 17 00:00:00 2001
+From: Just a nerd <157698061+foonerd@users.noreply.github.com>
+Date: Wed, 20 Nov 2024 14:08:48 +0000
+Subject: [PATCH] overlays: Enable Raspberry Touch 2 rotation with overlay
+
+See: https://github.com/raspberrypi/linux/pull/6480
+Signed-off-by: foonerd <foonerd@github.com>
+---
+ arch/arm/boot/dts/overlays/README | 1 +
+ arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5249,6 +5249,7 @@ Params: sizex Touchscr
+ invy Touchscreen inverted y axis
+ swapxy Touchscreen swapped x y axis
+ disable_touch Disables the touch screen overlay driver
++ rotation Display rotation {0,90,180,270} (default 0)
+ dsi0 Use DSI0 and i2c_csi_dsi0 (rather than
+ the default DSI1 and i2c_csi_dsi).
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-ili9881-7inch-overlay.dts
+@@ -118,5 +118,6 @@
+ invy = <0>, "+11";
+ swapxy = <>911>,"touchscreen-swapped-x-y?";
+ disable_touch = <>911>, "status=disabled";
++ rotation = <&dsi_panel>, "rotation:0";
+ };
+ };
--- a/include/linux/pio_rp1.h
+++ b/include/linux/pio_rp1.h
-@@ -245,7 +245,7 @@ static inline bool pio_can_add_program_a
+@@ -247,7 +247,7 @@ static inline bool pio_can_add_program_a
return !rp1_pio_can_add_program(client, &args);
}
{
struct rp1_pio_add_program_args args;
int offset;
-@@ -365,7 +365,7 @@ static inline int pio_sm_set_config(stru
+@@ -367,7 +367,7 @@ static inline int pio_sm_set_config(stru
return rp1_pio_sm_set_config(client, &args);
}
{
struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = false };
-@@ -375,7 +375,7 @@ int pio_sm_exec(struct rp1_pio_client *c
+@@ -377,7 +377,7 @@ int pio_sm_exec(struct rp1_pio_client *c
return rp1_pio_sm_exec(client, &args);
}
--- /dev/null
+From 8a6f640708627ac8ebf79f88793038933f169198 Mon Sep 17 00:00:00 2001
+From: Giedrius <giedrius@blokas.io>
+Date: Thu, 21 Nov 2024 08:04:02 +0000
+Subject: [PATCH] Adding Pimidi kernel module.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Giedrius Trainavičius <giedrius@blokas.io>
+---
+ sound/drivers/Kconfig | 10 +
+ sound/drivers/Makefile | 2 +
+ sound/drivers/pimidi.c | 1113 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1125 insertions(+)
+ create mode 100644 sound/drivers/pimidi.c
+
+--- a/sound/drivers/Kconfig
++++ b/sound/drivers/Kconfig
+@@ -263,4 +263,14 @@ config SND_AC97_POWER_SAVE_DEFAULT
+
+ See SND_AC97_POWER_SAVE for more details.
+
++config SND_PIMIDI
++ tristate "Pimidi driver"
++ depends on SND_SEQUENCER && CRC8
++ select SND_RAWMIDI
++ help
++ Say Y here to include support for Blokas Pimidi.
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-pimidi.
++
+ endif # SND_DRIVERS
+--- a/sound/drivers/Makefile
++++ b/sound/drivers/Makefile
+@@ -9,6 +9,7 @@ snd-aloop-objs := aloop.o
+ snd-mtpav-objs := mtpav.o
+ snd-mts64-objs := mts64.o
+ snd-pcmtest-objs := pcmtest.o
++snd-pimidi-objs := pimidi.o
+ snd-portman2x4-objs := portman2x4.o
+ snd-serial-u16550-objs := serial-u16550.o
+ snd-serial-generic-objs := serial-generic.o
+@@ -23,6 +24,7 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-s
+ obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o
+ obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+ obj-$(CONFIG_SND_MTS64) += snd-mts64.o
++obj-$(CONFIG_SND_PIMIDI) += snd-pimidi.o
+ obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
+
+ obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
+--- /dev/null
++++ b/sound/drivers/pimidi.c
+@@ -0,0 +1,1113 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Pimidi Linux kernel module.
++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the
++ * License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/completion.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/irq.h>
++#include <linux/irqdesc.h>
++#include <linux/bitops.h>
++#include <linux/of_irq.h>
++#include <linux/kfifo.h>
++#include <linux/list.h>
++#include <linux/workqueue.h>
++#include <linux/gpio.h>
++#include <linux/gpio/consumer.h>
++#include <linux/interrupt.h>
++#include <linux/mutex.h>
++#include <linux/refcount.h>
++#include <linux/crc8.h>
++#include <linux/delay.h>
++
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/rawmidi.h>
++#include <sound/asequencer.h>
++#include <sound/info.h>
++
++#define PIMIDI_LOG_IMPL(instance, log_func, msg, ...) log_func("pimidi(%s)[%c]: " msg "\n", \
++ __func__, (instance) ? (instance)->d + '0' : 'G', ## __VA_ARGS__)
++
++#ifdef PIMIDI_DEBUG
++# define printd(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert, __VA_ARGS__)
++# define printd_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_alert_ratelimited, __VA_ARGS__)
++# define printd_g(...) printd((struct pimidi_instance *)NULL, __VA_ARGS__)
++#else
++# define printd(instance, ...) do {} while (0)
++# define printd_rl(instance, ...) do {} while (0)
++# define printd_g(...) do {} while (0)
++#endif
++
++#define printe(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err, __VA_ARGS__)
++#define printe_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_err_ratelimited, __VA_ARGS__)
++#define printi(instance, ...) PIMIDI_LOG_IMPL(instance, pr_info, __VA_ARGS__)
++#define printw(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn, __VA_ARGS__)
++#define printw_rl(instance, ...) PIMIDI_LOG_IMPL(instance, pr_warn_ratelimited, __VA_ARGS__)
++
++#define printe_g(...) printe((struct pimidi_instance *)NULL, __VA_ARGS__)
++#define printi_g(...) printi((struct pimidi_instance *)NULL, __VA_ARGS__)
++
++DECLARE_CRC8_TABLE(pimidi_crc8_table);
++enum { PIMIDI_CRC8_POLYNOMIAL = 0x83 };
++enum { PIMIDI_MAX_DEVICES = 4 };
++enum { PIMIDI_MAX_PACKET_SIZE = 17 };
++enum { PIMIDI_PORTS = 2 };
++
++struct pimidi_shared {
++ // lock protects the shared reset_gpio and devices list.
++ struct mutex lock;
++ struct gpio_desc *reset_gpio;
++ struct workqueue_struct *work_queue;
++ struct list_head devices;
++};
++
++static struct pimidi_shared pimidi_global = {
++ .devices = LIST_HEAD_INIT(pimidi_global.devices),
++};
++
++struct pimidi_version_t {
++ u8 hwrev;
++ u8 major;
++ u8 minor;
++ u8 build;
++};
++
++enum { PIMIDI_IN_FIFO_SIZE = 4096 };
++
++struct pimidi_midi_port {
++ // in_lock protects the input substream.
++ struct mutex in_lock;
++ // out_lock protects the output substream.
++ struct mutex out_lock;
++ DECLARE_KFIFO(in_fifo, uint8_t, PIMIDI_IN_FIFO_SIZE);
++ unsigned int last_output_at;
++ unsigned int output_buffer_used_in_millibytes;
++ struct work_struct in_handler;
++ struct delayed_work out_handler;
++ unsigned long enabled_streams;
++ unsigned int tx_cnt;
++ unsigned int rx_cnt;
++};
++
++struct pimidi_instance {
++ struct list_head list;
++ struct i2c_client *i2c_client;
++ struct pimidi_version_t version;
++ char serial[11];
++ char d;
++ struct gpio_desc *data_ready_gpio;
++
++ struct work_struct drdy_handler;
++
++ // comm_lock serializes I2C communication.
++ struct mutex comm_lock;
++ char *rx_buf;
++ size_t rx_len;
++ int rx_status;
++ struct completion *rx_completion;
++
++ struct snd_rawmidi *rawmidi;
++ struct pimidi_midi_port midi_port[PIMIDI_PORTS];
++ bool stopping;
++};
++
++static struct snd_rawmidi_substream *pimidi_find_substream(struct snd_rawmidi *rawmidi,
++ int stream,
++ int number
++ )
++{
++ struct snd_rawmidi_substream *substream;
++
++ list_for_each_entry(substream, &rawmidi->streams[stream].substreams, list) {
++ if (substream->number == number)
++ return substream;
++ }
++ return NULL;
++}
++
++static void pimidi_midi_in_handler(struct pimidi_instance *instance, int port)
++{
++ int i, n, err;
++
++ printd(instance, "(%d)", port);
++
++ struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++ if (!test_bit(SNDRV_RAWMIDI_STREAM_INPUT, &midi_port->enabled_streams)) {
++ printd(instance, "Input not enabled for %d", port);
++ return;
++ }
++
++ u8 data[512];
++
++ n = kfifo_out_peek(&midi_port->in_fifo, data, sizeof(data));
++ printd(instance, "Peeked %d MIDI bytes", n);
++
++ mutex_lock(&midi_port->in_lock);
++ struct snd_rawmidi_substream *substream =
++ pimidi_find_substream(instance->rawmidi,
++ SNDRV_RAWMIDI_STREAM_INPUT,
++ port);
++
++ err = snd_rawmidi_receive(substream, data, n);
++ if (err > 0)
++ midi_port->rx_cnt += err;
++ mutex_unlock(&midi_port->in_lock);
++
++ for (i = 0; i < err; ++i)
++ kfifo_skip(&midi_port->in_fifo);
++
++ if (n != err)
++ printw_rl(instance,
++ "Not all MIDI data consumed for port %d: %d / %d", port, err, n);
++
++ if (!kfifo_is_empty(&midi_port->in_fifo) && !instance->stopping)
++ queue_work(pimidi_global.work_queue, &midi_port->in_handler);
++
++ printd(instance, "Done");
++}
++
++static void pimidi_midi_in_handler_0(struct work_struct *work)
++{
++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[0].in_handler),
++ 0);
++}
++
++static void pimidi_midi_in_handler_1(struct work_struct *work)
++{
++ pimidi_midi_in_handler(container_of(work, struct pimidi_instance, midi_port[1].in_handler),
++ 1);
++}
++
++static void pimidi_midi_out_handler(struct pimidi_instance *instance, int port)
++{
++ printd(instance, "(%d)", port);
++ if (!test_bit(SNDRV_RAWMIDI_STREAM_OUTPUT, &instance->midi_port[port].enabled_streams)) {
++ printd(instance, "Output not enabled for %d", port);
++ return;
++ }
++
++ struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++ struct snd_rawmidi_substream *substream =
++ pimidi_find_substream(instance->rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, port);
++
++ mutex_lock(&midi_port->out_lock);
++
++ enum { MIDI_MILLI_BYTES_PER_JIFFY = 3125000 / HZ };
++ enum { MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES =
++ (512 - PIMIDI_MAX_PACKET_SIZE - 1) * 1000 };
++
++ unsigned int now = jiffies;
++ unsigned int millibytes_became_available =
++ (MIDI_MILLI_BYTES_PER_JIFFY) * (now - midi_port->last_output_at);
++
++ midi_port->output_buffer_used_in_millibytes =
++ midi_port->output_buffer_used_in_millibytes <=
++ millibytes_became_available ? 0 : midi_port->output_buffer_used_in_millibytes -
++ millibytes_became_available;
++
++ unsigned int output_buffer_available =
++ (MIDI_MAX_OUTPUT_BUFFER_SIZE_IN_MILLIBYTES
++ - midi_port->output_buffer_used_in_millibytes)
++ / 1000;
++
++ u8 buffer[PIMIDI_MAX_PACKET_SIZE];
++ int n, batch, err;
++
++ for (batch = 0; batch < 3; ++batch) {
++ if (output_buffer_available == 0)
++ printd(instance, "Buffer full");
++
++ printd(instance, "Buffer available: %u (%u +%u, %u -> %u, dt %u) (%u) @ %u",
++ output_buffer_available, midi_port->output_buffer_used_in_millibytes,
++ millibytes_became_available, midi_port->last_output_at, now,
++ now - midi_port->last_output_at, midi_port->tx_cnt, HZ);
++ midi_port->last_output_at = now;
++
++ n = output_buffer_available
++ ? snd_rawmidi_transmit_peek(substream, buffer + 1,
++ min(output_buffer_available,
++ sizeof(buffer) - 2))
++ : 0;
++ if (n > 0) {
++ printd(instance, "Peeked: %d", n);
++ snd_rawmidi_transmit_ack(substream, n);
++
++ buffer[0] = (port << 4) | n;
++ buffer[n + 1] = ~crc8(pimidi_crc8_table, buffer, n + 1, CRC8_INIT_VALUE);
++
++#ifdef PIMIDI_DEBUG
++ pr_debug("%s[%d]: Sending %d bytes:", __func__, instance->d, n + 2);
++ int i;
++
++ for (i = 0; i < n + 2; ++i)
++ pr_cont(" %02x", buffer[i]);
++
++ pr_cont("\n");
++#endif
++ mutex_lock(&instance->comm_lock);
++ err = i2c_master_send(instance->i2c_client, buffer, n + 2);
++ mutex_unlock(&instance->comm_lock);
++
++ if (err < 0) {
++ printe(instance,
++ "Error occurred when sending MIDI data over I2C! (%d)",
++ err);
++ goto cleanup;
++ }
++
++ midi_port->tx_cnt += n;
++ midi_port->output_buffer_used_in_millibytes += n * 1000;
++ output_buffer_available -= n;
++ } else if (n < 0) {
++ err = n;
++ printe(instance, "snd_rawmidi_transmit_peek returned error %d!", err);
++ goto cleanup;
++ } else {
++ break;
++ }
++ }
++
++ printd(instance, "Checking if empty %p", substream);
++ if (!snd_rawmidi_transmit_empty(substream) && !instance->stopping) {
++ unsigned int delay = 1;
++
++ if (output_buffer_available == 0)
++ delay = 125000 / MIDI_MILLI_BYTES_PER_JIFFY;
++ printd(instance, "Queue more work after %u jiffies", delay);
++ mod_delayed_work(pimidi_global.work_queue, &midi_port->out_handler, delay);
++ }
++
++cleanup:
++ mutex_unlock(&midi_port->out_lock);
++ printd(instance, "Done");
++}
++
++static void pimidi_midi_out_handler_0(struct work_struct *work)
++{
++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance,
++ midi_port[0].out_handler.work), 0);
++}
++
++static void pimidi_midi_out_handler_1(struct work_struct *work)
++{
++ pimidi_midi_out_handler(container_of(work, struct pimidi_instance,
++ midi_port[1].out_handler.work), 1);
++}
++
++static void pimidi_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
++{
++ struct pimidi_instance *instance = substream->rmidi->private_data;
++
++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up);
++
++ if (up == 0) {
++ clear_bit(substream->stream,
++ &instance->midi_port[substream->number].enabled_streams);
++ } else {
++ set_bit(substream->stream,
++ &instance->midi_port[substream->number].enabled_streams);
++ if (!delayed_work_pending(&instance->midi_port[substream->number].out_handler)) {
++ printd(instance, "Queueing work");
++ queue_delayed_work(pimidi_global.work_queue,
++ &instance->midi_port[substream->number].out_handler, 0);
++ }
++ }
++}
++
++static void pimidi_midi_output_drain(struct snd_rawmidi_substream *substream)
++{
++ struct pimidi_instance *instance = substream->rmidi->private_data;
++
++ printd(instance, "(%d, %d)", substream->stream, substream->number);
++
++ printd(instance, "Begin draining!");
++
++ queue_delayed_work(pimidi_global.work_queue,
++ &instance->midi_port[substream->number].out_handler, 0);
++
++ unsigned long deadline = jiffies + 5 * HZ;
++
++ do {
++ printd(instance, "Before flush");
++ while (delayed_work_pending(&instance->midi_port[substream->number].out_handler))
++ flush_delayed_work(&instance->midi_port[substream->number].out_handler);
++ printd(instance, "Flushed");
++ } while (!snd_rawmidi_transmit_empty(substream) && time_before(jiffies, deadline));
++
++ printd(instance, "Done!");
++}
++
++static int pimidi_midi_output_close(struct snd_rawmidi_substream *substream)
++{
++ struct pimidi_instance *instance = substream->rmidi->private_data;
++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number];
++
++ mutex_lock(&midi_port->out_lock);
++ clear_bit(substream->stream, &midi_port->enabled_streams);
++ mutex_unlock(&midi_port->out_lock);
++ return 0;
++}
++
++static int pimidi_midi_input_close(struct snd_rawmidi_substream *substream)
++{
++ struct pimidi_instance *instance = substream->rmidi->private_data;
++ struct pimidi_midi_port *midi_port = &instance->midi_port[substream->number];
++
++ mutex_lock(&midi_port->in_lock);
++ clear_bit(substream->stream, &midi_port->enabled_streams);
++ mutex_unlock(&midi_port->in_lock);
++ return 0;
++}
++
++static void pimidi_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
++{
++ struct pimidi_instance *instance = substream->rmidi->private_data;
++
++ printd(instance, "(%d, %d, %d)", substream->stream, substream->number, up);
++
++ if (up == 0) {
++ clear_bit(substream->stream,
++ &instance->midi_port[substream->number].enabled_streams);
++ cancel_work_sync(&instance->midi_port[substream->number].in_handler);
++ } else {
++ set_bit(substream->stream,
++ &instance->midi_port[substream->number].enabled_streams);
++ if (!instance->stopping)
++ queue_work(pimidi_global.work_queue,
++ &instance->midi_port[substream->number].in_handler);
++ }
++}
++
++static void pimidi_get_port_info(struct snd_rawmidi *rmidi, int number,
++ struct snd_seq_port_info *seq_port_info)
++{
++ printd_g("%p, %d, %p", rmidi, number, seq_port_info);
++ seq_port_info->type =
++ SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
++ SNDRV_SEQ_PORT_TYPE_HARDWARE |
++ SNDRV_SEQ_PORT_TYPE_PORT;
++ strscpy(seq_port_info->name, number == 0 ? "a" : "b",
++ sizeof(seq_port_info->name));
++ seq_port_info->midi_voices = 0;
++}
++
++static const struct snd_rawmidi_global_ops pimidi_midi_ops = {
++ .get_port_info = pimidi_get_port_info,
++};
++
++static int pimidi_midi_open(struct snd_rawmidi_substream *substream)
++{
++ printd_g("(%p) stream=%d number=%d", substream, substream->stream, substream->number);
++ return 0;
++}
++
++static const struct snd_rawmidi_ops pimidi_midi_output_ops = {
++ .open = pimidi_midi_open,
++ .close = pimidi_midi_output_close,
++ .trigger = pimidi_midi_output_trigger,
++ .drain = pimidi_midi_output_drain,
++};
++
++static const struct snd_rawmidi_ops pimidi_midi_input_ops = {
++ .open = pimidi_midi_open,
++ .close = pimidi_midi_input_close,
++ .trigger = pimidi_midi_input_trigger,
++};
++
++static int pimidi_register(struct pimidi_instance *instance)
++{
++ int err = 0;
++
++ mutex_lock(&pimidi_global.lock);
++ printd(instance, "Registering...");
++ if (!pimidi_global.reset_gpio) {
++ printd_g("Getting reset pin.");
++ pimidi_global.reset_gpio = gpiod_get(&instance->i2c_client->dev, "reset",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(pimidi_global.reset_gpio)) {
++ err = PTR_ERR(pimidi_global.reset_gpio);
++ printe_g("gpiod_get failed: %d", err);
++ pimidi_global.reset_gpio = NULL;
++ mutex_unlock(&pimidi_global.lock);
++ return err;
++ }
++ }
++ list_add_tail(&instance->list, &pimidi_global.devices);
++ mutex_unlock(&pimidi_global.lock);
++ return err;
++}
++
++static void pimidi_unregister(struct pimidi_instance *instance)
++{
++ mutex_lock(&pimidi_global.lock);
++ printd(instance, "Unregistering...");
++ list_del(&instance->list);
++ if (list_empty(&pimidi_global.devices)) {
++ printd_g("Releasing reset pin");
++ gpiod_put(pimidi_global.reset_gpio);
++ pimidi_global.reset_gpio = NULL;
++ }
++ mutex_unlock(&pimidi_global.lock);
++}
++
++static void pimidi_perform_reset(void)
++{
++ mutex_lock(&pimidi_global.lock);
++
++ printd_g("Performing reset.");
++
++ struct list_head *p;
++
++ list_for_each(p, &pimidi_global.devices) {
++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++ printd(instance, "Pausing...");
++ instance->stopping = true;
++ disable_irq(instance->i2c_client->irq);
++ cancel_work(&instance->drdy_handler);
++
++ int i;
++
++ for (i = 0; i < PIMIDI_PORTS; ++i) {
++ cancel_work(&instance->midi_port[i].in_handler);
++ cancel_delayed_work(&instance->midi_port[i].out_handler);
++ }
++
++ drain_workqueue(pimidi_global.work_queue);
++ }
++
++ printd_g("Reset = low");
++ gpiod_set_value(pimidi_global.reset_gpio, 1);
++
++ list_for_each(p, &pimidi_global.devices) {
++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++ if (gpiod_is_active_low(instance->data_ready_gpio))
++ gpiod_toggle_active_low(instance->data_ready_gpio);
++ gpiod_direction_output(instance->data_ready_gpio, 1);
++ printd(instance, "DRDY high");
++ }
++
++ usleep_range(1000, 5000);
++ printd_g("Reset = high");
++ gpiod_set_value(pimidi_global.reset_gpio, 0);
++ msleep(30);
++
++ int i;
++
++ for (i = 0; i < PIMIDI_MAX_DEVICES; ++i) {
++ usleep_range(1000, 3000);
++ list_for_each(p, &pimidi_global.devices) {
++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance,
++ list);
++
++ if (instance->d < i)
++ continue;
++ printd(instance, "DRDY -> %d", !gpiod_get_value(instance->data_ready_gpio));
++ gpiod_set_value(instance->data_ready_gpio,
++ !gpiod_get_value(instance->data_ready_gpio));
++ }
++ }
++ usleep_range(16000, 20000);
++
++ list_for_each(p, &pimidi_global.devices) {
++ struct pimidi_instance *instance = list_entry(p, struct pimidi_instance, list);
++
++ if (!gpiod_is_active_low(instance->data_ready_gpio))
++ gpiod_toggle_active_low(instance->data_ready_gpio);
++
++ printd(instance, "DRDY input");
++ gpiod_direction_input(instance->data_ready_gpio);
++
++ printd(instance, "Resume...");
++ instance->stopping = false;
++ enable_irq(instance->i2c_client->irq);
++ }
++
++ printd_g("Reset done.");
++ usleep_range(16000, 20000);
++
++ mutex_unlock(&pimidi_global.lock);
++}
++
++static int pimidi_read_version(struct pimidi_version_t *version, struct pimidi_instance *instance)
++{
++ memset(version, 0, sizeof(*version));
++
++ const char cmd[4] = { 0xb2, 0x01, 0x01, 0x95 };
++
++ char result[9];
++
++ memset(result, 0, sizeof(result));
++
++ DECLARE_COMPLETION_ONSTACK(done);
++
++ mutex_lock(&instance->comm_lock);
++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd));
++
++ if (err < 0) {
++ mutex_unlock(&instance->comm_lock);
++ return err;
++ }
++ instance->rx_buf = result;
++ instance->rx_len = sizeof(result);
++ instance->rx_completion = &done;
++ mutex_unlock(&instance->comm_lock);
++
++ printd(instance, "Waiting for drdy");
++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u));
++ printd(instance, "Done waiting");
++
++ if (!completion_done(&done)) {
++ mutex_lock(&instance->comm_lock);
++ instance->rx_buf = NULL;
++ instance->rx_len = 0;
++ instance->rx_status = -ETIMEDOUT;
++ instance->rx_completion = NULL;
++ mutex_unlock(&instance->comm_lock);
++ return -ETIMEDOUT;
++ }
++
++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result, sizeof(result),
++ CRC8_INIT_VALUE))
++ return -EIO;
++
++ const char expected[4] = { 0xb7, 0x81, 0x01, 0x00 };
++
++ if (memcmp(result, expected, sizeof(expected)) != 0)
++ return -EPROTO;
++
++ u32 v = ntohl(*(uint32_t *)(result + 4));
++
++ version->hwrev = v >> 24;
++ version->major = (v & 0x00ff0000) >> 16;
++ version->minor = (v & 0x0000ff00) >> 8;
++ version->build = v & 0x000000ff;
++
++ return 0;
++}
++
++static int pimidi_read_serial(char serial[11], struct pimidi_instance *instance)
++{
++ memset(serial, 0, sizeof(char[11]));
++
++ const char cmd[4] = { 0xb2, 0x03, 0x04, 0x97 };
++
++ char result[PIMIDI_MAX_PACKET_SIZE];
++
++ memset(result, 0, sizeof(result));
++
++ DECLARE_COMPLETION_ONSTACK(done);
++
++ mutex_lock(&instance->comm_lock);
++ int err = i2c_master_send(instance->i2c_client, cmd, sizeof(cmd));
++
++ if (err < 0) {
++ mutex_unlock(&instance->comm_lock);
++ return err;
++ }
++ instance->rx_buf = result;
++ instance->rx_len = sizeof(result);
++ instance->rx_completion = &done;
++ mutex_unlock(&instance->comm_lock);
++
++ printd(instance, "Waiting for drdy");
++ wait_for_completion_io_timeout(&done, msecs_to_jiffies(1000u));
++ printd(instance, "Done waiting");
++
++ if (!completion_done(&done)) {
++ mutex_lock(&instance->comm_lock);
++ instance->rx_buf = NULL;
++ instance->rx_len = 0;
++ instance->rx_status = -ETIMEDOUT;
++ instance->rx_completion = NULL;
++ mutex_unlock(&instance->comm_lock);
++ printe(instance, "Timed out");
++ return -ETIMEDOUT;
++ }
++
++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) != crc8(pimidi_crc8_table, result,
++ (result[0] & 0x0f) + 2, CRC8_INIT_VALUE))
++ return -EIO;
++
++ const char expected[4] = { 0xbd, 0x83, 0x04, 0x0a };
++
++ if (memcmp(result, expected, sizeof(expected)) != 0) {
++ printe(instance, "Unexpected response: %02x %02x %02x %02x", result[0], result[1],
++ result[2], result[3]);
++ return -EPROTO;
++ }
++
++ memcpy(serial, result + 4, 10);
++
++ if (strspn(serial, "\xff") == 10)
++ strscpy(serial, "(unset)", 8);
++
++ return 0;
++}
++
++static void pimidi_handle_midi_data(struct pimidi_instance *instance, int port, const uint8_t *data,
++ unsigned int n)
++{
++ printd(instance, "Handling MIDI data for port %d (%u bytes)", port, n);
++ if (n == 0)
++ return;
++
++ struct pimidi_midi_port *midi_port = &instance->midi_port[port];
++
++ kfifo_in(&midi_port->in_fifo, data, n);
++
++ if (!instance->stopping)
++ queue_work(pimidi_global.work_queue, &midi_port->in_handler);
++
++ printd(instance, "Done");
++}
++
++static void pimidi_drdy_continue(struct pimidi_instance *instance)
++{
++ if (instance->stopping) {
++ printd(instance, "Refusing to queue work / enable IRQ due to stopping.");
++ return;
++ }
++
++ if (gpiod_get_value(instance->data_ready_gpio)) {
++ printd_rl(instance, "Queue work due to DRDY line still low");
++ queue_work(pimidi_global.work_queue, &instance->drdy_handler);
++ } else {
++ printd_rl(instance, "Enabling irq for more data");
++ enable_irq(gpiod_to_irq(instance->data_ready_gpio));
++ }
++}
++
++static void pimidi_drdy_handler(struct work_struct *work)
++{
++ struct pimidi_instance *instance = container_of(work, struct pimidi_instance, drdy_handler);
++
++ printd(instance, "(%p)", work);
++
++ mutex_lock(&instance->comm_lock);
++ if (!instance->rx_completion) {
++ u8 data[PIMIDI_MAX_PACKET_SIZE];
++ int n = i2c_master_recv(instance->i2c_client, data, 3);
++
++ if (n < 0) {
++ printe(instance, "Error reading from device: %d", n);
++ mutex_unlock(&instance->comm_lock);
++ pimidi_drdy_continue(instance);
++ return;
++ }
++
++ if (data[0] == 0xfe) {
++ printe_rl(instance, "Invalid packet 0x%02x 0x%02x 0x%02x", data[0], data[1],
++ data[2]);
++ mutex_unlock(&instance->comm_lock);
++ pimidi_drdy_continue(instance);
++ return;
++ }
++
++ int len = (data[0] & 0x0f) + 2;
++
++ if (len > n) {
++ printd(instance, "Need %d more bytes", len - n);
++ int err = i2c_master_recv(instance->i2c_client, data + n, len - n);
++
++ if (err < 0) {
++ printe(instance, "Error reading remainder from device: %d", err);
++ mutex_unlock(&instance->comm_lock);
++ pimidi_drdy_continue(instance);
++ return;
++#ifdef PIMIDI_DEBUG
++ } else {
++ pr_debug("Recv_2:");
++ int i;
++
++ for (i = n; i < len; ++i)
++ pr_cont(" %02x", data[i]);
++ pr_cont("\n");
++#endif
++ }
++ }
++
++ if (CRC8_GOOD_VALUE(pimidi_crc8_table) == crc8(pimidi_crc8_table, data, len,
++ CRC8_INIT_VALUE)) {
++ switch (data[0] & 0xf0) {
++ case 0x00:
++ pimidi_handle_midi_data(instance, 0, data + 1, len - 2);
++ break;
++ case 0x10:
++ pimidi_handle_midi_data(instance, 1, data + 1, len - 2);
++ break;
++ default:
++ printd(instance, "Unhandled command %02x", data[0]);
++ break;
++ }
++ } else {
++ printe(instance, "I2C rx corruption detected.");
++ pr_info("Packet [%d]:", len);
++ int i;
++
++ for (i = 0; i < len; ++i)
++ pr_cont(" %02x", data[i]);
++ pr_cont("\n");
++ }
++
++ mutex_unlock(&instance->comm_lock);
++ } else {
++ printd(instance, "Completing drdy");
++ instance->rx_status = i2c_master_recv(instance->i2c_client, instance->rx_buf, 3);
++ printd(instance, "Recv_1 %02x %02x %02x", instance->rx_buf[0], instance->rx_buf[1],
++ instance->rx_buf[2]);
++ if (instance->rx_len > 3 && instance->rx_status == 3) {
++ instance->rx_status = i2c_master_recv(instance->i2c_client,
++ instance->rx_buf + 3,
++ instance->rx_len - 3);
++ if (instance->rx_status >= 0)
++ instance->rx_status += 3;
++#ifdef PIMIDI_DEBUG
++ pr_debug("Recv_2:");
++ int i;
++
++ for (i = 3; i < instance->rx_len; ++i)
++ pr_cont(" %02x", instance->rx_buf[i]);
++ pr_cont("\n");
++#endif
++ }
++ struct completion *done = instance->rx_completion;
++
++ instance->rx_buf = NULL;
++ instance->rx_len = 0;
++ instance->rx_completion = NULL;
++ complete_all(done);
++ mutex_unlock(&instance->comm_lock);
++ }
++
++ pimidi_drdy_continue(instance);
++}
++
++static irqreturn_t pimidi_drdy_interrupt_handler(int irq, void *dev_id)
++{
++ struct pimidi_instance *instance = (struct pimidi_instance *)dev_id;
++
++ if (instance->stopping) {
++ printd(instance, "DRDY interrupt, but stopping, ignoring...");
++ return IRQ_HANDLED;
++ }
++
++ printd(instance, "DRDY interrupt, masking");
++ disable_irq_nosync(irq);
++
++ printd(instance, "Queue work due to DRDY interrupt");
++ queue_work(pimidi_global.work_queue, &instance->drdy_handler);
++
++ return IRQ_HANDLED;
++}
++
++static void pimidi_proc_stat_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++ const unsigned int *d = entry->private_data;
++
++ snd_iprintf(buffer, "%u\n", *d);
++}
++
++static void pimidi_proc_serial_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++ struct pimidi_instance *instance = entry->private_data;
++
++ snd_iprintf(buffer, "%s\n", instance->serial);
++}
++
++static void pimidi_proc_version_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++ struct pimidi_instance *instance = entry->private_data;
++
++ snd_iprintf(buffer, "%u.%u.%u\n", instance->version.major, instance->version.minor,
++ instance->version.build);
++}
++
++static void pimidi_proc_hwrev_show(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
++{
++ struct pimidi_instance *instance = entry->private_data;
++
++ snd_iprintf(buffer, "%u\n", instance->version.hwrev);
++}
++
++static int pimidi_i2c_probe(struct i2c_client *client)
++{
++ struct snd_card *card = NULL;
++ int err, d, i;
++
++ d = client->addr - 0x20;
++
++ if (d < 0 || d >= 8) {
++ printe_g("Unexpected device address: %d", client->addr);
++ err = -EINVAL;
++ goto finalize;
++ }
++
++ err = snd_card_new(&client->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE,
++ sizeof(struct pimidi_instance), &card);
++
++ if (err) {
++ printe_g("snd_card_new failed: %d", err);
++ return err;
++ }
++
++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data;
++
++ instance->i2c_client = client;
++ instance->d = d;
++
++ struct snd_rawmidi *rawmidi;
++
++ err = snd_rawmidi_new(card, card->shortname, 0, 2, 2, &rawmidi);
++ if (err < 0) {
++ printe(instance, "snd_rawmidi_new failed: %d", err);
++ goto finalize;
++ }
++
++ instance->rawmidi = rawmidi;
++ strscpy(rawmidi->name, "pimidi", sizeof(rawmidi->name));
++
++ rawmidi->info_flags =
++ SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
++ rawmidi->private_data = instance;
++ rawmidi->ops = &pimidi_midi_ops;
++
++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &pimidi_midi_output_ops);
++ snd_rawmidi_set_ops(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pimidi_midi_input_ops);
++
++ instance->data_ready_gpio = devm_gpiod_get(&client->dev, "data-ready", GPIOD_OUT_HIGH);
++ if (IS_ERR(instance->data_ready_gpio)) {
++ err = PTR_ERR(instance->data_ready_gpio);
++ printe(instance, "devm_gpiod_get failed: %d", err);
++ goto finalize;
++ }
++
++ err = pimidi_register(instance);
++ if (err < 0) {
++ printe(instance, "pimidi_register failed: %d", err);
++ goto finalize;
++ }
++
++ pimidi_perform_reset();
++
++ INIT_WORK(&instance->drdy_handler, pimidi_drdy_handler);
++ mutex_init(&instance->comm_lock);
++
++ err = devm_request_irq(&client->dev, client->irq, pimidi_drdy_interrupt_handler,
++ IRQF_SHARED | IRQF_TRIGGER_LOW, "data_ready_int", instance);
++
++ if (err != 0) {
++ printe(instance, "data_available IRQ request failed! %d", err);
++ goto finalize;
++ }
++
++ err = pimidi_read_version(&instance->version, instance);
++ if (err < 0) {
++ printe(instance, "pimidi_read_version failed: %d", err);
++ goto finalize;
++ }
++
++ err = pimidi_read_serial(instance->serial, instance);
++ if (err < 0) {
++ printe(instance, "pimidi_read_serial failed: %d", err);
++ goto finalize;
++ } else if (instance->serial[0] != 'P' || instance->serial[1] != 'M' ||
++ strlen(instance->serial) != 10) {
++ printe(instance, "Unexpected serial number: %s", instance->serial);
++ err = -EIO;
++ goto finalize;
++ }
++
++ printi(instance, "pimidi%d hw:%d version %u.%u.%u-%u, serial %s",
++ d,
++ card->number,
++ instance->version.major,
++ instance->version.minor,
++ instance->version.build,
++ instance->version.hwrev,
++ instance->serial
++ );
++
++ strscpy(card->driver, "snd-pimidi", sizeof(card->driver));
++ snprintf(card->shortname, sizeof(card->shortname), "pimidi%d", d);
++ snprintf(card->longname, sizeof(card->longname), "pimidi%d %s", d, instance->serial);
++ snprintf(card->id, sizeof(card->id), "pimidi%d", d);
++
++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 0)->name,
++ 10u, "pimidi%d-a", d);
++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 0)->name,
++ 10u, "pimidi%d-a", d);
++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 1)->name,
++ 10u, "pimidi%d-b", d);
++ snprintf(pimidi_find_substream(rawmidi, SNDRV_RAWMIDI_STREAM_INPUT, 1)->name,
++ 10u, "pimidi%d-b", d);
++
++ err = snd_card_ro_proc_new(card, "a-tx", &instance->midi_port[0].tx_cnt,
++ pimidi_proc_stat_show);
++ err = snd_card_ro_proc_new(card, "a-rx", &instance->midi_port[0].rx_cnt,
++ pimidi_proc_stat_show);
++ err = snd_card_ro_proc_new(card, "b-tx", &instance->midi_port[1].tx_cnt,
++ pimidi_proc_stat_show);
++ err = snd_card_ro_proc_new(card, "b-rx", &instance->midi_port[1].rx_cnt,
++ pimidi_proc_stat_show);
++ err = snd_card_ro_proc_new(card, "serial", instance, pimidi_proc_serial_show);
++ err = snd_card_ro_proc_new(card, "version", instance, pimidi_proc_version_show);
++ err = snd_card_ro_proc_new(card, "hwrev", instance, pimidi_proc_hwrev_show);
++ if (err < 0) {
++ printe(instance, "snd_card_ro_proc_new failed: %d", err);
++ goto finalize;
++ }
++
++ err = snd_card_register(card);
++ if (err < 0) {
++ printe(instance, "snd_card_register failed: %d", err);
++ goto finalize;
++ }
++
++finalize:
++ if (err) {
++ instance->stopping = true;
++ cancel_work_sync(&instance->drdy_handler);
++ mutex_destroy(&instance->comm_lock);
++ pimidi_unregister(instance);
++ snd_card_free(card);
++ return err;
++ }
++
++ for (i = 0; i < PIMIDI_PORTS; ++i) {
++ struct pimidi_midi_port *port = &instance->midi_port[i];
++
++ mutex_init(&port->in_lock);
++ mutex_init(&port->out_lock);
++ INIT_WORK(&port->in_handler,
++ i == 0 ? pimidi_midi_in_handler_0 : pimidi_midi_in_handler_1);
++ INIT_DELAYED_WORK(&port->out_handler,
++ i == 0 ? pimidi_midi_out_handler_0 : pimidi_midi_out_handler_1);
++ INIT_KFIFO(port->in_fifo);
++ port->last_output_at = jiffies;
++ }
++
++ i2c_set_clientdata(client, card);
++ return 0;
++}
++
++static void pimidi_i2c_remove(struct i2c_client *client)
++{
++ printd_g("(%p)", client);
++
++ int i;
++ struct snd_card *card = i2c_get_clientdata(client);
++
++ if (card) {
++ printi_g("Unloading hw:%d %s", card->number, card->longname);
++ struct pimidi_instance *instance = (struct pimidi_instance *)card->private_data;
++
++ instance->stopping = true;
++ i2c_set_clientdata(client, NULL);
++ devm_free_irq(&client->dev, client->irq, instance);
++ cancel_work_sync(&instance->drdy_handler);
++
++ for (i = 0; i < PIMIDI_PORTS; ++i) {
++ cancel_work_sync(&instance->midi_port[i].in_handler);
++ cancel_delayed_work_sync(&instance->midi_port[i].out_handler);
++ mutex_destroy(&instance->midi_port[i].out_lock);
++ mutex_destroy(&instance->midi_port[i].in_lock);
++ kfifo_free(&instance->midi_port[i].in_fifo);
++ }
++
++ mutex_destroy(&instance->comm_lock);
++ pimidi_unregister(instance);
++ snd_card_free(card);
++ }
++}
++
++static const struct i2c_device_id pimidi_i2c_ids[] = {
++ { "pimidi", 0 },
++ {}
++};
++MODULE_DEVICE_TABLE(i2c, pimidi_i2c_ids);
++
++static const struct of_device_id pimidi_i2c_dt_ids[] = {
++ { .compatible = "blokaslabs,pimidi", },
++ {}
++};
++MODULE_DEVICE_TABLE(of, pimidi_i2c_dt_ids);
++
++static struct i2c_driver pimidi_i2c_driver = {
++ .driver = {
++ .name = "pimidi",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(pimidi_i2c_dt_ids),
++ },
++ .probe = pimidi_i2c_probe,
++ .remove = pimidi_i2c_remove,
++ .id_table = pimidi_i2c_ids,
++};
++
++int pimidi_module_init(void)
++{
++ int err = 0;
++
++ mutex_init(&pimidi_global.lock);
++
++ INIT_LIST_HEAD(&pimidi_global.devices);
++
++ pimidi_global.work_queue = create_singlethread_workqueue("pimidi");
++ if (!pimidi_global.work_queue) {
++ err = -ENOMEM;
++ goto cleanup;
++ }
++
++ err = i2c_add_driver(&pimidi_i2c_driver);
++ if (err < 0)
++ goto cleanup;
++
++ crc8_populate_msb(pimidi_crc8_table, PIMIDI_CRC8_POLYNOMIAL);
++
++ return 0;
++
++cleanup:
++ mutex_destroy(&pimidi_global.lock);
++ return err;
++}
++
++void pimidi_module_exit(void)
++{
++ i2c_del_driver(&pimidi_i2c_driver);
++ mutex_lock(&pimidi_global.lock);
++ if (pimidi_global.reset_gpio) {
++ gpiod_put(pimidi_global.reset_gpio);
++ pimidi_global.reset_gpio = NULL;
++ }
++ mutex_unlock(&pimidi_global.lock);
++
++ destroy_workqueue(pimidi_global.work_queue);
++ pimidi_global.work_queue = NULL;
++
++ mutex_destroy(&pimidi_global.lock);
++}
++
++module_init(pimidi_module_init);
++module_exit(pimidi_module_exit);
++
++MODULE_AUTHOR("Giedrius Trainavi\xc4\x8dius <giedrius@blokas.io>");
++MODULE_DESCRIPTION("MIDI driver for Blokas Pimidi, https://blokas.io/");
++MODULE_LICENSE("GPL");
++
++/* vim: set ts=8 sw=8 noexpandtab: */
--- /dev/null
+From 75ab92b077602734458f0a77e19a3599be29b93b Mon Sep 17 00:00:00 2001
+From: Giedrius <giedrius@blokas.io>
+Date: Thu, 21 Nov 2024 08:05:49 +0000
+Subject: [PATCH] Adding pimidi-overlay.dts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Giedrius Trainavičius <giedrius@blokas.io>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 8 +++
+ arch/arm/boot/dts/overlays/pimidi-overlay.dts | 54 +++++++++++++++++++
+ 3 files changed, 63 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/pimidi-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -203,6 +203,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ pifi-dac-zero.dtbo \
+ pifi-mini-210.dtbo \
+ piglow.dtbo \
++ pimidi.dtbo \
+ pineboards-hat-ai.dtbo \
+ pineboards-hatdrive-poe-plus.dtbo \
+ piscreen.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3701,6 +3701,14 @@ Load: dtoverlay=piglow
+ Params: <None>
+
+
++Name: pimidi
++Info: Configures the Blokas Labs Pimidi card
++Load: dtoverlay=pimidi,<param>=<val>
++Params: sel The position used for the sel rotary switch.
++ Each unit in the stack must be set on a unique
++ position. If param is omitted, sel=0 is assumed.
++
++
+ Name: pineboards-hat-ai
+ Info: Pineboards Hat Ai! overlay for the Google Coral Edge TPU
+ Load: dtoverlay=pineboards-hat-ai
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts
+@@ -0,0 +1,54 @@
++/*
++ * Pimidi Linux kernel module.
++ * Copyright (C) 2017-2024 Vilniaus Blokas UAB, https://blokas.io/
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the
++ * License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&i2c_arm>;
++ __overlay__ {
++ status = "okay";
++ clock-frequency=<1000000>;
++
++ pimidi_ctrl: pimidi_ctrl@20 {
++ compatible = "blokaslabs,pimidi";
++
++ reg = <0x20>;
++ status = "okay";
++
++ interrupt-parent = <&gpio>;
++ interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
++ interrupt-names = "data_ready";
++ interrupt-controller;
++ #interrupt-cells = <2>;
++
++ data-ready-gpios = <&gpio 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
++ reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
++ };
++ };
++ };
++
++ __overrides__ {
++ sel = <&pimidi_ctrl>,"reg:0{0=0x20,1=0x21,2=0x22,3=0x23}",
++ <&pimidi_ctrl>,"data-ready-gpios:4{0=23,1=5,2=6,3=27}",
++ <&pimidi_ctrl>,"interrupts:0{0=23,1=5,2=6,3=27}";
++ };
++};
-From fc5ed9d9bf0411523220bab60304da6d23257a64 Mon Sep 17 00:00:00 2001
+From a1e4b72997dc3ef423b6f510bfead470475750d4 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 1 Nov 2018 17:31:37 +0000
-Subject: [PATCH 0297/1085] staging: vchiq_arm: Add 36-bit address support
+Subject: [PATCH] staging: vchiq_arm: Add 36-bit address support
Conditional on a new compatible string, change the pagelist encoding
such that the top 24 bits are the pfn, leaving 8 bits for run length
-From d4712f611e6d60dd9cf09df581f5df6fad6a2207 Mon Sep 17 00:00:00 2001
+From 1129091b2d95273d930acf2926a569b90512a248 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Tue, 21 Jul 2020 17:34:09 +0100
-Subject: [PATCH 0298/1085] staging: vchiq_arm: children inherit DMA config
+Subject: [PATCH] staging: vchiq_arm: children inherit DMA config
Although it is no longer necessary for vchiq's children to have a
different DMA configuration to the parent, they do still need to
-From 9f328c347fc9a5495b8383aa2bae1d3bc242a2ab Mon Sep 17 00:00:00 2001
+From 2d26a598ceceaea8a6837146c741eb742bbd4baa Mon Sep 17 00:00:00 2001
From: detule <ogjoneski@gmail.com>
Date: Tue, 2 Oct 2018 04:10:08 -0400
-Subject: [PATCH 0299/1085] staging: vchiq_arm: Usa a DMA pool for small bulks
+Subject: [PATCH] staging: vchiq_arm: Usa a DMA pool for small bulks
During a bulk transfer we request a DMA allocation to hold the
scatter-gather list. Most of the time, this allocation is small
-From 79f24f7454a416fed9106c75ea9b3be480465dda Mon Sep 17 00:00:00 2001
+From 5b29221e96d1ba60a78d5c804a20fa35a6d0517a Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Fri, 29 Apr 2022 09:19:10 +0100
-Subject: [PATCH 0365/1085] staging: vchiq_arm: Add log_level module params
+Subject: [PATCH] staging: vchiq_arm: Add log_level module params
Add module parameters to control the logging levels for the various
vchiq logging categories.
--- /dev/null
+From 8691544f688bd3ae9b6db0845a75ce230fc9e90f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 21 Nov 2024 15:54:58 +0000
+Subject: [PATCH] media: i2c: imx477: Fix link frequency menu
+
+"media: i2c: imx477: Add options for slightly modifying the link freq"
+created a link frequency menu with 2 items in instead of one.
+Correct this.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2051,7 +2051,7 @@ static int imx477_init_controls(struct i
+ /* LINK_FREQ is also read only */
+ imx477->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx477_ctrl_ops,
+- V4L2_CID_LINK_FREQ, 1, 0,
++ V4L2_CID_LINK_FREQ, 0, 0,
+ &link_freqs[imx477->link_freq_idx]);
+ if (imx477->link_freq)
+ imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
--- a/include/linux/pio_rp1.h
+++ b/include/linux/pio_rp1.h
-@@ -318,7 +318,7 @@ static inline int pio_sm_unclaim(struct
+@@ -320,7 +320,7 @@ static inline int pio_sm_unclaim(struct
if (bad_params_if(client, sm >= NUM_PIO_STATE_MACHINES))
return -EINVAL;
--- /dev/null
+From 008c93b47b9b965368eb5bbfbef60b816931e0ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 20 Nov 2024 13:58:08 +0000
+Subject: [PATCH] drm: vc4: dsi: Handle the different command FIFO widths
+
+DSI0 and DSI1 have different widths for the command FIFO (24bit
+vs 32bit), but the driver was assuming the 32bit width of DSI1
+in all cases.
+DSI0 also wants the data packed as 24bit big endian, so the
+formatting code needs updating.
+
+Handle the difference via the variant structure.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_dsi.c | 64 ++++++++++++++++++++++++-----------
+ 1 file changed, 44 insertions(+), 20 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_dsi.c
++++ b/drivers/gpu/drm/vc4/vc4_dsi.c
+@@ -44,7 +44,6 @@
+
+ #define DSI_CMD_FIFO_DEPTH 16
+ #define DSI_PIX_FIFO_DEPTH 256
+-#define DSI_PIX_FIFO_WIDTH 4
+
+ #define DSI0_CTRL 0x00
+
+@@ -170,11 +169,15 @@
+ #define DSI1_DISP1_CTRL 0x2c
+ /* Format of the data written to TXPKT_PIX_FIFO. */
+ # define DSI_DISP1_PFORMAT_MASK VC4_MASK(2, 1)
+-# define DSI_DISP1_PFORMAT_SHIFT 1
+-# define DSI_DISP1_PFORMAT_16BIT 0
+-# define DSI_DISP1_PFORMAT_24BIT 1
+-# define DSI_DISP1_PFORMAT_32BIT_LE 2
+-# define DSI_DISP1_PFORMAT_32BIT_BE 3
++# define DSI1_DISP1_PFORMAT_SHIFT 1
++# define DSI0_DISP1_PFORMAT_16BIT 0
++# define DSI0_DISP1_PFORMAT_16BIT_ADJ 1
++# define DSI0_DISP1_PFORMAT_24BIT 2
++# define DSI0_DISP1_PFORMAT_32BIT_LE 3 /* NB Invalid, but required for macros to work */
++# define DSI1_DISP1_PFORMAT_16BIT 0
++# define DSI1_DISP1_PFORMAT_24BIT 1
++# define DSI1_DISP1_PFORMAT_32BIT_LE 2
++# define DSI1_DISP1_PFORMAT_32BIT_BE 3
+
+ /* DISP1 is always command mode. */
+ # define DSI_DISP1_ENABLE BIT(0)
+@@ -553,6 +556,7 @@ struct vc4_dsi_variant {
+ unsigned int port;
+
+ bool broken_axi_workaround;
++ unsigned int cmd_fifo_width;
+
+ const char *debugfs_name;
+ const struct debugfs_reg32 *regs;
+@@ -1151,10 +1155,16 @@ static void vc4_dsi_bridge_pre_enable(st
+ /* Set up DISP1 for transferring long command payloads through
+ * the pixfifo.
+ */
+- DSI_PORT_WRITE(DISP1_CTRL,
+- VC4_SET_FIELD(DSI_DISP1_PFORMAT_32BIT_LE,
+- DSI_DISP1_PFORMAT) |
+- DSI_DISP1_ENABLE);
++ if (dsi->variant->cmd_fifo_width == 4)
++ DSI_PORT_WRITE(DISP1_CTRL,
++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_32BIT_LE),
++ DSI_DISP1_PFORMAT) |
++ DSI_DISP1_ENABLE);
++ else
++ DSI_PORT_WRITE(DISP1_CTRL,
++ VC4_SET_FIELD(DSI_PORT_BIT(DISP1_PFORMAT_24BIT),
++ DSI_DISP1_PFORMAT) |
++ DSI_DISP1_ENABLE);
+
+ /* Bring AFE out of reset. */
+ DSI_PORT_WRITE(PHY_AFEC0,
+@@ -1235,9 +1245,9 @@ static ssize_t vc4_dsi_transfer(struct v
+ pix_fifo_len = 0;
+ } else {
+ cmd_fifo_len = (packet.payload_length %
+- DSI_PIX_FIFO_WIDTH);
++ dsi->variant->cmd_fifo_width);
+ pix_fifo_len = ((packet.payload_length - cmd_fifo_len) /
+- DSI_PIX_FIFO_WIDTH);
++ dsi->variant->cmd_fifo_width);
+ }
+
+ WARN_ON_ONCE(pix_fifo_len >= DSI_PIX_FIFO_DEPTH);
+@@ -1255,14 +1265,25 @@ static ssize_t vc4_dsi_transfer(struct v
+
+ for (i = 0; i < cmd_fifo_len; i++)
+ DSI_PORT_WRITE(TXPKT_CMD_FIFO, packet.payload[i]);
+- for (i = 0; i < pix_fifo_len; i++) {
+- const u8 *pix = packet.payload + cmd_fifo_len + i * 4;
++ if (dsi->variant->cmd_fifo_width == 4) {
++ for (i = 0; i < pix_fifo_len; i++) {
++ const u8 *pix = packet.payload + cmd_fifo_len + i * 4;
++
++ DSI_PORT_WRITE(TXPKT_PIX_FIFO,
++ pix[0] |
++ pix[1] << 8 |
++ pix[2] << 16 |
++ pix[3] << 24);
++ }
++ } else {
++ for (i = 0; i < pix_fifo_len; i++) {
++ const u8 *pix = packet.payload + cmd_fifo_len + i * 3;
+
+- DSI_PORT_WRITE(TXPKT_PIX_FIFO,
+- pix[0] |
+- pix[1] << 8 |
+- pix[2] << 16 |
+- pix[3] << 24);
++ DSI_PORT_WRITE(TXPKT_PIX_FIFO,
++ pix[2] |
++ pix[1] << 8 |
++ pix[0] << 16);
++ }
+ }
+
+ if (msg->flags & MIPI_DSI_MSG_USE_LPM)
+@@ -1516,6 +1537,7 @@ static const struct drm_encoder_funcs vc
+
+ static const struct vc4_dsi_variant bcm2711_dsi1_variant = {
+ .port = 1,
++ .cmd_fifo_width = 4,
+ .debugfs_name = "dsi1_regs",
+ .regs = dsi1_regs,
+ .nregs = ARRAY_SIZE(dsi1_regs),
+@@ -1523,6 +1545,7 @@ static const struct vc4_dsi_variant bcm2
+
+ static const struct vc4_dsi_variant bcm2835_dsi0_variant = {
+ .port = 0,
++ .cmd_fifo_width = 3,
+ .debugfs_name = "dsi0_regs",
+ .regs = dsi0_regs,
+ .nregs = ARRAY_SIZE(dsi0_regs),
+@@ -1530,6 +1553,7 @@ static const struct vc4_dsi_variant bcm2
+
+ static const struct vc4_dsi_variant bcm2835_dsi1_variant = {
+ .port = 1,
++ .cmd_fifo_width = 4,
+ .broken_axi_workaround = true,
+ .debugfs_name = "dsi1_regs",
+ .regs = dsi1_regs,
--- /dev/null
+From eafaa6015fc0ed676f6115905e7c4145d23f5b7d Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 26 Nov 2024 15:53:24 +0000
+Subject: [PATCH] dts: bcm2712-rpi: For CM5IO, i2c_csi_dsi needs to be
+ CAM/DISP1
+
+Noted setting up a display on CM5IO. Add
+"dtoverlay=vc4-kms-dsi-ili7881-7inch" fails as it tries to
+find the regulator/backlight/touch on i2c_csi_dsi, which pointed
+at i2c_csi_dsi0 by default.
+
+Adding the dsi0 override updated to point at dsi0, and pointed
+the i2c at i2c_csi_dsi0, which all works.
+
+The default with i2c_csi_dsi needs to be consistent in using
+dsi1/csi1 and the corresponding i2c interface (i2c_csi_dsi1).
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5io.dtsi
+@@ -11,4 +11,4 @@ i2c_csi_dsi0: &i2c6 { // Note: This is f
+ symlink = "i2c-6";
+ };
+
+-i2c_csi_dsi: &i2c_csi_dsi0 { }; // The connector that needs no jumper to enable
++i2c_csi_dsi: &i2c_csi_dsi1 { }; // The connector that needs no jumper to enable
--- /dev/null
+From d128c123754e9dd03ad72c16851a1652331d6da1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 27 Nov 2024 10:24:47 +0000
+Subject: [PATCH] dts: bcm2712-rpi-cm5: Remove inaccessible USB_OC_N
+
+Although VBUS_EN on GPIO42 appears on the CM5's 100-way headers,
+USB_OC_N on GPIO43 does not. Remove the signal name to avoid further
+confusion and disappointment.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -718,7 +718,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ "-", // GPIO40
+ "-", // GPIO41
+ "USB_VBUS_EN", // GPIO42
+- "USB_OC_N", // GPIO43
++ "-", // GPIO43
+ "RP1_STAT_LED", // GPIO44
+ "FAN_PWM", // GPIO45
+ "-", // GPIO46
--- /dev/null
+From 77389e715039b1feac9c6261727600892cc12fdb Mon Sep 17 00:00:00 2001
+From: Michael Heimpold <michael.heimpold@chargebyte.com>
+Date: Fri, 29 Nov 2024 14:10:04 +0100
+Subject: [PATCH] overlays: qca7000: replace URL with textual hint
+
+The deep link into the website is not that stable, so let's
+replace it with a textual description where to find the
+product information.
+
+Signed-off-by: Michael Heimpold <michael.heimpold@chargebyte.com>
+---
+ arch/arm/boot/dts/overlays/qca7000-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/qca7000-overlay.dts
++++ b/arch/arm/boot/dts/overlays/qca7000-overlay.dts
+@@ -1,5 +1,5 @@
+ // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK
+-// Visit: https://chargebyte.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details
++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details
+
+ /dts-v1/;
+ /plugin/;
+--- a/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts
++++ b/arch/arm/boot/dts/overlays/qca7000-uart0-overlay.dts
+@@ -1,5 +1,5 @@
+ // Overlay for the Qualcomm Atheros QCA7000 on PLC Stamp micro EVK
+-// Visit: https://in-tech-smartcharging.com/products/evaluation-tools/plc-stamp-micro-2-evaluation-board for details
++// Visit: https://chargebyte.com -> Controllers & Modules -> Evaluation Tools -> PLC Stamp Micro 2 Evaluation Board for details
+
+ /dts-v1/;
+ /plugin/;
--- /dev/null
+From 178f1c2747c3920723242f26ba290785d45bffae Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 11 Nov 2024 16:38:01 +0000
+Subject: [PATCH] dt-bindings: net: cdns,macb: Add compatible for Raspberry Pi
+ RP1
+
+The Raspberry Pi RP1 chip has the Cadence GEM ethernet
+controller, so add a compatible string for it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/net/cdns,macb.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
+@@ -54,6 +54,7 @@ properties:
+ - cdns,np4-macb # NP4 SoC devices
+ - microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface
+ - microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface
++ - raspberrypi,rp1-gem # Raspberry Pi RP1 gigabit ethernet interface
+ - sifive,fu540-c000-gem # SiFive FU540-C000 SoC
+ - cdns,emac # Generic
+ - cdns,gem # Generic
From f9f0024bd9bf04a58b64bae356be4c04022d23bc Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Mon, 11 Nov 2024 16:40:07 +0000
-Subject: [PATCH 1423/1482] net: macb: Add support for Raspberry Pi RP1
- ethernet controller
+Subject: [PATCH] net: macb: Add support for Raspberry Pi RP1 ethernet
+ controller
The RP1 chip has the Cadence GEM block, but wants the tx_clock
to always run at 125MHz, in the same way as sama7g5.
From 33c225f622d596034a9261316666089a92aa6834 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Mon, 25 Nov 2024 12:30:06 +0000
-Subject: [PATCH 1424/1482] rp1: clk: Only set PLL_SEC_RST in
- rp1_pll_divider_off
+Subject: [PATCH] rp1: clk: Only set PLL_SEC_RST in rp1_pll_divider_off
Rather than clearing all the bits in rp1_pll_divider_off
and setting PLL_SEC_RST, retain the status of all the other
From eb836a6a299322a8e2b9627cccd23c7a76d068ba Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Fri, 8 Nov 2024 17:36:13 +0000
-Subject: [PATCH 1425/1482] rp1: clk: Rationalise the use of the
- CLK_IS_CRITICAL flag
+Subject: [PATCH] rp1: clk: Rationalise the use of the CLK_IS_CRITICAL flag
The clock setup had been copied from clk-bcm2835 which had to cope
with the firmware having configured clocks, so there were flags
From 0b4af929b7125abd3a262577b380c7c81ee9b1c5 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Mon, 11 Nov 2024 15:18:14 +0000
-Subject: [PATCH 1426/1482] dt: arm64: Fixup RP1 ethernet DT configuration
+Subject: [PATCH] dt: arm64: Fixup RP1 ethernet DT configuration
Configure RP1's ethernet block to do the correct thing.
clk_eth is intended to be fixed at 125MHz, so use a new compatible,
--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -24,6 +24,7 @@
+@@ -32,6 +32,7 @@
// RP1_PLL_VIDEO_CORE and dividers are now managed by VEC,DPI drivers
<&rp1_clocks RP1_PLL_SYS>,
<&rp1_clocks RP1_PLL_SYS_SEC>,
<&rp1_clocks RP1_PLL_AUDIO>,
<&rp1_clocks RP1_PLL_AUDIO_SEC>,
<&rp1_clocks RP1_CLK_SYS>,
-@@ -38,6 +39,7 @@
+@@ -46,6 +47,7 @@
<1536000000>, // RP1_PLL_AUDIO_CORE
<200000000>, // RP1_PLL_SYS
<125000000>, // RP1_PLL_SYS_SEC
<61440000>, // RP1_PLL_AUDIO
<192000000>, // RP1_PLL_AUDIO_SEC
<200000000>, // RP1_CLK_SYS
-@@ -968,12 +970,14 @@
+@@ -976,12 +978,14 @@
rp1_eth: ethernet@100000 {
reg = <0xc0 0x40100000 0x0 0x4000>;
From d4e41ed9954fa86c4774f98d393aa401c81a68e7 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Wed, 13 Nov 2024 13:10:27 +0000
-Subject: [PATCH 1427/1482] clk: rp1: Add RP1_CLK_DMA.
+Subject: [PATCH] clk: rp1: Add RP1_CLK_DMA.
The DMA block has a clock, but wasn't defined in the driver. This
resulted in the parent being disabled as unused, and then DMA
From 9049e4df2c54b5e620f855f66db3a18c9f2e181f Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Fri, 8 Nov 2024 17:37:08 +0000
-Subject: [PATCH 1428/1482] rp1: clk: Remove CLK_IGNORE_UNUSED flags
+Subject: [PATCH] rp1: clk: Remove CLK_IGNORE_UNUSED flags
There should be no issue in disabling the RP1 clocks as long as
the kernel knows about all consumers.
From 542d0f7f2e9f90fc0f02f8cb141f7c3fbf46081b Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Mon, 11 Nov 2024 17:11:18 +0000
-Subject: [PATCH 1429/1482] dt: rp1: Use clk_sys for ethernet hclk and pclk
+Subject: [PATCH] dt: rp1: Use clk_sys for ethernet hclk and pclk
hclk and pclk of the MAC are connected to clk_sys, so define
them as being connected accordingly, rather than having fake
--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -974,7 +974,8 @@
+@@ -982,7 +982,8 @@
#address-cells = <1>;
#size-cells = <0>;
interrupts = <RP1_INT_ETH IRQ_TYPE_LEVEL_HIGH>;
&rp1_clocks RP1_CLK_ETH_TSU
&rp1_clocks RP1_CLK_ETH>;
clock-names = "pclk", "hclk", "tsu_clk", "tx_clk";
-@@ -1195,18 +1196,6 @@
+@@ -1230,18 +1231,6 @@
clock-output-names = "xosc";
clock-frequency = <50000000>;
};
From efecbda4014b490e042c7fd090942b32316f9345 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Wed, 13 Nov 2024 13:11:33 +0000
-Subject: [PATCH 1430/1482] dt: rp1: Link RP1 DMA to the associated clock
+Subject: [PATCH] dt: rp1: Link RP1 DMA to the associated clock
This makes the kernel representation of the clock structure
match reality.
--- a/arch/arm64/boot/dts/broadcom/rp1.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1.dtsi
-@@ -1061,7 +1061,7 @@
+@@ -1081,7 +1081,7 @@
reg = <0xc0 0x40188000 0x0 0x1000>;
compatible = "snps,axi-dma-1.01a";
interrupts = <RP1_INT_DMA IRQ_TYPE_LEVEL_HIGH>;
--- /dev/null
+From eb035f3ad7da1324d310ef83b42398f47d5bafe7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Fri, 1 Nov 2024 19:42:17 +0000
+Subject: [PATCH] raspberrypi-firmware: Add the RPI firmware UART APIs
+
+Add VideoCore mailbox definitions for the new RPi firmware UART.
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ include/soc/bcm2835/raspberrypi-firmware.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -98,6 +98,8 @@ enum rpi_firmware_property_tag {
+ RPI_FIRMWARE_GET_REBOOT_FLAGS = 0x00030064,
+ RPI_FIRMWARE_SET_REBOOT_FLAGS = 0x00038064,
+ RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066,
++ RPI_FIRMWARE_GET_SW_UART = 0x0003008a,
++ RPI_FIRMWARE_SET_SW_UART = 0x0003808a,
+
+ /* Dispmanx TAGS */
+ RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
--- /dev/null
+From b8a0e563fd181205565a0edaaebc82b1abf0c5be Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Fri, 1 Nov 2024 19:43:21 +0000
+Subject: [PATCH] serial: core: Add the Raspberry Pi firmware UART id
+
+Assign a new serial core number for the RPi firmware UART.
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ include/uapi/linux/serial_core.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -245,4 +245,7 @@
+ /* Sunplus UART */
+ #define PORT_SUNPLUS 123
+
++/* RPi firmware UART */
++#define PORT_RPI_FW 124
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */
--- /dev/null
+From 2548d954d78bca44c5cf430f8ea6de7c771312d7 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Wed, 28 Aug 2024 09:46:50 +0100
+Subject: [PATCH] serial: tty: Add a driver for the RPi firmware UART
+
+On Raspberry Pi 4 and earlier models the firmware provides
+a low speed (up to 115200 baud) bit-bashed UART on arbitrary
+GPIOs using the second VPU core.
+
+The firmware driver is designed to support 19200 baud. Higher
+rates up to 115200 seem to work but there may be more jitter.
+
+This can be useful for debug or managing additional low
+speed peripherals if the hardware PL011 and 8250 hardware
+UARTs are already used for console / bluetooth.
+
+The firmware driver requires a fixed core clock frequency
+and also requires the VPU PWM audio driver to be disabled
+(dtparam=audio=off)
+
+Runtime configuration is handled via the vc-mailbox APIs
+with the FIFO buffers being allocated in uncached VPU
+addressable memory. The FIFO pointers are stored in spare
+VideoCore multi-core sync registers in order to reduce the number
+of uncached SDRAM accesses thereby reducing jitter.
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ drivers/tty/serial/Kconfig | 11 +
+ drivers/tty/serial/Makefile | 1 +
+ drivers/tty/serial/rpi-fw-uart.c | 563 +++++++++++++++++++++++++++++++
+ 3 files changed, 575 insertions(+)
+ create mode 100644 drivers/tty/serial/rpi-fw-uart.c
+
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1578,6 +1578,17 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
+ but you can alter that using a kernel command line option such as
+ "console=ttyNVTx".
+
++config SERIAL_RPI_FW
++ tristate "Raspberry Pi Firmware software UART support"
++ depends on ARM_AMBA || COMPILE_TEST
++ select SERIAL_CORE
++ help
++ This selects the Raspberry Pi firmware UART. This is a bit-bashed
++ implementation running on the Raspbery Pi VPU core.
++ This is not supported on Raspberry Pi 5 or newer platforms.
++
++ If unsure, say N.
++
+ endmenu
+
+ config SERIAL_MCTRL_GPIO
+--- a/drivers/tty/serial/Makefile
++++ b/drivers/tty/serial/Makefile
+@@ -88,6 +88,7 @@ obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += mi
+ obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o
+ obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o
+ obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o
++obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o
+
+ # GPIOLIB helpers for modem control lines
+ obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
+--- /dev/null
++++ b/drivers/tty/serial/rpi-fw-uart.c
+@@ -0,0 +1,563 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2024, Raspberry Pi Ltd. All rights reserved.
++ */
++
++#include <linux/console.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/gpio/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/serial.h>
++#include <linux/serial_core.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++#include <linux/dma-mapping.h>
++
++#define RPI_FW_UART_RX_FIFO_RD 0xb0
++#define RPI_FW_UART_RX_FIFO_WR 0xb4
++#define RPI_FW_UART_TX_FIFO_RD 0xb8
++#define RPI_FW_UART_TX_FIFO_WR 0xbc
++
++#define RPI_FW_UART_FIFO_SIZE 32
++#define RPI_FW_UART_FIFO_SIZE_MASK (RPI_FW_UART_FIFO_SIZE - 1)
++
++#define RPI_FW_UART_MIN_VERSION 3
++
++struct rpi_fw_uart_params {
++ u32 start;
++ u32 baud;
++ u32 data_bits;
++ u32 stop_bits;
++ u32 gpio_rx;
++ u32 gpio_tx;
++ u32 flags;
++ u32 fifosize;
++ u32 rx_buffer;
++ u32 tx_buffer;
++ u32 version;
++ u32 fifo_reg_base;
++};
++
++struct rpi_fw_uart {
++ struct uart_driver driver;
++ struct uart_port port;
++ struct rpi_firmware *firmware;
++ struct gpio_desc *rx_gpiod;
++ struct gpio_desc *tx_gpiod;
++ unsigned int rx_gpio;
++ unsigned int tx_gpio;
++ unsigned int baud;
++ unsigned int data_bits;
++ unsigned int stop_bits;
++ unsigned char __iomem *base;
++ size_t dma_buffer_size;
++
++ struct hrtimer trigger_start_rx;
++ ktime_t rx_poll_delay;
++ void *rx_buffer;
++ dma_addr_t rx_buffer_dma_addr;
++ int rx_stop;
++
++ void *tx_buffer;
++ dma_addr_t tx_buffer_dma_addr;
++};
++
++static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ u32 rd, wr;
++
++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);
++ return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd;
++}
++
++static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ u32 rd, wr;
++
++ if (!rfu->tx_buffer)
++ return 1;
++
++ rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
++ wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);
++
++ return rd == wr;
++}
++
++unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ u32 rd, wr;
++
++ if (!rfu->rx_buffer)
++ return 1;
++
++ rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD);
++ wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR);
++
++ return rd == wr;
++}
++
++static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port)
++{
++ return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0;
++}
++
++static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++ /*
++ * No hardware flow control, firmware automatically configures
++ * TX to output high and RX to input low.
++ */
++ dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl);
++}
++
++static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port)
++{
++ /* No hardware flow control */
++ return TIOCM_CTS;
++}
++
++static void rpi_fw_uart_stop(struct uart_port *port)
++{
++ struct rpi_fw_uart_params msg = {.start = 0};
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++ hrtimer_cancel(&rfu->trigger_start_rx);
++
++ if (rpi_firmware_property(rfu->firmware,
++ RPI_FIRMWARE_SET_SW_UART,
++ &msg, sizeof(msg)))
++ dev_warn(port->dev,
++ "Failed to shutdown rpi-fw uart. Firmware not configured?");
++}
++
++static void rpi_fw_uart_stop_tx(struct uart_port *port)
++{
++ /* No supported by the current firmware APIs. */
++}
++
++static void rpi_fw_uart_stop_rx(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++ rfu->rx_stop = 1;
++}
++
++static unsigned int rpi_fw_write(struct uart_port *port, const char *s,
++ unsigned int count)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ u8 *out = rfu->tx_buffer;
++ unsigned int consumed = 0;
++
++ while (consumed < count && !rpi_fw_uart_tx_is_full(port)) {
++ u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR)
++ & RPI_FW_UART_FIFO_SIZE_MASK;
++ out[wp] = s[consumed++];
++ wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
++ writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR);
++ }
++ return consumed;
++}
++
++/* Called with port.lock taken */
++static void rpi_fw_uart_start_tx(struct uart_port *port)
++{
++ struct circ_buf *xmit;
++
++ xmit = &port->state->xmit;
++ for (;;) {
++ unsigned int consumed;
++ unsigned long count = CIRC_CNT_TO_END(xmit->head, xmit->tail,
++ UART_XMIT_SIZE);
++ if (!count)
++ break;
++
++ consumed = rpi_fw_write(port, &xmit->buf[xmit->tail], count);
++ uart_xmit_advance(port, consumed);
++ }
++ uart_write_wakeup(port);
++}
++
++/* Called with port.lock taken */
++static void rpi_fw_uart_start_rx(struct uart_port *port)
++{
++ struct tty_port *tty_port = &port->state->port;
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ int count = 0;
++
++ /*
++ * RX is polled, read up to a full buffer of data before trying again
++ * so that this can be interrupted if the firmware is filling the
++ * buffer too fast
++ */
++ while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) {
++ const u8 *in = rfu->rx_buffer;
++ u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD)
++ & RPI_FW_UART_FIFO_SIZE_MASK;
++
++ tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL);
++ rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
++ writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD);
++ count++;
++ }
++ if (count)
++ tty_flip_buffer_push(tty_port);
++}
++
++static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t)
++{
++ unsigned long flags;
++ struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart,
++ trigger_start_rx);
++
++ spin_lock_irqsave(&rfu->port.lock, flags);
++ if (rfu->rx_stop) {
++ spin_unlock_irqrestore(&rfu->port.lock, flags);
++ return HRTIMER_NORESTART;
++ }
++
++ rpi_fw_uart_start_rx(&rfu->port);
++ spin_unlock_irqrestore(&rfu->port.lock, flags);
++ hrtimer_forward_now(t, rfu->rx_poll_delay);
++ return HRTIMER_RESTART;
++}
++
++static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl)
++{
++ dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl);
++}
++
++static int rpi_fw_uart_configure(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++ struct rpi_fw_uart_params msg;
++ unsigned long flags;
++ int rc;
++
++ rpi_fw_uart_stop(port);
++
++ memset(&msg, 0, sizeof(msg));
++ msg.start = 1;
++ msg.gpio_rx = rfu->rx_gpio;
++ msg.gpio_tx = rfu->tx_gpio;
++ msg.data_bits = rfu->data_bits;
++ msg.stop_bits = rfu->stop_bits;
++ msg.baud = rfu->baud;
++ msg.fifosize = RPI_FW_UART_FIFO_SIZE;
++ msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr;
++ msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr;
++
++ rfu->rx_poll_delay = ms_to_ktime(50);
++
++ /*
++ * Reconfigures the firmware UART with the new settings. On the first
++ * call retrieve the addresses of the FIFO buffers. The buffers are
++ * allocated at startup and are not de-allocated.
++ * NB rpi_firmware_property can block
++ */
++ rc = rpi_firmware_property(rfu->firmware,
++ RPI_FIRMWARE_SET_SW_UART,
++ &msg, sizeof(msg));
++ if (rc)
++ goto fail;
++
++ rc = rpi_firmware_property(rfu->firmware,
++ RPI_FIRMWARE_GET_SW_UART,
++ &msg, sizeof(msg));
++ if (rc)
++ goto fail;
++
++ dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version,
++ msg.fifo_reg_base);
++
++ dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
++ msg.start, msg.baud, msg.data_bits, msg.stop_bits,
++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
++
++ if (msg.fifosize != port->fifosize) {
++ dev_err(port->dev, "Expected fifo size %u actual %u",
++ port->fifosize, msg.fifosize);
++ rc = -EINVAL;
++ goto fail;
++ }
++
++ if (!msg.start) {
++ dev_err(port->dev, "Firmware service not running\n");
++ rc = -EINVAL;
++ }
++
++ spin_lock_irqsave(&rfu->port.lock, flags);
++ rfu->rx_stop = 0;
++ hrtimer_start(&rfu->trigger_start_rx,
++ rfu->rx_poll_delay, HRTIMER_MODE_REL);
++ spin_unlock_irqrestore(&rfu->port.lock, flags);
++ return 0;
++fail:
++ dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?");
++ return rc;
++}
++
++static void rpi_fw_uart_free_buffers(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++ if (rfu->rx_buffer)
++ dma_free_coherent(port->dev, rfu->dma_buffer_size,
++ rfu->rx_buffer, GFP_ATOMIC);
++
++ if (rfu->tx_buffer)
++ dma_free_coherent(port->dev, rfu->dma_buffer_size,
++ rfu->tx_buffer, GFP_ATOMIC);
++
++ rfu->rx_buffer = NULL;
++ rfu->tx_buffer = NULL;
++ rfu->rx_buffer_dma_addr = 0;
++ rfu->tx_buffer_dma_addr = 0;
++}
++
++static int rpi_fw_uart_alloc_buffers(struct uart_port *port)
++{
++ struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
++
++ if (rfu->tx_buffer)
++ return 0;
++
++ rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE);
++
++ rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
++ &rfu->rx_buffer_dma_addr, GFP_ATOMIC);
++
++ if (!rfu->rx_buffer)
++ goto alloc_fail;
++
++ rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
++ &rfu->tx_buffer_dma_addr, GFP_ATOMIC);
++
++ if (!rfu->tx_buffer)
++ goto alloc_fail;
++
++ dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n",
++ rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr,
++ rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr);
++ return 0;
++
++alloc_fail:
++ dev_err(port->dev, "%s uart buffer allocation failed\n", __func__);
++ rpi_fw_uart_free_buffers(port);
++ return -ENOMEM;
++}
++
++static int rpi_fw_uart_startup(struct uart_port *port)
++{
++ int rc;
++
++ rc = rpi_fw_uart_alloc_buffers(port);
++ if (rc)
++ dev_err(port->dev, "Failed to start\n");
++ return rc;
++}
++
++static void rpi_fw_uart_shutdown(struct uart_port *port)
++{
++ rpi_fw_uart_stop(port);
++ rpi_fw_uart_free_buffers(port);
++}
++
++static void rpi_fw_uart_set_termios(struct uart_port *port,
++ struct ktermios *new,
++ const struct ktermios *old)
++{
++ struct rpi_fw_uart *rfu =
++ container_of(port, struct rpi_fw_uart, port);
++ rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200);
++ rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1;
++
++ rpi_fw_uart_configure(port);
++}
++
++static const struct uart_ops rpi_fw_uart_ops = {
++ .tx_empty = rpi_fw_uart_tx_empty,
++ .set_mctrl = rpi_fw_uart_set_mctrl,
++ .get_mctrl = rpi_fw_uart_get_mctrl,
++ .stop_rx = rpi_fw_uart_stop_rx,
++ .stop_tx = rpi_fw_uart_stop_tx,
++ .start_tx = rpi_fw_uart_start_tx,
++ .break_ctl = rpi_fw_uart_break_ctl,
++ .startup = rpi_fw_uart_startup,
++ .shutdown = rpi_fw_uart_shutdown,
++ .set_termios = rpi_fw_uart_set_termios,
++};
++
++static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name)
++{
++ struct of_phandle_args of_args = { 0 };
++ bool is_bcm28xx;
++
++ /* This really shouldn't fail, given that we have a gpiod */
++ if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args))
++ return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n");
++
++ is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") ||
++ of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio");
++ of_node_put(of_args.np);
++ if (!is_bcm28xx || of_args.args_count != 2)
++ return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n");
++
++ return of_args.args[0];
++}
++
++static int rpi_fw_uart_probe(struct platform_device *pdev)
++{
++ struct device_node *firmware_node;
++ struct device *dev = &pdev->dev;
++ struct rpi_firmware *firmware;
++ struct uart_port *port;
++ struct rpi_fw_uart *rfu;
++ struct rpi_fw_uart_params msg;
++ int version_major;
++ int err;
++
++ dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node);
++
++ /*
++ * We can be probed either through the an old-fashioned
++ * platform device registration or through a DT node that is a
++ * child of the firmware node. Handle both cases.
++ */
++ if (dev->of_node)
++ firmware_node = of_parse_phandle(dev->of_node, "firmware", 0);
++ else
++ firmware_node = of_find_compatible_node(NULL, NULL,
++ "raspberrypi,bcm2835-firmware");
++ if (!firmware_node) {
++ dev_err(dev, "Missing firmware node\n");
++ return -ENOENT;
++ }
++
++ firmware = devm_rpi_firmware_get(dev, firmware_node);
++ of_node_put(firmware_node);
++ if (!firmware)
++ return -EPROBE_DEFER;
++
++ rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL);
++ if (!rfu)
++ return -ENOMEM;
++
++ rfu->firmware = firmware;
++
++ err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART,
++ &msg, sizeof(msg));
++ if (err) {
++ dev_err(dev, "VC firmware does not support rpi-fw-uart\n");
++ return err;
++ }
++
++ version_major = msg.version >> 16;
++ if (msg.version < RPI_FW_UART_MIN_VERSION) {
++ dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n",
++ version_major, RPI_FW_UART_MIN_VERSION);
++ return -EINVAL;
++ }
++
++ rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN);
++ if (IS_ERR(rfu->rx_gpiod))
++ return PTR_ERR(rfu->rx_gpiod);
++
++ rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH);
++ if (IS_ERR(rfu->tx_gpiod))
++ return PTR_ERR(rfu->tx_gpiod);
++
++ rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios");
++ if (rfu->rx_gpio < 0)
++ return rfu->rx_gpio;
++ rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios");
++ if (rfu->tx_gpio < 0)
++ return rfu->tx_gpio;
++
++ rfu->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(rfu->base))
++ return PTR_ERR(rfu->base);
++
++ /* setup the driver */
++ rfu->driver.owner = THIS_MODULE;
++ rfu->driver.driver_name = "ttyRFU";
++ rfu->driver.dev_name = "ttyRFU";
++ rfu->driver.nr = 1;
++ rfu->data_bits = 8;
++
++ /* RX is polled */
++ hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx;
++
++ err = uart_register_driver(&rfu->driver);
++ if (err) {
++ dev_err(dev, "failed to register UART driver: %d\n",
++ err);
++ return err;
++ }
++
++ /* setup the port */
++ port = &rfu->port;
++ spin_lock_init(&port->lock);
++ port->dev = &pdev->dev;
++ port->type = PORT_RPI_FW;
++ port->ops = &rpi_fw_uart_ops;
++ port->fifosize = RPI_FW_UART_FIFO_SIZE;
++ port->iotype = UPIO_MEM;
++ port->flags = UPF_BOOT_AUTOCONF;
++ port->private_data = rfu;
++
++ err = uart_add_one_port(&rfu->driver, port);
++ if (err) {
++ dev_err(dev, "failed to add UART port: %d\n", err);
++ goto unregister_uart;
++ }
++ platform_set_drvdata(pdev, rfu);
++
++ dev_info(dev, "version %d.%d gpios tx %u rx %u\n",
++ msg.version >> 16, msg.version & 0xffff,
++ rfu->tx_gpio, rfu->rx_gpio);
++ return 0;
++
++unregister_uart:
++ uart_unregister_driver(&rfu->driver);
++
++ return err;
++}
++
++static int rpi_fw_uart_remove(struct platform_device *pdev)
++{
++ struct rpi_fw_uart *rfu = platform_get_drvdata(pdev);
++
++ uart_remove_one_port(&rfu->driver, &rfu->port);
++ uart_unregister_driver(&rfu->driver);
++
++ return 0;
++}
++
++static const struct of_device_id rpi_fw_match[] = {
++ { .compatible = "raspberrypi,firmware-uart" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, rpi_fw_match);
++
++static struct platform_driver rpi_fw_driver = {
++ .driver = {
++ .name = "rpi_fw-uart",
++ .of_match_table = rpi_fw_match,
++ },
++ .probe = rpi_fw_uart_probe,
++ .remove = rpi_fw_uart_remove,
++};
++module_platform_driver(rpi_fw_driver);
++
++MODULE_AUTHOR("Tim Gover <tim.gover@rasberrypi.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver");
--- /dev/null
+From b6b126861062020fb50859c5af71d8846ce43d7c Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Mon, 4 Nov 2024 13:44:10 +0000
+Subject: [PATCH] dtoverlay: Add an overlay for the Raspberry Pi firmware UART
+
+Add a device-tree overlay to configure the GPIOs for the
+Raspberry Pi firmware UART.
+
+Example config.txt
+dtoverlay=rpi-fw-uart,txd0_pin=20,rxd0_pin=21
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 12 ++++++
+ .../boot/dts/overlays/rpi-fw-uart-overlay.dts | 41 +++++++++++++++++++
+ 3 files changed, 54 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -233,6 +233,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ rpi-dacpro.dtbo \
+ rpi-digiampplus.dtbo \
+ rpi-ft5406.dtbo \
++ rpi-fw-uart.dtbo \
+ rpi-poe.dtbo \
+ rpi-poe-plus.dtbo \
+ rpi-sense.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4141,6 +4141,18 @@ Params: touchscreen-size-x Touchscr
+ touchscreen-swapped-x-y Swap X and Y cordinates (default 0);
+
+
++Name: rpi-fw-uart
++Info: Configures the firmware software UART driver.
++ This driver requires exclusive usage of the second VPU core. The
++ following config.txt entries should be set when this driver is used.
++ dtparam=audio=off
++ isp_use_vpu0=1
++Load: dtoverlay=rpi-fw-uart,<param>[=<val>]
++Params: txd0_pin GPIO pin for TXD0 (any free - default 20)
++
++ rxd0_pin GPIO pin for RXD0 (any free - default 21)
++
++
+ Name: rpi-poe
+ Info: Raspberry Pi PoE HAT fan
+ Load: dtoverlay=rpi-poe,<param>[=<val>]
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/rpi-fw-uart-overlay.dts
+@@ -0,0 +1,41 @@
++// SPDX-License-Identifier: GPL-2.0
++// Overlay for the Raspberry Pi Firmware UART driver
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2835";
++
++ fragment@0 {
++ target = <&gpio>;
++ __overlay__ {
++ rpi_fw_uart_pins: rpi_fw_uart_pins@4 {
++ brcm,pins = <20 21>;
++ brcm,function = <1 0>; /* output input */
++ brcm,pull = <0 2>; /* none pull-up */
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&soc>;
++ __overlay__ {
++ rpi_fw_uart: rpi_fw_uart@7e000000 {
++ compatible = "raspberrypi,firmware-uart";
++ reg = <0x7e000000 0x100>; /* VideoCore MS sync regs */
++ firmware = <&firmware>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&rpi_fw_uart_pins>;
++ tx-gpios = <&gpio 20 0>;
++ rx-gpios = <&gpio 21 0>;
++ };
++ };
++ };
++
++ __overrides__ {
++ txd0_pin = <&rpi_fw_uart>,"tx-gpios:4",
++ <&rpi_fw_uart_pins>, "brcm,pins:0";
++ rxd0_pin = <&rpi_fw_uart>,"rx-gpios:4",
++ <&rpi_fw_uart_pins>, "brcm,pins:4";
++ };
++};
--- /dev/null
+From 1993b453dc4a62378e90d91e9e0006a6c085f38a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 18 Sep 2024 10:23:41 +0100
+Subject: [PATCH] ARM: dts: Remove duplicate tags
+
+A dts file should have exactly one /dts-v1/ tag, and overlays should
+also have one /plugin/ tag. Through careless inclusion of other files,
+some Device Trees and overlays end up with duplicated tags - this
+commit removes them.
+
+The change is largely cosmetic, unless using an old version of dtc.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts | 1 -
+ arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi | 3 ---
+ arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi | 2 --
+ arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts | 3 ---
+ arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts | 3 ---
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi | 1 -
+ 11 files changed, 28 deletions(-)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-400.dts
+@@ -1,5 +1,4 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/dts-v1/;
+ #include "bcm2711-rpi-4-b.dts"
+
+ / {
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -1,7 +1,4 @@
+ // Definitions for I2C based sensors using the Industrial IO or HWMON interface.
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/gpio/gpio.h>
+
+ / {
+--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
+@@ -1,8 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+ // Partial definitions for IMX290 or IMX327 camera module on VC I2C bus
+ // The compatible string should be set in an overlay that then includes this one
+-/dts-v1/;
+-/plugin/;
+
+ #include <dt-bindings/gpio/gpio.h>
+
+--- a/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pisound-pi5-overlay.dts
+@@ -17,9 +17,6 @@
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+-/dts-v1/;
+-/plugin/;
+-
+ #include "pisound-overlay.dts"
+
+ &pisound_spi {
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
+@@ -2,9 +2,6 @@
+ * vc4-fkms-v3d-overlay.dts
+ */
+
+-/dts-v1/;
+-/plugin/;
+-
+ #include "cma-overlay.dts"
+
+ / {
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
+@@ -2,9 +2,6 @@
+ * vc4-fkms-v3d-overlay.dts
+ */
+
+-/dts-v1/;
+-/plugin/;
+-
+ #include "cma-overlay.dts"
+
+ &frag0 {
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-overlay.dts
+@@ -2,9 +2,6 @@
+ * vc4-kms-v3d-overlay.dts
+ */
+
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/clock/bcm2835.h>
+
+ #include "cma-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi4-overlay.dts
+@@ -2,9 +2,6 @@
+ * vc4-kms-v3d-pi4-overlay.dts
+ */
+
+-/dts-v1/;
+-/plugin/;
+-
+ #include <dt-bindings/clock/bcm2835.h>
+
+ #include "cma-overlay.dts"
+--- a/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/w1-gpio-pi5-overlay.dts
+@@ -1,6 +1,3 @@
+-/dts-v1/;
+-/plugin/;
+-
+ #include "w1-gpio-overlay.dts"
+
+ / {
+--- a/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/w1-gpio-pullup-pi5-overlay.dts
+@@ -1,6 +1,3 @@
+-/dts-v1/;
+-/plugin/;
+-
+ #include "w1-gpio-pullup-overlay.dts"
+
+ / {
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5l.dtsi
+@@ -1,5 +1,4 @@
+ // SPDX-License-Identifier: GPL-2.0
+-/dts-v1/;
+
+ #include "bcm2712-rpi-cm5.dtsi"
+
--- /dev/null
+From e33702e5e5fe9fef6ec967961e2e5e1c2285ba36 Mon Sep 17 00:00:00 2001
+From: gtrainavicius <gtrainavicius@users.noreply.github.com>
+Date: Wed, 4 Dec 2024 11:18:14 +0200
+Subject: [PATCH] =?UTF-8?q?Allow=20setting=20I=C2=B2C=20clock=20frequency?=
+ =?UTF-8?q?=20via=20i2c=5Farm=5Fbaudrate=20dtparam=20when=20using=20pimidi?=
+ =?UTF-8?q?=20overlay.?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This change removes the forced 1MHz clock frequency, so it can be overridden using `i2c_arm_baudrate`.
+---
+ arch/arm/boot/dts/overlays/pimidi-overlay.dts | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/pimidi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pimidi-overlay.dts
+@@ -26,7 +26,6 @@
+ target = <&i2c_arm>;
+ __overlay__ {
+ status = "okay";
+- clock-frequency=<1000000>;
+
+ pimidi_ctrl: pimidi_ctrl@20 {
+ compatible = "blokaslabs,pimidi";
--- /dev/null
+From fda47c026dee7acd975ee2c0f7a440d4038cfaa3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 3 Dec 2024 15:57:01 +0000
+Subject: [PATCH] nvme-pci: Disable Host Memory Buffer usage
+
+Some NVME drives seem to request significant amounts of DMA coherent
+memory - enough to exhaust our standard 64MB CMA allocation.
+
+Try disabling the feature to see what effect it has - drives should
+continue to function without it.
+
+Link: https://github.com/raspberrypi/linux/issues/6504
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/nvme/host/pci.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -1932,6 +1932,7 @@ static void nvme_free_host_mem(struct nv
+ dev->nr_host_mem_descs = 0;
+ }
+
++#if 0
+ static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
+ u32 chunk_size)
+ {
+@@ -2000,9 +2001,11 @@ out:
+ dev->host_mem_descs = NULL;
+ return -ENOMEM;
+ }
++#endif
+
+ static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred)
+ {
++#if 0
+ u64 min_chunk = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES);
+ u64 hmminds = max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2);
+ u64 chunk_size;
+@@ -2015,6 +2018,7 @@ static int nvme_alloc_host_mem(struct nv
+ nvme_free_host_mem(dev);
+ }
+ }
++#endif
+
+ return -ENOMEM;
+ }
--- /dev/null
+From 0313a0961b685973f7833017479a277e3a4c05a4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 4 Dec 2024 14:40:59 +0000
+Subject: [PATCH] fixup! serial: tty: Add a driver for the RPi firmware UART
+
+Make SERIAL_RPI_FW depend on RASPBERRYPI_FIRMWARE.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1580,7 +1580,7 @@ config SERIAL_NUVOTON_MA35D1_CONSOLE
+
+ config SERIAL_RPI_FW
+ tristate "Raspberry Pi Firmware software UART support"
+- depends on ARM_AMBA || COMPILE_TEST
++ depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
+ select SERIAL_CORE
+ help
+ This selects the Raspberry Pi firmware UART. This is a bit-bashed
--- /dev/null
+From 0a5be0fe6ba3a981508421131def7eab55d6d75c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 5 Dec 2024 12:08:23 +0000
+Subject: [PATCH] serial: rpi-fw-uart: Demote debug log messages
+
+A dev_info call in rpi_fw_uart_configure causes kernel log output every
+time one opens the UART. Demote it to dev_dbg.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/rpi-fw-uart.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/rpi-fw-uart.c
++++ b/drivers/tty/serial/rpi-fw-uart.c
+@@ -277,9 +277,9 @@ static int rpi_fw_uart_configure(struct
+ dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version,
+ msg.fifo_reg_base);
+
+- dev_info(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
+- msg.start, msg.baud, msg.data_bits, msg.stop_bits,
+- msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
++ dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
++ msg.start, msg.baud, msg.data_bits, msg.stop_bits,
++ msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);
+
+ if (msg.fifosize != port->fifosize) {
+ dev_err(port->dev, "Expected fifo size %u actual %u",
--- /dev/null
+From 02dee262a9c7295ea514e9db7b9aa4b239922cb3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 2 Dec 2024 15:41:21 +0000
+Subject: [PATCH] dtoverlays: Add Arducam override for ov9281
+
+The Arducam module is slow starting up, so add an override
+to slow the regulator down.
+https://forums.raspberrypi.com/viewtopic.php?t=380236
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 2 ++
+ arch/arm/boot/dts/overlays/ov9281-overlay.dts | 13 ++++++++++++-
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3538,6 +3538,8 @@ Params: rotation Mounting
+ configuring the sensor (default on)
+ cam0 Adopt the default configuration for CAM0 on a
+ Compute Module (CSI0, i2c_vc, and cam0_reg).
++ arducam Slow down the regulator for slow Arducam
++ modules.
+
+
+ Name: papirus
+--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts
+@@ -57,6 +57,14 @@
+ };
+ };
+
++ reg_frag: fragment@5 {
++ target = <&cam1_reg>;
++ __dormant__ {
++ startup-delay-us = <20000>;
++ off-on-delay-us = <30000>;
++ };
++ };
++
+ __overrides__ {
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+@@ -65,7 +73,10 @@
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+- <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>,
++ <®_frag>, "target:0=",<&cam0_reg>;
++ arducam = <0>, "+5";
++
+ };
+ };
+
--- /dev/null
+From 97638920f1a40e2e0cab363d1e03837ff50c5478 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Thu, 5 Dec 2024 17:19:23 +0800
+Subject: [PATCH] drivers:input:touchscreen: Add support for no irq to ili210x
+ driver
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/input/touchscreen/ili210x.c | 63 ++++++++++++++++++++++++-----
+ 1 file changed, 52 insertions(+), 11 deletions(-)
+
+--- a/drivers/input/touchscreen/ili210x.c
++++ b/drivers/input/touchscreen/ili210x.c
+@@ -67,6 +67,8 @@ struct ili210x {
+ u8 version_proto[2];
+ u8 ic_mode[2];
+ bool stop;
++ struct timer_list poll_timer;
++ struct work_struct poll_work;
+ };
+
+ static int ili210x_read_reg(struct i2c_client *client,
+@@ -360,6 +362,34 @@ static irqreturn_t ili210x_irq(int irq,
+ return IRQ_HANDLED;
+ }
+
++static void ili210x_poll_work(struct work_struct *work)
++{
++ struct ili210x *priv = container_of(work, struct ili210x, poll_work);
++ struct i2c_client *client = priv->client;
++ const struct ili2xxx_chip *chip = priv->chip;
++ u8 touchdata[ILI210X_DATA_SIZE] = { 0 };
++ bool touch;
++ int error;
++
++ error = chip->get_touch_data(client, touchdata);
++ if (error) {
++ dev_err(&client->dev, "Unable to get touch data: %d\n", error);
++ return;
++ }
++
++ touch = ili210x_report_events(priv, touchdata);
++}
++
++static void ili210x_poll_timer_callback(struct timer_list *t)
++{
++ struct ili210x *priv = from_timer(priv, t, poll_timer);
++
++ schedule_work(&priv->poll_work);
++
++ if (!priv->stop)
++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD));
++}
++
+ static int ili251x_firmware_update_resolution(struct device *dev)
+ {
+ struct i2c_client *client = to_i2c_client(dev);
+@@ -945,11 +975,6 @@ static int ili210x_i2c_probe(struct i2c_
+ return -ENODEV;
+ }
+
+- if (client->irq <= 0) {
+- dev_err(dev, "No IRQ!\n");
+- return -EINVAL;
+- }
+-
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset_gpio))
+ return PTR_ERR(reset_gpio);
+@@ -1001,12 +1026,17 @@ static int ili210x_i2c_probe(struct i2c_
+ return error;
+ }
+
+- error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
+- IRQF_ONESHOT, client->name, priv);
+- if (error) {
+- dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+- error);
+- return error;
++ if (client->irq) {
++ error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq,
++ IRQF_ONESHOT, client->name, priv);
++ if (error) {
++ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error);
++ return error;
++ }
++ } else {
++ timer_setup(&priv->poll_timer, ili210x_poll_timer_callback, 0);
++ mod_timer(&priv->poll_timer, jiffies + msecs_to_jiffies(ILI2XXX_POLL_PERIOD));
++ INIT_WORK(&priv->poll_work, ili210x_poll_work);
+ }
+
+ error = devm_add_action_or_reset(dev, ili210x_stop, priv);
+@@ -1029,6 +1059,16 @@ static int ili210x_i2c_probe(struct i2c_
+ return 0;
+ }
+
++static void ili210x_i2c_remove(struct i2c_client *client)
++{
++ struct ili210x *tsdata = i2c_get_clientdata(client);
++
++ if (!client->irq) {
++ del_timer(&tsdata->poll_timer);
++ cancel_work_sync(&tsdata->poll_work);
++ }
++}
++
+ static const struct i2c_device_id ili210x_i2c_id[] = {
+ { "ili210x", (long)&ili210x_chip },
+ { "ili2117", (long)&ili211x_chip },
+@@ -1054,6 +1094,7 @@ static struct i2c_driver ili210x_ts_driv
+ },
+ .id_table = ili210x_i2c_id,
+ .probe = ili210x_i2c_probe,
++ .remove = ili210x_i2c_remove,
+ };
+
+ module_i2c_driver(ili210x_ts_driver);
--- /dev/null
+From 4a89fda8f73df89e009a6188ef07ab97b1d03c7f Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Thu, 5 Dec 2024 17:20:22 +0800
+Subject: [PATCH] drivers:gpu:drm:panel: Added waveshare 13.3inch panel(support
+ 2/4lane)
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 155 +++++++++++++++++---
+ 1 file changed, 138 insertions(+), 17 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -32,6 +32,12 @@ struct ws_panel {
+ enum drm_panel_orientation orientation;
+ };
+
++struct ws_panel_data {
++ const struct drm_display_mode *mode;
++ int lanes;
++ unsigned long mode_flags;
++};
++
+ /* 2.8inch 480x640
+ * https://www.waveshare.com/product/raspberry-pi/displays/2.8inch-dsi-lcd.htm
+ */
+@@ -47,6 +53,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 640 + 150 + 50 + 150,
+ };
+
++static const struct ws_panel_data ws_panel_2_8_data = {
++ .mode = &ws_panel_2_8_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 3.4inch 800x800 Round
+ * https://www.waveshare.com/product/displays/lcd-oled/3.4inch-dsi-lcd-c.htm
+ */
+@@ -62,6 +74,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 800 + 8 + 4 + 16,
+ };
+
++static const struct ws_panel_data ws_panel_3_4_data = {
++ .mode = &ws_panel_3_4_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 4.0inch 480x800
+ * https://www.waveshare.com/product/raspberry-pi/displays/4inch-dsi-lcd.htm
+ */
+@@ -77,6 +95,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 800 + 20 + 100 + 20,
+ };
+
++static const struct ws_panel_data ws_panel_4_0_data = {
++ .mode = &ws_panel_4_0_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 7.0inch C 1024x600
+ * https://www.waveshare.com/product/raspberry-pi/displays/lcd-oled/7inch-dsi-lcd-c-with-case-a.htm
+ */
+@@ -92,6 +116,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 600 + 10 + 10 + 10,
+ };
+
++static const struct ws_panel_data ws_panel_7_0_c_data = {
++ .mode = &ws_panel_7_0_c_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 7.9inch 400x1280
+ * https://www.waveshare.com/product/raspberry-pi/displays/7.9inch-dsi-lcd.htm
+ */
+@@ -107,6 +137,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 1280 + 20 + 10 + 20,
+ };
+
++static const struct ws_panel_data ws_panel_7_9_data = {
++ .mode = &ws_panel_7_9_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 8.0inch or 10.1inch 1280x800
+ * https://www.waveshare.com/product/raspberry-pi/displays/8inch-dsi-lcd-c.htm
+ * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm
+@@ -123,6 +159,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 800 + 40 + 48 + 40,
+ };
+
++static const struct ws_panel_data ws_panel_10_1_data = {
++ .mode = &ws_panel_10_1_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 11.9inch 320x1480
+ * https://www.waveshare.com/product/raspberry-pi/displays/11.9inch-dsi-lcd.htm
+ */
+@@ -138,6 +180,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 1480 + 60 + 60 + 60,
+ };
+
++static const struct ws_panel_data ws_panel_11_9_data = {
++ .mode = &ws_panel_11_9_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ static const struct drm_display_mode ws_panel_4_mode = {
+ .clock = 50000,
+ .hdisplay = 720,
+@@ -150,6 +198,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 720 + 8 + 4 + 16,
+ };
+
++static const struct ws_panel_data ws_panel_4_data = {
++ .mode = &ws_panel_4_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 5.0inch 720x1280
+ * https://www.waveshare.com/5inch-dsi-lcd-d.htm
+ */
+@@ -165,6 +219,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 1280 + 20 + 20 + 20,
+ };
+
++static const struct ws_panel_data ws_panel_5_0_data = {
++ .mode = &ws_panel_5_0_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 6.25inch 720x1560
+ * https://www.waveshare.com/6.25inch-dsi-lcd.htm
+ */
+@@ -180,6 +240,12 @@ static const struct drm_display_mode ws_
+ .vtotal = 1560 + 20 + 20 + 20,
+ };
+
++static const struct ws_panel_data ws_panel_6_25_data = {
++ .mode = &ws_panel_6_25_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
+ /* 8.8inch 480x1920
+ * https://www.waveshare.com/8.8inch-dsi-lcd.htm
+ */
+@@ -195,6 +261,48 @@ static const struct drm_display_mode ws_
+ .vtotal = 1920 + 20 + 20 + 20,
+ };
+
++static const struct ws_panel_data ws_panel_8_8_data = {
++ .mode = &ws_panel_8_8_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
++};
++
++static const struct drm_display_mode ws_panel_13_3_4lane_mode = {
++ .clock = 148500,
++ .hdisplay = 1920,
++ .hsync_start = 1920 + 88,
++ .hsync_end = 1920 + 88 + 44,
++ .htotal = 1920 + 88 + 44 + 148,
++ .vdisplay = 1080,
++ .vsync_start = 1080 + 4,
++ .vsync_end = 1080 + 4 + 5,
++ .vtotal = 1080 + 4 + 5 + 36,
++};
++
++static const struct ws_panel_data ws_panel_13_3_4lane_data = {
++ .mode = &ws_panel_13_3_4lane_mode,
++ .lanes = 4,
++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
++};
++
++static const struct drm_display_mode ws_panel_13_3_2lane_mode = {
++ .clock = 83333,
++ .hdisplay = 1920,
++ .hsync_start = 1920 + 88,
++ .hsync_end = 1920 + 88 + 44,
++ .htotal = 1920 + 88 + 44 + 148,
++ .vdisplay = 1080,
++ .vsync_start = 1080 + 4,
++ .vsync_end = 1080 + 4 + 5,
++ .vtotal = 1080 + 4 + 5 + 36,
++};
++
++static const struct ws_panel_data ws_panel_13_3_2lane_data = {
++ .mode = &ws_panel_13_3_2lane_mode,
++ .lanes = 2,
++ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
++};
++
+ static struct ws_panel *panel_to_ts(struct drm_panel *panel)
+ {
+ return container_of(panel, struct ws_panel, base);
+@@ -232,7 +340,10 @@ static int ws_panel_enable(struct drm_pa
+ {
+ struct ws_panel *ts = panel_to_ts(panel);
+
+- ws_panel_i2c_write(ts, 0xad, 0x01);
++ if (ts->mode == &ws_panel_13_3_2lane_mode)
++ ws_panel_i2c_write(ts, 0xad, 0x02);
++ else
++ ws_panel_i2c_write(ts, 0xad, 0x01);
+
+ return 0;
+ }
+@@ -328,13 +439,18 @@ static int ws_panel_probe(struct i2c_cli
+ .channel = 0,
+ .node = NULL,
+ };
++ const struct ws_panel_data *_ws_panel_data;
+ int ret;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+- ts->mode = of_device_get_match_data(dev);
++ _ws_panel_data = of_device_get_match_data(dev);
++ if (!_ws_panel_data)
++ return -EINVAL;
++
++ ts->mode = _ws_panel_data->mode;
+ if (!ts->mode)
+ return -EINVAL;
+
+@@ -396,10 +512,9 @@ static int ws_panel_probe(struct i2c_cli
+ */
+ drm_panel_add(&ts->base);
+
+- ts->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+- MIPI_DSI_CLOCK_NON_CONTINUOUS;
++ ts->dsi->mode_flags = _ws_panel_data->mode_flags;
+ ts->dsi->format = MIPI_DSI_FMT_RGB888;
+- ts->dsi->lanes = 2;
++ ts->dsi->lanes = _ws_panel_data->lanes;
+
+ ret = devm_mipi_dsi_attach(dev, ts->dsi);
+
+@@ -432,40 +547,46 @@ static void ws_panel_shutdown(struct i2c
+ static const struct of_device_id ws_panel_of_ids[] = {
+ {
+ .compatible = "waveshare,2.8inch-panel",
+- .data = &ws_panel_2_8_mode,
++ .data = &ws_panel_2_8_data,
+ }, {
+ .compatible = "waveshare,3.4inch-panel",
+- .data = &ws_panel_3_4_mode,
++ .data = &ws_panel_3_4_data,
+ }, {
+ .compatible = "waveshare,4.0inch-panel",
+- .data = &ws_panel_4_0_mode,
++ .data = &ws_panel_4_0_data,
+ }, {
+ .compatible = "waveshare,7.0inch-c-panel",
+- .data = &ws_panel_7_0_c_mode,
++ .data = &ws_panel_7_0_c_data,
+ }, {
+ .compatible = "waveshare,7.9inch-panel",
+- .data = &ws_panel_7_9_mode,
++ .data = &ws_panel_7_9_data,
+ }, {
+ .compatible = "waveshare,8.0inch-panel",
+- .data = &ws_panel_10_1_mode,
++ .data = &ws_panel_10_1_data,
+ }, {
+ .compatible = "waveshare,10.1inch-panel",
+- .data = &ws_panel_10_1_mode,
++ .data = &ws_panel_10_1_data,
+ }, {
+ .compatible = "waveshare,11.9inch-panel",
+- .data = &ws_panel_11_9_mode,
++ .data = &ws_panel_11_9_data,
+ }, {
+ .compatible = "waveshare,4inch-panel",
+- .data = &ws_panel_4_mode,
++ .data = &ws_panel_4_data,
+ }, {
+ .compatible = "waveshare,5.0inch-panel",
+- .data = &ws_panel_5_0_mode,
++ .data = &ws_panel_5_0_data,
+ }, {
+ .compatible = "waveshare,6.25inch-panel",
+- .data = &ws_panel_6_25_mode,
++ .data = &ws_panel_6_25_data,
+ }, {
+ .compatible = "waveshare,8.8inch-panel",
+- .data = &ws_panel_8_8_mode,
++ .data = &ws_panel_8_8_data,
++ }, {
++ .compatible = "waveshare,13.3inch-4lane-panel",
++ .data = &ws_panel_13_3_4lane_data,
++ }, {
++ .compatible = "waveshare,13.3inch-2lane-panel",
++ .data = &ws_panel_13_3_2lane_data,
+ }, {
+ /* sentinel */
+ }
--- /dev/null
+From e442e5c1ab6bff5b5460b4fc949beb72aaf77970 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Thu, 5 Dec 2024 18:11:26 +0800
+Subject: [PATCH] arch:arm:boot:dts:overlays: Added waveshare 13.3inch panel
+ support
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ arch/arm/boot/dts/overlays/README | 2 ++
+ .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 7 +++++++
+ 2 files changed, 9 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5338,6 +5338,8 @@ Params: 2_8_inch 2.8" 480
+ 8_0_inch 8.0" 1280x800
+ 10_1_inch 10.1" 1280x800
+ 11_9_inch 11.9" 320x1480
++ 13_3_inch_4lane 13.3" 1920x1080 4lane
++ 13_3_inch_2lane 13.3" 1920x1080 2lane
+ i2c1 Use i2c-1 with jumper wires from GPIOs 2&3
+ disable_touch Disable the touch controller
+ rotation Set the panel orientation property
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
+@@ -51,6 +51,11 @@
+ reg = <0x14>;
+ compatible = "goodix,gt911";
+ };
++
++ touch2: ilitek@41 {
++ compatible = "ilitek,ili251x";
++ reg = <0x41>;
++ };
+ };
+ };
+
+@@ -120,6 +125,8 @@
+ <&touch>, "touchscreen-inverted-x?",
+ <&touch>, "touchscreen-inverted-y?";
+ 8_8_inch = <&panel>, "compatible=waveshare,8.8inch-panel";
++ 13_3_inch_4lane = <&panel>, "compatible=waveshare,13.3inch-4lane-panel";
++ 13_3_inch_2lane = <&panel>, "compatible=waveshare,13.3inch-2lane-panel";
+ i2c1 = <&i2c_frag>, "target:0=",<&i2c1>,
+ <0>, "-3-4+5";
+ disable_touch = <&touch>, "status=disabled";
--- /dev/null
+From 166dfc4399643681f2e4277bf7b7407e926861e5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 9 Dec 2024 14:58:16 +0000
+Subject: [PATCH] fixup! cgroup: Use kernel command line to disable memory
+ cgroup
+
+cgroup features are distinct from cgroup subsystems - handle them
+correctly.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ kernel/cgroup/cgroup.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/kernel/cgroup/cgroup.c
++++ b/kernel/cgroup/cgroup.c
+@@ -6769,11 +6769,19 @@ static int __init cgroup_enable(char *st
+ strcmp(token, ss->legacy_name))
+ continue;
+
+- cgroup_feature_disable_mask &= ~(1 << i);
+ static_branch_enable(cgroup_subsys_enabled_key[i]);
+ pr_info("Enabling %s control group subsystem\n",
+ ss->name);
+ }
++
++ for (i = 0; i < OPT_FEATURE_COUNT; i++) {
++ if (strcmp(token, cgroup_opt_feature_names[i]))
++ continue;
++ cgroup_feature_disable_mask &= ~(1 << i);
++ pr_info("Enabling %s control group feature\n",
++ cgroup_opt_feature_names[i]);
++ break;
++ }
+ }
+ return 1;
+ }
--- /dev/null
+From e23afbf2c7aae9264322eee8e5c72ca1887606df Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Dec 2024 10:43:18 +0000
+Subject: [PATCH] media: i2c: ov9282: Correct the exposure offset
+
+The datasheet lists that "Maximum exposure time is frame
+length -25 row periods, where frame length is set by
+registers {0x380E, 0x380F}".
+However this driver had OV9282_EXPOSURE_OFFSET set to 12
+which allowed that restriction to be violated, and would
+result in very under-exposed images.
+
+Correct the offset.
+
+Fixes: 14ea315bbeb7 ("media: i2c: Add ov9282 camera sensor driver")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov9282.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov9282.c
++++ b/drivers/media/i2c/ov9282.c
+@@ -40,7 +40,7 @@
+ /* Exposure control */
+ #define OV9282_REG_EXPOSURE 0x3500
+ #define OV9282_EXPOSURE_MIN 1
+-#define OV9282_EXPOSURE_OFFSET 12
++#define OV9282_EXPOSURE_OFFSET 25
+ #define OV9282_EXPOSURE_STEP 1
+ #define OV9282_EXPOSURE_DEFAULT 0x0282
+
--- /dev/null
+From 448a2db3990534810b45d3e4202df96ab2dc5815 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 10 Dec 2024 15:28:28 +0000
+Subject: [PATCH] Revert "drm/vc4: hvs: Don't write gamma luts on 2711"
+
+This reverts commit 40c77e93cfdda320f47fc1a00a76ce466d20e976.
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -521,9 +521,6 @@ static void vc4_hvs_lut_load(struct vc4_
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+- if (hvs->vc4->gen == VC4_GEN_5)
+- return;
+-
+ /* The LUT memory is laid out with each HVS channel in order,
+ * each of which takes 256 writes for R, 256 for G, then 256
+ * for B.
--- /dev/null
+From 746662562995125ef7fb2c294300b0bd061b1251 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 10 Dec 2024 16:39:31 +0000
+Subject: [PATCH] Revert "PCI: Warn if no host bridge NUMA node info"
+
+This warning doesn't mean anyting on our platform and
+the warning causes confusion.
+
+See: https://forums.raspberrypi.com/viewtopic.php?p=2276125#p2276125
+
+This reverts commit ad5086108b9f0361929aa9a79cf959ab5681d249.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/pci/probe.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -968,9 +968,6 @@ static int pci_register_host_bridge(stru
+ else
+ pr_info("PCI host bridge to bus %s\n", name);
+
+- if (nr_node_ids > 1 && pcibus_to_node(bus) == NUMA_NO_NODE)
+- dev_warn(&bus->dev, "Unknown NUMA node; performance will be reduced\n");
+-
+ /* Coalesce contiguous windows */
+ resource_list_for_each_entry_safe(window, n, &resources) {
+ if (list_is_last(&window->node, &resources))
--- /dev/null
+From 7d294fbff4863e53a64685335b30aed9604cae49 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Tue, 19 Nov 2024 16:11:32 +0000
+Subject: [PATCH] drm: bridge: panel: Connector to allow interlaced modes
+
+When initialized from panel_bridge_attach(), connector should
+allow interlaced modes rather than invariably rejecting them,
+so that other components can validate them.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/bridge/panel.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/panel.c
++++ b/drivers/gpu/drm/bridge/panel.c
+@@ -82,6 +82,8 @@ static int panel_bridge_attach(struct dr
+ return ret;
+ }
+
++ connector->interlace_allowed = true;
++
+ drm_panel_bridge_set_orientation(connector, bridge);
+
+ drm_connector_attach_encoder(&panel_bridge->connector,
--- /dev/null
+From 2b0acbe8fd008e09a904b7a3c796a2dc79bf10ea Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Tue, 19 Nov 2024 16:17:40 +0000
+Subject: [PATCH] dts: overlays: vc4-kms-dpi-generic-overlay: Add "interlaced"
+ property
+
+Almost no DPI hardware supports it, but it's useful for RP1 video out.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 1 +
+ arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -5099,6 +5099,7 @@ Params: clock-frequency Display
+ vsync-invert Vertical sync active low
+ de-invert Data Enable active low
+ pixclk-invert Negative edge pixel clock
++ interlaced Use an interlaced mode (where supported)
+ width-mm Define the screen width in mm
+ height-mm Define the screen height in mm
+ rgb565 Change to RGB565 output on GPIOs 0-19
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dpi-generic-overlay.dts
+@@ -59,6 +59,7 @@
+ vsync-invert = <&timing>, "vsync-active:0=0";
+ de-invert = <&timing>, "de-active:0=0";
+ pixclk-invert = <&timing>, "pixelclk-active:0=0";
++ interlaced = <&timing>, "interlaced?";
+
+ width-mm = <&panel_generic>, "width-mm:0";
+ height-mm = <&panel_generic>, "height-mm:0";
From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Date: Tue, 10 Dec 2024 13:23:09 +0000
-Subject: [PATCH 1456/1482] drm: rp1: rp1-dpi: Add interlaced modes and PIO
- program to fix VSYNC
+Subject: [PATCH] drm: rp1: rp1-dpi: Add interlaced modes and PIO program to
+ fix VSYNC
Implement interlaced modes by wobbling the base pointer and VFP width
for every field. This results in correct pixels but incorrect VSYNC.
+++ /dev/null
-From f85f3509692f966ec32e4db499f7e64dc6b6b952 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 12 Dec 2024 10:09:13 +0000
-Subject: [PATCH 1457/1482] pwm: Improve PWM_PIO_RP1 dependencies
-
-PWM_PIO_RP1 should select RP1_PIO, as it is useless without it.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/pwm/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/pwm/Kconfig
-+++ b/drivers/pwm/Kconfig
-@@ -457,6 +457,7 @@ config PWM_PCA9685
- config PWM_PIO_RP1
- tristate "RP1 PIO PWM support"
- depends on FIRMWARE_RP1 || COMPILE_TEST
-+ select RP1_PIO
- help
- This is a PWM framework driver for Raspberry Pi 5, using the PIO
- hardware of RP1 to provide PWM functionality. Supports up to 4
+++ /dev/null
-From 73fb1e979a210094935f4af4c3d6e700fba30c5f Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 12 Dec 2024 10:28:54 +0000
-Subject: [PATCH 1458/1482] Revert "pwm: Improve PWM_PIO_RP1 dependencies"
-
-This reverts commit f85f3509692f966ec32e4db499f7e64dc6b6b952.
----
- drivers/pwm/Kconfig | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/pwm/Kconfig
-+++ b/drivers/pwm/Kconfig
-@@ -457,7 +457,6 @@ config PWM_PCA9685
- config PWM_PIO_RP1
- tristate "RP1 PIO PWM support"
- depends on FIRMWARE_RP1 || COMPILE_TEST
-- select RP1_PIO
- help
- This is a PWM framework driver for Raspberry Pi 5, using the PIO
- hardware of RP1 to provide PWM functionality. Supports up to 4
--- /dev/null
+From ac0cd73932aa1e371ffaf0b974855ed3cd22937f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 11 Dec 2024 13:47:30 +0000
+Subject: [PATCH] ASoC: allo-piano-dac-plus: Fix volume limit locking
+
+Calling snd_soc_limit_volume from within a kcontrol put handler seems
+to cause a deadlock as it attempts to claim a write lock that is already
+held. Call snd_soc_limit_volume from the main initialisation code
+instead, to avoid the recursive locking.
+
+See: https://github.com/raspberrypi/linux/issues/6527
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/allo-piano-dac-plus.c | 32 +++++++++++++----------------
+ 1 file changed, 14 insertions(+), 18 deletions(-)
+
+--- a/sound/soc/bcm/allo-piano-dac-plus.c
++++ b/sound/soc/bcm/allo-piano-dac-plus.c
+@@ -452,14 +452,6 @@ static int pcm512x_set_reg_sub(struct sn
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+- if (digital_gain_0db_limit) {
+- ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
+- 207);
+- if (ret < 0)
+- dev_warn(card->dev, "Failed to set volume limit: %d\n",
+- ret);
+- }
+-
+ // When in Dual Mono, Sub vol control should not set anything.
+ if (glb_ptr->dual_mode != 1) { //Not in Dual Mono mode
+
+@@ -562,14 +554,6 @@ static int pcm512x_set_reg_master(struct
+
+ rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]);
+
+- if (digital_gain_0db_limit) {
+- ret = snd_soc_limit_volume(card, "Master Playback Volume",
+- 207);
+- if (ret < 0)
+- dev_warn(card->dev, "Failed to set volume limit: %d\n",
+- ret);
+- }
+-
+ if (glb_ptr->dual_mode == 1) { //in Dual Mono Mode
+
+ ret = snd_soc_component_write(asoc_rtd_to_codec(rtd, 0)->component,
+@@ -750,6 +734,18 @@ static int snd_allo_piano_dac_init(struc
+ if (digital_gain_0db_limit) {
+ int ret;
+
++ ret = snd_soc_limit_volume(card, "Master Playback Volume",
++ 207);
++ if (ret < 0)
++ dev_warn(card->dev, "Failed to set master volume limit: %d\n",
++ ret);
++
++ ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
++ 207);
++ if (ret < 0)
++ dev_warn(card->dev, "Failed to set subwoofer volume limit: %d\n",
++ ret);
++
+ //Set volume limit on both dacs
+ for (i = 0; i < ARRAY_SIZE(codec_ctl_pfx); i++) {
+ char cname[256];
+@@ -757,8 +753,8 @@ static int snd_allo_piano_dac_init(struc
+ sprintf(cname, "%s %s", codec_ctl_pfx[i], codec_ctl_name[0]);
+ ret = snd_soc_limit_volume(card, cname, 207);
+ if (ret < 0)
+- dev_warn(card->dev, "Failed to set volume limit: %d\n",
+- ret);
++ dev_warn(card->dev, "Failed to set %s volume limit: %d\n",
++ cname, ret);
+ }
+ }
+
--- /dev/null
+From af4ab4fb77dfc697c8ae068b18f27de1ee5d609f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Dec 2024 16:30:43 +0000
+Subject: [PATCH] drm: vc4: txp: Do not allow 24bpp formats when transposing
+
+The hardware doesn't support transposing to 24bpp (RGB888/BGR888)
+formats. There's no way to advertise this through DRM, so block
+it from atomic_check instead.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -272,6 +272,13 @@ static int vc4_txp_connector_atomic_chec
+ return -EINVAL;
+ }
+
++ if (conn_state->rotation & DRM_MODE_TRANSPOSE &&
++ (fb->format->format == DRM_FORMAT_RGB888 ||
++ fb->format->format == DRM_FORMAT_BGR888)) {
++ DRM_DEBUG_KMS("24bpp formats not supported when transposing\n");
++ return -EINVAL;
++ }
++
+ for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) {
+ if (fb->format->format == drm_fmts[i])
+ break;
--- /dev/null
+From 0b216b3988e5b7035cd5ed8a9910eacbb3420ce0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 12 Dec 2024 11:59:52 +0000
+Subject: [PATCH] drm: Validate connector rotation has one bit set in the
+ rotation property
+
+Copy the same validation logic as from the plane rotation property.
+
+Fixes: 8fec3ff87049 ("drm: Add a rotation parameter to connectors.")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_atomic_uapi.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/gpu/drm/drm_atomic_uapi.c
++++ b/drivers/gpu/drm/drm_atomic_uapi.c
+@@ -812,6 +812,12 @@ static int drm_atomic_connector_set_prop
+ } else if (property == connector->privacy_screen_sw_state_property) {
+ state->privacy_screen_sw_state = val;
+ } else if (property == connector->rotation_property) {
++ if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK)) {
++ drm_dbg_atomic(connector->dev,
++ "[CONNECTOR:%d:%s] bad rotation bitmask: 0x%llx\n",
++ connector->base.id, connector->name, val);
++ return -EINVAL;
++ }
+ state->rotation = val;
+ } else if (connector->funcs->atomic_set_property) {
+ return connector->funcs->atomic_set_property(connector,
--- /dev/null
+From 61494a7aa2ea887fa1cd1399a8db1317c87f661b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 12 Dec 2024 13:05:41 +0000
+Subject: [PATCH] ASoC: allo-piano-dac-plus: Suppress -517 errors
+
+Use dev_err_probe to simplify the code and suppress EPROBE_DEFER errors.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/allo-piano-dac-plus.c | 37 ++++++++---------------------
+ 1 file changed, 10 insertions(+), 27 deletions(-)
+
+--- a/sound/soc/bcm/allo-piano-dac-plus.c
++++ b/sound/soc/bcm/allo-piano-dac-plus.c
+@@ -974,48 +974,31 @@ static int snd_allo_piano_dac_probe(stru
+
+ allo_piano_2_1_codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+- if (!allo_piano_2_1_codecs[0].of_node) {
+- dev_err(&pdev->dev,
+- "Property 'audio-codec' missing or invalid\n");
+- return -EINVAL;
+- }
+-
+ allo_piano_2_1_codecs[1].of_node =
+ of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
+- if (!allo_piano_2_1_codecs[1].of_node) {
+- dev_err(&pdev->dev,
++ if (!allo_piano_2_1_codecs[0].of_node || !allo_piano_2_1_codecs[1].of_node)
++ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Property 'audio-codec' missing or invalid\n");
+- return -EINVAL;
+- }
+
+ mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1",
+ GPIOD_OUT_LOW);
+- if (IS_ERR(mute_gpio[0])) {
+- ret = PTR_ERR(mute_gpio[0]);
+- dev_err(&pdev->dev,
+- "failed to get mute1 gpio6: %d\n", ret);
+- return ret;
+- }
++ if (IS_ERR(mute_gpio[0]))
++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[0]),
++ "failed to get mute1 gpio\n");
+
+ mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2",
+ GPIOD_OUT_LOW);
+- if (IS_ERR(mute_gpio[1])) {
+- ret = PTR_ERR(mute_gpio[1]);
+- dev_err(&pdev->dev,
+- "failed to get mute2 gpio25: %d\n", ret);
+- return ret;
+- }
++ if (IS_ERR(mute_gpio[1]))
++ return dev_err_probe(&pdev->dev, PTR_ERR(mute_gpio[1]),
++ "failed to get mute2 gpio\n");
+
+ if (mute_gpio[0] && mute_gpio[1])
+ snd_allo_piano_dac.set_bias_level =
+ snd_allo_piano_set_bias_level;
+
+ ret = snd_soc_register_card(&snd_allo_piano_dac);
+- if (ret < 0) {
+- dev_err(&pdev->dev,
+- "snd_soc_register_card() failed: %d\n", ret);
+- return ret;
+- }
++ if (ret < 0)
++ return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
+
+ if ((mute_gpio[0]) && (mute_gpio[1]))
+ snd_allo_piano_gpio_mute(&snd_allo_piano_dac);
From 80533a952218696c0ef1b346bab50dc401e6b74c Mon Sep 17 00:00:00 2001
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Date: Thu, 12 Dec 2024 11:58:12 +0000
-Subject: [PATCH 1463/1482] drm: rp1: rp1-dpi: Fix optional dependency on
- RP1_PIO
+Subject: [PATCH] drm: rp1: rp1-dpi: Fix optional dependency on RP1_PIO
Add optional dependency to Kconfig, and conditionally compile
PIO-dependent code. Add a mode validation function to reject
--- /dev/null
+From 694247173f2e136196d7cb3a392c84cda65674d2 Mon Sep 17 00:00:00 2001
+From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Date: Mon, 7 Oct 2024 12:27:15 -0400
+Subject: [PATCH] serial: sc16is7xx: announce support for SER_RS485_RTS_ON_SEND
+
+commit 068d35a7be65fa3bca4bba21c269bfe0b39158a6 upstream.
+
+When specifying flag SER_RS485_RTS_ON_SEND in RS485 configuration,
+we get the following warning after commit 4afeced55baa ("serial: core:
+fix sanitizing check for RTS settings"):
+
+ invalid RTS setting, using RTS_AFTER_SEND instead
+
+This results in SER_RS485_RTS_AFTER_SEND being set and the
+driver always write to the register field SC16IS7XX_EFCR_RTS_INVERT_BIT,
+which breaks some hardware using these chips.
+
+The hardware supports both RTS_ON_SEND and RTS_AFTER_SEND modes, so fix
+this by announcing support for RTS_ON_SEND.
+
+Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Suggested-by: Konstantin Pugin <ria.freelander@gmail.com>
+Link: https://lore.kernel.org/lkml/20240422133219.2710061-2-ria.freelander@gmail.com
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Tested-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
+Link: https://lore.kernel.org/r/20241007162716.3122912-1-hugo@hugovil.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/serial/sc16is7xx.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -1457,7 +1457,7 @@ static int sc16is7xx_setup_mctrl_ports(s
+ }
+
+ static const struct serial_rs485 sc16is7xx_rs485_supported = {
+- .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND,
++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
+ .delay_rts_before_send = 1,
+ .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */
+ };
--- /dev/null
+From b75fd2a9385e1358fa82218184e73513f9a5e57f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 16 Dec 2024 15:11:08 +0000
+Subject: [PATCH] dtoverlays: Add override for target-path on I2C overlays
+
+To allow for attaching any of the standard overlays to a
+bitbashed i2c-gpio bus, allow specifying the target path for
+the overlay.
+
+Suggested by:
+https://forums.raspberrypi.com/viewtopic.php?t=381059
+
+Example:
+dtoverlay=i2c-gpio,i2c_gpio_sda=10,i2c_gpio_scl=11
+dtoverlay=mcp23017,i2c-path=/i2c@0
+dtoverlay=i2c-gpio,i2c_gpio_sda=12,i2c_gpio_scl=13,bus=3
+dtoverlay=mcp23017,i2c-path=/i2c@3
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 59 +++++++++++++++++++
+ .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 +
+ .../boot/dts/overlays/edt-ft5406-overlay.dts | 3 +
+ arch/arm/boot/dts/overlays/goodix-overlay.dts | 4 +-
+ .../dts/overlays/hd44780-i2c-lcd-overlay.dts | 4 +-
+ .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 +
+ .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 +
+ .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 +
+ .../boot/dts/overlays/ilitek251x-overlay.dts | 4 +-
+ .../boot/dts/overlays/mcp23017-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/pca953x-overlay.dts | 30 +++++++++-
+ .../arm/boot/dts/overlays/pcf857x-overlay.dts | 30 +++++++++-
+ .../dts/overlays/sc16is750-i2c-overlay.dts | 30 +++++++++-
+ .../dts/overlays/sc16is752-i2c-overlay.dts | 30 +++++++++-
+ 16 files changed, 201 insertions(+), 7 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -555,6 +555,7 @@ Params: addr I2C bus
+ overlay - BCM2711 only)
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+ Channel parameters can be set for each enabled channel.
+ A maximum of 4 channels can be enabled (letters a thru d).
+@@ -1238,6 +1239,7 @@ Params: sizex Touchscr
+ addr Sets the address for the touch controller. Note
+ that the device must be configured to use the
+ specified address.
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: enc28j60
+@@ -1439,6 +1441,7 @@ Info: Enables I2C connected Goodix gt9
+ Load: dtoverlay=goodix,<param>=<val>
+ Params: interrupt GPIO used for interrupt (default 4)
+ reset GPIO used for reset (default 17)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: googlevoicehat-soundcard
+@@ -1730,6 +1733,7 @@ Params: addr I2C addr
+ display_height Height of the display in characters (default 2)
+
+ display_width Width of the display in characters (default 16)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: hd44780-lcd
+@@ -2095,6 +2099,8 @@ Params: addr Sets the
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
+
++ i2c-path Override I2C path to allow for i2c-gpio buses
++
+ minpwm PWM setting for the fan when the SoC is below
+ mintemp (range 0-255. default 0)
+ maxpwm PWM setting for the fan when the SoC is above
+@@ -2165,6 +2171,8 @@ Params: pca9542 Select t
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
+
++ i2c-path Override I2C path to allow for i2c-gpio buses
++
+ disconnect_on_idle Force the mux to disconnect all child buses
+ after every transaction.
+
+@@ -2186,6 +2194,7 @@ Params: addr I2C addr
+ overlay - BCM2711 only)
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: i2c-rtc
+@@ -2255,6 +2264,8 @@ Params: abx80x Select o
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
+
++ i2c-path Override I2C path to allow for i2c-gpio buses
++
+ addr Sets the address for the RTC. Note that the
+ device must be configured to use the specified
+ address.
+@@ -2519,6 +2530,8 @@ Params: addr Set the
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
+
++ i2c-path Override I2C path to allow for i2c-gpio buses
++
+
+ Name: i2c0
+ Info: Change i2c0 pin usage. Not all pin combinations are usable on all
+@@ -2661,6 +2674,7 @@ Params: interrupt GPIO use
+ touchscreen (in pixels)
+ sizey Touchscreen size y, vertical resolution of
+ touchscreen (in pixels)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: imx219
+@@ -3138,6 +3152,7 @@ Params: gpiopin Gpio pin
+ overlay - BCM2711 only)
+ i2c6 Choose the I2C6 bus (configure with the i2c6
+ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: mcp23s17
+@@ -3587,6 +3602,17 @@ Params: addr I2C addr
+ cat9554 Select the Onnn CAT9554 (8 bit)
+ pca9654 Select the Onnn PCA9654 (8 bit)
+ xra1202 Select the Exar XRA1202 (8 bit)
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c3 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c4 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c5 Choose the I2C5 bus (configure with the i2c4
++ overlay - BCM2711 only)
++ i2c6 Choose the I2C6 bus (configure with the i2c6
++ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: pcf857x
+@@ -3598,6 +3624,17 @@ Params: addr I2C addr
+ pcf8574a Select the NXP PCF8574A (8 bit)
+ pcf8575 Select the NXP PCF8575 (16 bit)
+ pca8574 Select the NXP PCA8574 (8 bit)
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c3 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c4 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c5 Choose the I2C5 bus (configure with the i2c4
++ overlay - BCM2711 only)
++ i2c6 Choose the I2C6 bus (configure with the i2c6
++ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: pcie-32bit-dma
+@@ -4257,6 +4294,17 @@ Load: dtoverlay=sc16is750-i2c,<param>=
+ Params: int_pin GPIO used for IRQ (default 24)
+ addr Address (default 0x48)
+ xtal On-board crystal frequency (default 14745600)
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c3 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c4 Choose the I2C4 bus (configure with the i2c4
++ overlay - BCM2711 only)
++ i2c5 Choose the I2C5 bus (configure with the i2c5
++ overlay - BCM2711 only)
++ i2c6 Choose the I2C6 bus (configure with the i2c6
++ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: sc16is750-spi0
+@@ -4275,6 +4323,17 @@ Load: dtoverlay=sc16is752-i2c,<param>=
+ Params: int_pin GPIO used for IRQ (default 24)
+ addr Address (default 0x48)
+ xtal On-board crystal frequency (default 14745600)
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c3 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c4 Choose the I2C4 bus (configure with the i2c4
++ overlay - BCM2711 only)
++ i2c5 Choose the I2C5 bus (configure with the i2c5
++ overlay - BCM2711 only)
++ i2c6 Choose the I2C6 bus (configure with the i2c6
++ overlay - BCM2711 only)
++ i2c-path Override I2C path to allow for i2c-gpio buses
+
+
+ Name: sc16is752-spi0
+--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts
+@@ -131,5 +131,7 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
+@@ -41,6 +41,9 @@
+ i2c6 = <&ts_i2c_frag>, "target?=0",
+ <&ts_i2c_frag>, "target-path=i2c6",
+ <0>,"-0-1";
++ i2c-path = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path",
++ <0>,"-0-1";
+ addr = <&ft5406>,"reg:0";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts
++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts
+@@ -16,7 +16,7 @@
+ };
+ };
+
+- fragment@1 {
++ i2c_frag: fragment@1 {
+ target = <&i2c1>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -42,5 +42,7 @@
+ <>9271>,"irq-gpios:4";
+ reset = <&goodix_pins>,"brcm,pins:4",
+ <>9271>,"reset-gpios:4";
++ i2c-path = <&i2c_frag>, "target?=0",
++ <&i2c_frag>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hd44780-i2c-lcd-overlay.dts
+@@ -4,7 +4,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ i2c_frag: fragment@0 {
+ target = <&i2c_arm>;
+ __overlay__ {
+ status = "okay";
+@@ -52,6 +52,8 @@
+ display_height = <&lcd_screen>,"display-height-chars:0";
+ display_width = <&lcd_screen>,"display-width-chars:0";
+ addr = <&pcf857x>,"reg:0";
++ i2c-path = <&i2c_frag>, "target?=0",
++ <&i2c_frag>, "target-path";
+ };
+
+ };
+--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts
+@@ -93,6 +93,8 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ addr = <&emc2301>,"reg:0";
+ minpwm = <&emc2301>,"emc2305,pwm-min.0";
+ maxpwm = <&emc2301>,"emc2305,pwm-max.0";
+--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts
+@@ -175,6 +175,8 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ disconnect_on_idle =
+ <&pca9542>,"idle-state:0=", <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
+@@ -57,5 +57,7 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts
+@@ -38,5 +38,7 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
+@@ -38,5 +38,7 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ilitek251x-overlay.dts
+@@ -16,7 +16,7 @@
+ };
+ };
+
+- fragment@1 {
++ frag1: fragment@1 {
+ target = <&i2c1>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -41,5 +41,7 @@
+ <&ili251x>,"interrupts:0";
+ sizex = <&ili251x>,"touchscreen-size-x:0";
+ sizey = <&ili251x>,"touchscreen-size-y:0";
++ i2c-path = <&frag1>, "target?=0",
++ <&frag1>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -98,6 +98,8 @@
+ <&frag100>, "target-path=i2c5";
+ i2c6 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c6";
++ i2c-path = <&frag100>, "target?=0",
++ <&frag100>, "target-path";
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts
+@@ -5,7 +5,7 @@
+ /{
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ frag0: fragment@0 {
+ target = <&i2c_arm>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -204,6 +204,20 @@
+ };
+ };
+
++ fragment@100 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ addr = <&pca>,"reg:0";
+ pca6416 = <0>, "+1";
+@@ -236,5 +250,19 @@
+ cat9554 = <0>, "+28";
+ pca9654 = <0>, "+29";
+ xra1202 = <0>, "+30";
++ i2c0 = <&frag0>, "target:0=",<&i2c0>,
++ <0>,"+100+101";
++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+100+101";
++ i2c3 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c3";
++ i2c4 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c4";
++ i2c5 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c5";
++ i2c6 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c6";
++ i2c-path = <&frag0>, "target?=0",
++ <&frag0>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts
+@@ -6,7 +6,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ frag0: fragment@0 {
+ target = <&i2c_arm>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -22,11 +22,39 @@
+ };
+ };
+
++ fragment@100 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ pcf8574 = <&pcf857x>,"compatible=nxp,pcf8574", <&pcf857x>,"reg:0=0x20";
+ pcf8574a = <&pcf857x>,"compatible=nxp,pcf8574a", <&pcf857x>,"reg:0=0x38";
+ pcf8575 = <&pcf857x>,"compatible=nxp,pcf8575", <&pcf857x>,"reg:0=0x20";
+ pca8574 = <&pcf857x>,"compatible=nxp,pca8574", <&pcf857x>,"reg:0=0x20";
+ addr = <&pcf857x>,"reg:0";
++ i2c0 = <&frag0>, "target:0=",<&i2c0>,
++ <0>,"+100+101";
++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+100+101";
++ i2c3 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c3";
++ i2c4 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c4";
++ i2c5 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c5";
++ i2c6 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c6";
++ i2c-path = <&frag0>, "target?=0",
++ <&frag0>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
+@@ -4,7 +4,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ frag0: fragment@0 {
+ target = <&i2c_arm>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -48,10 +48,38 @@
+ };
+ };
+
++ fragment@100 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ int_pin = <&sc16is750>,"interrupts:0", <&int_pins>,"brcm,pins:0",
+ <&int_pins>,"reg:0";
+ addr = <&sc16is750>,"reg:0", <&sc16is750_clk>,"name";
+ xtal = <&sc16is750_clk>,"clock-frequency:0";
++ i2c0 = <&frag0>, "target:0=",<&i2c0>,
++ <0>,"+100+101";
++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+100+101";
++ i2c3 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c3";
++ i2c4 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c4";
++ i2c5 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c5";
++ i2c6 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c6";
++ i2c-path = <&frag0>, "target?=0",
++ <&frag0>, "target-path";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts
+@@ -4,7 +4,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ frag0: fragment@0 {
+ target = <&i2c_arm>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -48,10 +48,38 @@
+ };
+ };
+
++ fragment@100 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ int_pin = <&sc16is752>,"interrupts:0", <&int_pins>,"brcm,pins:0",
+ <&int_pins>,"reg:0";
+ addr = <&sc16is752>,"reg:0",<&sc16is752_clk>,"name";
+ xtal = <&sc16is752_clk>,"clock-frequency:0";
++ i2c0 = <&frag0>, "target:0=",<&i2c0>,
++ <0>,"+100+101";
++ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+100+101";
++ i2c3 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c3";
++ i2c4 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c4";
++ i2c5 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c5";
++ i2c6 = <&frag0>, "target?=0",
++ <&frag0>, "target-path=i2c6";
++ i2c-path = <&frag0>, "target?=0",
++ <&frag0>, "target-path";
+ };
+ };
From 4b0ca96738bb937529655a0062d60775f47b0f5e Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 16 Dec 2024 23:01:41 +0000
-Subject: [PATCH 1468/1482] misc: rp1-pio: Support larger data transfers
+Subject: [PATCH] misc: rp1-pio: Support larger data transfers
Add a separate IOCTL for larger transfer with a 32-bit data_bytes
field.
--- /dev/null
+From a4a4d7f9183bae11d81616346038e9efaba2fce1 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 16 Dec 2024 19:15:52 +0000
+Subject: [PATCH] dtoverlays: Use continuous clock mode for ov9281
+
+This increases the maximum frame rate from 247 to 260fps in
+10-bit mode.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ov9281-overlay.dts | 1 -
+ arch/arm/boot/dts/overlays/ov9281.dtsi | 1 -
+ 2 files changed, 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts
+@@ -29,7 +29,6 @@
+ csi_ep: endpoint {
+ remote-endpoint = <&cam_endpoint>;
+ data-lanes = <1 2>;
+- clock-noncontinuous;
+ };
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/ov9281.dtsi
++++ b/arch/arm/boot/dts/overlays/ov9281.dtsi
+@@ -19,7 +19,6 @@ cam_node: ov9281@60 {
+ cam_endpoint: endpoint {
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+- clock-noncontinuous;
+ link-frequencies =
+ /bits/ 64 <400000000>;
+ };
--- /dev/null
+From 62085522016ee2dadbe8668a6a97919770020817 Mon Sep 17 00:00:00 2001
+From: Renjaya Raga Zenta <ragazenta@gmail.com>
+Date: Wed, 18 Dec 2024 16:44:32 +0700
+Subject: [PATCH] overlays: goodix: Allow override i2c address
+
+Some Goodix devices e.g. gt911 use address 0x5d instead of 0x14.
+So, make the address overridable.
+
+Signed-off-by: Renjaya Raga Zenta <ragazenta@gmail.com>
+---
+ arch/arm/boot/dts/overlays/README | 3 ++-
+ arch/arm/boot/dts/overlays/goodix-overlay.dts | 1 +
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1439,7 +1439,8 @@ Name: goodix
+ Info: Enables I2C connected Goodix gt9271 multiple touch controller using
+ GPIOs 4 and 17 (pins 7 and 11 on GPIO header) for interrupt and reset.
+ Load: dtoverlay=goodix,<param>=<val>
+-Params: interrupt GPIO used for interrupt (default 4)
++Params: addr I2C address (default 0x14)
++ interrupt GPIO used for interrupt (default 4)
+ reset GPIO used for reset (default 17)
+ i2c-path Override I2C path to allow for i2c-gpio buses
+
+--- a/arch/arm/boot/dts/overlays/goodix-overlay.dts
++++ b/arch/arm/boot/dts/overlays/goodix-overlay.dts
+@@ -37,6 +37,7 @@
+ };
+
+ __overrides__ {
++ addr = <>9271>,"reg:0";
+ interrupt = <&goodix_pins>,"brcm,pins:0",
+ <>9271>,"interrupts:0",
+ <>9271>,"irq-gpios:4";
From cd26850713088942ca4f9a248a8bed1f0504a58f Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 19 Dec 2024 15:11:40 +0000
-Subject: [PATCH 1471/1482] fixup! misc: Add RP1 PIO driver
+Subject: [PATCH] fixup! misc: Add RP1 PIO driver
Change the Kconfig dependencies so that RP1_PIO depends on FIRMWARE_RP1,
rather than selecting it.
From 468b525d45a726e4ba704b33c4eba53de47ac684 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 5 Dec 2024 16:03:39 +0000
-Subject: [PATCH 1473/1482] misc: rp1-pio: More logical probe sequence
+Subject: [PATCH] misc: rp1-pio: More logical probe sequence
Sort the probe function initialisation into a more logical order.
From 5c07ba20630a629399eaa6583457aca93ff74606 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 9 Dec 2024 09:58:29 +0000
-Subject: [PATCH 1474/1482] misc: rp1-pio: Convert floats to 24.8 fixed point
+Subject: [PATCH] misc: rp1-pio: Convert floats to 24.8 fixed point
Floating point arithmetic is not supported in the kernel, so use fixed
point instead.
From 75203c6641cfe47dfb817b095430021b0981ff47 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Tue, 10 Dec 2024 12:06:14 +0000
-Subject: [PATCH 1475/1482] misc: rp1-pio: Minor cosmetic tweaks
+Subject: [PATCH] misc: rp1-pio: Minor cosmetic tweaks
No functional change.
From fddd3e9318dbf01fb763b6880021abc558fce8e6 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 12 Dec 2024 17:09:27 +0000
-Subject: [PATCH 1476/1482] misc: rp1-pio: Add in-kernel DMA support
+Subject: [PATCH] misc: rp1-pio: Add in-kernel DMA support
Add kernel-facing implementations of pio_sm_config_xfer and
pio_xm_xfer_data.
From d6d83ad3d9a3a594909a1ad1c82b735ab711cd12 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Tue, 3 Dec 2024 16:09:30 +0000
-Subject: [PATCH 1477/1482] misc: Add ws2812-pio-rp1 driver
+Subject: [PATCH] misc: Add ws2812-pio-rp1 driver
ws2812-pio-rp1 is a PIO-based driver for WS2812 LEDS. It creates a
character device in /dev, the default name of which is /dev/leds<n>,
From 4a8f2b39157825fefc505fe4b94f3a9ce101e170 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 12 Dec 2024 23:23:39 +0000
-Subject: [PATCH 1478/1482] overlays: Add ws2812-pio overlay
+Subject: [PATCH] overlays: Add ws2812-pio overlay
Add an overlay to enable a WS2812 LED driver on a given GPIO.
--- a/arch/arm/boot/dts/overlays/Makefile
+++ b/arch/arm/boot/dts/overlays/Makefile
-@@ -337,7 +337,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+@@ -342,7 +342,8 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
waveshare-can-fd-hat-mode-a.dtbo \
waveshare-can-fd-hat-mode-b.dtbo \
wittypi.dtbo \
targets += $(dtbo-y)
--- a/arch/arm/boot/dts/overlays/README
+++ b/arch/arm/boot/dts/overlays/README
-@@ -5487,6 +5487,28 @@ Params: alsaname Changes
+@@ -5599,6 +5599,28 @@ Params: alsaname Changes
compatible Changes the codec compatibility
--- /dev/null
+From 489570796a5789f849683fc3fb034c55cb13e4c6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Dec 2024 17:13:17 +0000
+Subject: [PATCH] overlays: Add and document i2c_csi_dsi0 parameters
+
+Add "i2c_csi_dsi0" parameters to overlays that already have an
+"i2c_csi_dsi" parameter.
+
+The I2C bus and GPIO mapping of i2c_csi_dsi and i2c_csi_dsi0 varies
+between platforms. Document the associations against the dtparams
+"i2c_csi_dsi" and "i2c_csi_dsi0" - run "dtparam -h i2c_csi_dsi"
+and "dtparam -h i2c_csi_dsi0" to read it.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 103 ++++++++++++++++--
+ .../arm/boot/dts/overlays/ads1115-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/i2c-fan-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/i2c-mux-overlay.dts | 2 +
+ .../dts/overlays/i2c-pwm-pca9685a-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/i2c-rtc-overlay.dts | 2 +
+ .../boot/dts/overlays/i2c-sensor-overlay.dts | 2 +
+ .../boot/dts/overlays/mcp23017-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/pca953x-overlay.dts | 2 +
+ .../arm/boot/dts/overlays/pcf857x-overlay.dts | 2 +
+ .../dts/overlays/sc16is750-i2c-overlay.dts | 2 +
+ .../dts/overlays/sc16is752-i2c-overlay.dts | 2 +
+ 12 files changed, 113 insertions(+), 12 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -301,10 +301,31 @@ Params:
+ i2c_baudrate An alias for i2c_arm_baudrate
+
+ i2c_csi_dsi Set to "on" to enable the i2c_csi_dsi interface
++ The I2C bus and GPIOs are platform specific:
++ B rev 1:
++ i2c-1 on 2 & 3
++ B rev 2, B+, CM, Zero, Zero W, 2B, CM2, CM3,
++ CM4S:
++ i2c-0 on 28 & 29
++ 3B, 3B+, Zero 2W, 4B, 400, CM4:
++ i2c-0 on 44 & 45
++ 5, 500:
++ i2c-11/i2c-4 on 40 & 41
++ CM5 on CM5IO:
++ i2c-0 on 0 & 1
++ CM5 on CM4IO:
++ i2c-10/i2c-6 on 38 & 39
+
+ i2c_csi_dsi0 Set to "on" to enable the i2c_csi_dsi0 interface
++ The I2C bus and GPIOs are platform specific:
++ B rev 1 & 2, B+, CM, Zero, Zero W, 2B, CM2,
++ CM3, CM4S, 3B, 3B+, Zero 2W, 4B, 400, CM4,
++ CM5 on CM4IO:
++ i2c-0 on 0 & 1
++ 5, 500, CM5 on CM5IO:
++ i2c-10/i2c-6 on 38 & 39
+
+- i2c_csi_dsi1 Set to "on" to enable the i2c_csi_dsi1 interface
++ i2c_csi_dsi1 A Pi 5 family-specific alias for i2c_csi_dsi.
+
+ i2c_vc Set to "on" to enable the i2c interface
+ usually reserved for the VideoCore processor
+@@ -546,7 +567,12 @@ Params: addr I2C bus
+ Amplifier for this channel. (Default 1 sets the
+ full scale of the channel to 4.096 Volts)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C4 bus (configure with the i2c4
+@@ -2086,7 +2112,13 @@ Params: addr Sets the
+
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+@@ -2158,7 +2190,13 @@ Params: pca9542 Select t
+
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+@@ -2186,7 +2224,12 @@ Info: Adds support for an NXP PCA9685A
+ Load: dtoverlay=i2c-pwm-pca9685a,<param>=<val>
+ Params: addr I2C address of PCA9685A (default 0x40)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C3 bus (configure with the i2c3
+@@ -2251,7 +2294,13 @@ Params: abx80x Select o
+
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+@@ -2517,7 +2566,12 @@ Params: addr Set the
+
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+@@ -3144,7 +3198,12 @@ Params: gpiopin Gpio pin
+ mcp23008 Configure an MCP23008 instead.
+ noints Disable the interrupt GPIO line.
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C4 bus (configure with the i2c4
+@@ -3604,7 +3663,12 @@ Params: addr I2C addr
+ pca9654 Select the Onnn PCA9654 (8 bit)
+ xra1202 Select the Exar XRA1202 (8 bit)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C3 bus (configure with the i2c3
+@@ -3626,7 +3690,12 @@ Params: addr I2C addr
+ pcf8575 Select the NXP PCF8575 (16 bit)
+ pca8574 Select the NXP PCA8574 (8 bit)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C3 bus (configure with the i2c3
+@@ -4296,7 +4365,12 @@ Params: int_pin GPIO use
+ addr Address (default 0x48)
+ xtal On-board crystal frequency (default 14745600)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C4 bus (configure with the i2c4
+@@ -4325,7 +4399,12 @@ Params: int_pin GPIO use
+ addr Address (default 0x48)
+ xtal On-board crystal frequency (default 14745600)
+ i2c0 Choose the I2C0 bus on GPIOs 0&1
+- i2c_csi_dsi Choose the I2C0 bus on GPIOs 44&45
++ i2c_csi_dsi Choose the I2C bus connected to the main
++ camera/display connector.
++ See "dtparam -h i2c_csi_dsi" for details.
++ i2c_csi_dsi0 Choose the I2C bus connected to the second
++ camera/display connector, if present.
++ See "dtparam -h i2c_csi_dsi0" for details.
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+ i2c4 Choose the I2C4 bus (configure with the i2c4
+--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts
+@@ -123,6 +123,8 @@
+ i2c0 = <&frag100>, "target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-fan-overlay.dts
+@@ -85,6 +85,8 @@
+ i2c0 = <&frag100>,"target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>,"target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-mux-overlay.dts
+@@ -167,6 +167,8 @@
+ <0>,"+101+102";
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-pwm-pca9685a-overlay.dts
+@@ -49,6 +49,8 @@
+ <0>,"+101+102";
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts
+@@ -30,6 +30,8 @@
+ i2c0 = <&frag100>, "target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
+@@ -30,6 +30,8 @@
+ i2c0 = <&frag100>, "target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -90,6 +90,8 @@
+ i2c0 = <&frag100>, "target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
++ i2c_csi_dsi0 = <&frag100>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+101+102";
+ i2c3 = <&frag100>, "target?=0",
+ <&frag100>, "target-path=i2c3";
+ i2c4 = <&frag100>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/pca953x-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pca953x-overlay.dts
+@@ -254,6 +254,8 @@
+ <0>,"+100+101";
+ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+100+101";
++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+100+101";
+ i2c3 = <&frag0>, "target?=0",
+ <&frag0>, "target-path=i2c3";
+ i2c4 = <&frag0>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/pcf857x-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pcf857x-overlay.dts
+@@ -46,6 +46,8 @@
+ <0>,"+100+101";
+ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+100+101";
++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+100+101";
+ i2c3 = <&frag0>, "target?=0",
+ <&frag0>, "target-path=i2c3";
+ i2c4 = <&frag0>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
+@@ -71,6 +71,8 @@
+ <0>,"+100+101";
+ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+100+101";
++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+100+101";
+ i2c3 = <&frag0>, "target?=0",
+ <&frag0>, "target-path=i2c3";
+ i2c4 = <&frag0>, "target?=0",
+--- a/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is752-i2c-overlay.dts
+@@ -71,6 +71,8 @@
+ <0>,"+100+101";
+ i2c_csi_dsi = <&frag0>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+100+101";
++ i2c_csi_dsi0 = <&frag0>, "target:0=",<&i2c_csi_dsi0>,
++ <0>,"+100+101";
+ i2c3 = <&frag0>, "target?=0",
+ <&frag0>, "target-path=i2c3";
+ i2c4 = <&frag0>, "target?=0",
--- /dev/null
+From 147ddfdaf626fe5484596235bba8bdc6dcfde501 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 20 Dec 2024 15:08:52 +0000
+Subject: [PATCH] dts: Add noanthogs parameter to CM4 and CM5
+
+By default, the antenna selection on CM4 and CM5 is fixed at boot time,
+with the dtparams ant1, ant2 and noant selecting which should be
+enabled. Add a new dtparam - noanthogs - which leaves the GPIOs free
+to be controlled at runtime by the OS.
+
+N.B. Using this parameter without suitable OS support will leave both
+antennae disabled, resulting in attenuated WiFi and Bluetooth signals.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 2 ++
+ arch/arm/boot/dts/overlays/README | 6 ++++++
+ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 2 ++
+ 3 files changed, 10 insertions(+)
+
+--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts
+@@ -493,6 +493,8 @@ i2c_csi_dsi0: &i2c0 {
+ <&ant1>, "output-low?=on",
+ <&ant2>, "output-high?=off",
+ <&ant2>, "output-low?=on";
++ noanthogs = <&ant1>,"status=disabled",
++ <&ant2>, "status=disabled";
+
+ pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0";
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -153,6 +153,12 @@ Params:
+
+ noant Disable both antennas. CM4/5 only.
+
++ noanthogs Disable the GPIO hogs on the antenna controls
++ so they can be controlled at runtime. Note that
++ using this parameter without suitable OS
++ support will result in attenuated WiFi and
++ Bluetooth signals. CM4/5 only.
++
+ audio Set to "on" to enable the onboard ALSA audio
+ interface (default "off")
+
+--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
+@@ -750,5 +750,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ <&ant1>, "output-low?=on",
+ <&ant2>, "output-high?=off",
+ <&ant2>, "output-low?=on";
++ noanthogs = <&ant1>,"status=disabled",
++ <&ant2>, "status=disabled";
+ };
+ };